Import Ruty
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Finally remove all db records marked as deleted some time ago |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require INSTALL_PATH.'program/include/clisetup.php';
|
||||
|
||||
if (!empty($_SERVER['argv'][1])) {
|
||||
$days = intval($_SERVER['argv'][1]);
|
||||
}
|
||||
else {
|
||||
$days = 7;
|
||||
}
|
||||
|
||||
rcmail_utils::db_clean($days);
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
PWD=`dirname "$0"`
|
||||
|
||||
do_shrink() {
|
||||
rm -f "$2"
|
||||
csso $1 -o $2 --no-restructure
|
||||
}
|
||||
|
||||
if which csso > /dev/null 2>&1; then
|
||||
:
|
||||
else
|
||||
echo "csso not found. Please install e.g. 'npm install -g csso-cli'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compress single file from argument
|
||||
if [ $# -gt 0 ]; then
|
||||
CSS_FILE="$1"
|
||||
|
||||
echo "Shrinking $CSS_FILE"
|
||||
minfile=`echo $CSS_FILE | sed -e 's/\.css$/\.min\.css/'`
|
||||
do_shrink "$CSS_FILE" "$minfile"
|
||||
exit
|
||||
fi
|
||||
|
||||
DIRS="$PWD/../skins/* $PWD/../plugins/* $PWD/../plugins/*/skins/* $PWD/../plugins/*/themes/*"
|
||||
# default: compress application scripts
|
||||
for dir in $DIRS; do
|
||||
for file in $dir/*.css; do
|
||||
if echo "$file" | grep -q -e '.min.css$'; then
|
||||
continue
|
||||
fi
|
||||
if [ ! -f "$file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Shrinking $file"
|
||||
minfile=`echo $file | sed -e 's/\.css$/\.min\.css/'`
|
||||
do_shrink "$file" "$minfile"
|
||||
done
|
||||
done
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Decrypt the encrypted parts of the HTTP Received: headers |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Tomas Tevesz <ice@extreme.hu> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
/**
|
||||
* If http_received_header_encrypt is configured, the IP address and the
|
||||
* host name of the added Received: header is encrypted with 3DES, to
|
||||
* protect information that some could consider sensitive, yet their
|
||||
* availability is a must in some circumstances.
|
||||
*
|
||||
* Such an encrypted Received: header might look like:
|
||||
*
|
||||
* Received: from DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ==
|
||||
* [my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4]
|
||||
* with HTTP/1.1 (POST); Thu, 14 May 2009 19:17:28 +0200
|
||||
*
|
||||
* In this example, the two encrypted components are the sender host name
|
||||
* (DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ==) and the IP
|
||||
* address (my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4).
|
||||
*
|
||||
* Using this tool, they can be decrypted into plain text:
|
||||
*
|
||||
* $ bin/decrypt.sh 'my0nUbjZXKtl7KVBZcsvWOxxtyVFxza4' \
|
||||
* > 'DzgkvJBO5+bw+oje5JACeNIa/uSI4mRw2cy5YoPBba73eyBmjtyHnQ=='
|
||||
* 84.3.187.208
|
||||
* 5403BBD0.catv.pool.telekom.hu
|
||||
* $
|
||||
*
|
||||
* Thus it is known that this particular message was sent by 84.3.187.208,
|
||||
* having, at the time of sending, the name of 5403BBD0.catv.pool.telekom.hu.
|
||||
*
|
||||
* If (most likely binary) junk is shown, then
|
||||
* - either the encryption password has, between the time the mail was sent
|
||||
* and 'now', changed, or
|
||||
* - you are dealing with counterfeit header data.
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ .'/..') . '/');
|
||||
|
||||
require INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
if ($argc < 2) {
|
||||
die("Usage: " . basename($argv[0]) . " encrypted-hdr-part [encrypted-hdr-part ...]\n");
|
||||
}
|
||||
|
||||
$RCMAIL = rcube::get_instance();
|
||||
|
||||
for ($i = 1; $i < $argc; $i++) {
|
||||
printf("%s\n", $RCMAIL->decrypt($argv[$i]));
|
||||
};
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Utility script to remove all data related to a certain user |
|
||||
| from the local database. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <thomas@roundcube.net> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
function print_usage()
|
||||
{
|
||||
print "Usage: deluser.sh [--host=HOST][--age=DAYS][--dry-run] [username]\n";
|
||||
print "--host=HOST The IMAP hostname or IP the given user is related to\n";
|
||||
print "--age=DAYS Delete all users who have not logged in for more than X days\n";
|
||||
print "--dry-run List users but do not delete them (for use with --age)\n";
|
||||
}
|
||||
|
||||
function _die($msg, $usage=false)
|
||||
{
|
||||
fwrite(STDERR, $msg . "\n");
|
||||
if ($usage) print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
|
||||
// get arguments
|
||||
$args = rcube_utils::get_opt(['h' => 'host', 'a' => 'age', 'd' => 'dry-run:bool']);
|
||||
|
||||
if (!empty($args['age']) && ($age = intval($args['age']))) {
|
||||
$db = $rcmail->get_dbh();
|
||||
$db->db_connect('r');
|
||||
|
||||
$query = $db->query("SELECT `username`, `mail_host` FROM " . $db->table_name('users', true)
|
||||
. " WHERE `last_login` < " . $db->now($age * -1 * 86400)
|
||||
. ($args['host'] ? " AND `mail_host` = " . $db->quote($args['host']) : '')
|
||||
);
|
||||
|
||||
while ($user = $db->fetch_assoc($query)) {
|
||||
if (!empty($args['dry-run'])) {
|
||||
printf("%s (%s)\n", $user['username'], $user['mail_host']);
|
||||
continue;
|
||||
}
|
||||
system(sprintf("php %s/deluser.sh --host=%s %s", INSTALL_PATH . 'bin', escapeshellarg($user['mail_host']), escapeshellarg($user['username'])));
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$username = isset($args[0]) ? trim($args[0]) : null;
|
||||
if (empty($username)) {
|
||||
_die("Missing required parameters", true);
|
||||
}
|
||||
|
||||
if (empty($args['host'])) {
|
||||
$hosts = $rcmail->config->get('imap_host', '');
|
||||
if (is_string($hosts)) {
|
||||
$args['host'] = $hosts;
|
||||
}
|
||||
else if (is_array($hosts) && count($hosts) == 1) {
|
||||
$args['host'] = reset($hosts);
|
||||
}
|
||||
else {
|
||||
_die("Specify a host name", true);
|
||||
}
|
||||
|
||||
// host can be a URL like tls://192.168.12.44
|
||||
$host_url = parse_url($args['host']);
|
||||
if ($host_url['host']) {
|
||||
$args['host'] = $host_url['host'];
|
||||
}
|
||||
}
|
||||
|
||||
// connect to DB
|
||||
$db = $rcmail->get_dbh();
|
||||
$db->db_connect('w');
|
||||
$transaction = false;
|
||||
|
||||
if (!$db->is_connected() || $db->is_error()) {
|
||||
_die("No DB connection\n" . $db->is_error());
|
||||
}
|
||||
|
||||
// find user in local database
|
||||
$user = rcube_user::query($username, $args['host']);
|
||||
|
||||
if (!$user) {
|
||||
die("User not found.\n");
|
||||
}
|
||||
|
||||
// inform plugins about approaching user deletion
|
||||
$plugin = $rcmail->plugins->exec_hook('user_delete_prepare', ['user' => $user, 'username' => $username, 'host' => $args['host']]);
|
||||
|
||||
// let plugins cleanup their own user-related data
|
||||
if (!$plugin['abort']) {
|
||||
$transaction = $db->startTransaction();
|
||||
$plugin = $rcmail->plugins->exec_hook('user_delete', $plugin);
|
||||
}
|
||||
|
||||
if ($plugin['abort']) {
|
||||
unset($plugin['abort']);
|
||||
if ($transaction) {
|
||||
$db->rollbackTransaction();
|
||||
}
|
||||
_die("User deletion aborted by plugin");
|
||||
}
|
||||
|
||||
$db->query('DELETE FROM ' . $db->table_name('users', true) . ' WHERE `user_id` = ?', $user->ID);
|
||||
|
||||
if ($db->is_error()) {
|
||||
$rcmail->plugins->exec_hook('user_delete_rollback', $plugin);
|
||||
_die("DB error occurred: " . $db->is_error());
|
||||
}
|
||||
else {
|
||||
// inform plugins about executed user deletion
|
||||
$plugin = $rcmail->plugins->exec_hook('user_delete_commit', $plugin);
|
||||
|
||||
if ($plugin['abort']) {
|
||||
unset($plugin['abort']);
|
||||
$db->rollbackTransaction();
|
||||
$rcmail->plugins->exec_hook('user_delete_rollback', $plugin);
|
||||
}
|
||||
else {
|
||||
$db->endTransaction();
|
||||
echo "Successfully deleted user $user->ID\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Trigger garbage collecting routines manually (e.g. via cronjob) |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require INSTALL_PATH.'program/include/clisetup.php';
|
||||
|
||||
$rcmail = rcube::get_instance();
|
||||
|
||||
$session_driver = $rcmail->config->get('session_storage', 'db');
|
||||
$session_lifetime = $rcmail->config->get('session_lifetime', 0) * 60 * 2;
|
||||
|
||||
// Clean expired SQL sessions
|
||||
if ($session_driver == 'db' && $session_lifetime) {
|
||||
$db = $rcmail->get_dbh();
|
||||
$db->query("DELETE FROM " . $db->table_name('session')
|
||||
. " WHERE changed < " . $db->now(-$session_lifetime));
|
||||
}
|
||||
|
||||
// Clean caches and temp directory
|
||||
$rcmail->gc();
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Update the fulltext index for all contacts of the internal |
|
||||
| address book. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH.'program/include/clisetup.php';
|
||||
ini_set('memory_limit', -1);
|
||||
|
||||
rcmail_utils::indexcontacts();
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Create database schema |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
// get arguments
|
||||
$opts = rcube_utils::get_opt([
|
||||
'd' => 'dir',
|
||||
'u' => 'update'
|
||||
]);
|
||||
|
||||
if (empty($opts['dir'])) {
|
||||
rcube::raise_error("Database schema directory not specified (--dir).", false, true);
|
||||
}
|
||||
|
||||
// Check if directory exists
|
||||
if (!file_exists($opts['dir'])) {
|
||||
rcube::raise_error("Specified database schema directory doesn't exist.", false, true);
|
||||
}
|
||||
|
||||
$db = rcmail_utils::db();
|
||||
|
||||
if (!empty($opts['update']) && in_array($db->table_name('system'), (array)$db->list_tables())) {
|
||||
echo "Checking for database schema updates..." . PHP_EOL;
|
||||
rcmail_utils::db_update($opts['dir'], 'roundcube', null, ['errors' => true]);
|
||||
} else {
|
||||
rcmail_utils::db_init($opts['dir']);
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Update an existing Roundcube installation with files from |
|
||||
| this version |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
if (!function_exists('system')) {
|
||||
rcube::raise_error("PHP system() function is required. Check disable_functions in php.ini.", false, true);
|
||||
}
|
||||
|
||||
$target_dir = unslashify(end($_SERVER['argv']));
|
||||
$accept = in_array('-y', $_SERVER['argv']) ? 'y' : null;
|
||||
|
||||
if (empty($target_dir) || !is_dir(realpath($target_dir))) {
|
||||
rcube::raise_error("Invalid target: not a directory\nUsage: installto.sh [-y] <TARGET>", false, true);
|
||||
}
|
||||
|
||||
// read version from iniset.php
|
||||
$iniset = @file_get_contents($target_dir . '/program/include/iniset.php');
|
||||
if (!preg_match('/define\(.RCMAIL_VERSION.,\s*.([0-9.]+[a-z0-9-]*)/', $iniset, $m)) {
|
||||
rcube::raise_error("No valid Roundcube installation found at $target_dir", false, true);
|
||||
}
|
||||
|
||||
$oldversion = $m[1];
|
||||
|
||||
if (version_compare(version_parse($oldversion), version_parse(RCMAIL_VERSION), '>')) {
|
||||
rcube::raise_error("Target installation already in version $oldversion.", false, true);
|
||||
}
|
||||
|
||||
if (version_compare(version_parse($oldversion), version_parse(RCMAIL_VERSION), '==')) {
|
||||
echo "Target installation already in version $oldversion. Do you want to update again? (y/N)\n";
|
||||
}
|
||||
else {
|
||||
echo "Upgrading from $oldversion. Do you want to continue? (y/N)\n";
|
||||
}
|
||||
|
||||
$input = $accept ?: trim(fgets(STDIN));
|
||||
|
||||
if (strtolower($input) == 'y') {
|
||||
echo "Copying files to target location...";
|
||||
|
||||
$adds = [];
|
||||
$dirs = ['bin','SQL','plugins','skins','program'];
|
||||
|
||||
if (is_dir(INSTALL_PATH . 'vendor') && (!is_file("$target_dir/composer.json") || rcmail_install::vendor_dir_untouched($target_dir))) {
|
||||
$dirs[] = 'vendor';
|
||||
}
|
||||
if (file_exists("$target_dir/installer")) {
|
||||
$dirs[] = 'installer';
|
||||
}
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
// @FIXME: should we use --delete for all directories?
|
||||
$delete = in_array($dir, ['program', 'vendor', 'installer']) ? '--delete ' : '';
|
||||
$command = "rsync -aC --out-format=%n " . $delete . INSTALL_PATH . "$dir/ $target_dir/$dir/";
|
||||
|
||||
if (system($command, $ret) === false || $ret > 0) {
|
||||
rcube::raise_error("Failed to execute command: $command", false, true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (['index.php','config/defaults.inc.php','composer.json-dist','jsdeps.json','CHANGELOG.md','README.md','UPGRADING','LICENSE','INSTALL'] as $file) {
|
||||
$command = "rsync -a --out-format=%n " . INSTALL_PATH . "$file $target_dir/$file";
|
||||
|
||||
if (file_exists(INSTALL_PATH . $file) && (system($command, $ret) === false || $ret > 0)) {
|
||||
rcube::raise_error("Failed to execute command: $command", false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy .htaccess or .user.ini if needed
|
||||
foreach (['.htaccess','.user.ini'] as $file) {
|
||||
if (file_exists(INSTALL_PATH . $file)) {
|
||||
if (!file_exists("$target_dir/$file") || file_get_contents(INSTALL_PATH . $file) != file_get_contents("$target_dir/$file")) {
|
||||
if (copy(INSTALL_PATH . $file, "$target_dir/$file.new")) {
|
||||
echo "$file.new\n";
|
||||
$adds[] = "NOTICE: New $file file saved as $file.new.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remove old (<1.0) .htaccess file
|
||||
@unlink("$target_dir/program/.htaccess");
|
||||
echo "done.\n\n";
|
||||
|
||||
if (is_dir("$target_dir/skins/default")) {
|
||||
echo "Removing old default skin...";
|
||||
system("rm -rf $target_dir/skins/default $target_dir/plugins/jqueryui/themes/default");
|
||||
foreach (glob(INSTALL_PATH . "plugins/*/skins") as $plugin_skin_dir) {
|
||||
$plugin_skin_dir = preg_replace('!^.*' . INSTALL_PATH . '!', '', $plugin_skin_dir);
|
||||
if (is_dir("$target_dir/$plugin_skin_dir/classic")) {
|
||||
system("rm -rf $target_dir/$plugin_skin_dir/default");
|
||||
}
|
||||
}
|
||||
echo "done.\n\n";
|
||||
}
|
||||
|
||||
// Warn about situation when using "complete" package to update "custom" installation (#7087)
|
||||
// Note: "Complete" package do not include jsdeps.json nor install-jsdeps.sh
|
||||
if (file_exists("$target_dir/jsdeps.json") && !file_exists(INSTALL_PATH . "jsdeps.json")) {
|
||||
$adds[] = "WARNING: JavaScript dependencies update skipped. New jsdeps.json file not found.";
|
||||
}
|
||||
// check if js-deps are up-to-date
|
||||
else if (file_exists("$target_dir/jsdeps.json") && file_exists("$target_dir/bin/install-jsdeps.sh")) {
|
||||
$jsdeps = json_decode(file_get_contents("$target_dir/jsdeps.json"));
|
||||
$package = $jsdeps->dependencies[0];
|
||||
$dest_file = $target_dir . '/' . $package->dest;
|
||||
|
||||
if (!file_exists($dest_file) || sha1_file($dest_file) !== $package->sha1) {
|
||||
echo "Installing JavaScript dependencies...";
|
||||
system("cd $target_dir && bin/install-jsdeps.sh");
|
||||
echo "done.\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists("$target_dir/installer")) {
|
||||
$adds[] = "NOTICE: The 'installer' directory still exists. You should remove it after the upgrade.";
|
||||
}
|
||||
|
||||
if (!empty($adds)) {
|
||||
echo implode("\n", $adds) . "\n\n";
|
||||
}
|
||||
|
||||
echo "Running update script at target...\n";
|
||||
system("cd $target_dir && php bin/update.sh --version=$oldversion" . ($accept ? ' -y' : ''));
|
||||
echo "All done.\n";
|
||||
}
|
||||
else {
|
||||
echo "Update cancelled. See ya!\n";
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
PWD=`dirname "$0"`
|
||||
LANG_IN='ECMASCRIPT5'
|
||||
|
||||
do_shrink() {
|
||||
rm -f "$2"
|
||||
# copy the first comment block with license information for LibreJS
|
||||
grep -q '@lic' $1 && sed -n '/\/\*/,/\*\// { p; /\*\//q; }' $1 > $2
|
||||
uglifyjs --compress --mangle -- $1 >> $2
|
||||
}
|
||||
|
||||
if which uglifyjs > /dev/null 2>&1; then
|
||||
:
|
||||
else
|
||||
echo "uglifyjs not found. Please install e.g. 'npm install -g uglify-js'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compress single file from argument
|
||||
if [ $# -gt 0 ]; then
|
||||
JS_FILE="$1"
|
||||
|
||||
if [ $# -gt 1 ]; then
|
||||
LANG_IN="$2"
|
||||
fi
|
||||
|
||||
echo "Shrinking $JS_FILE"
|
||||
minfile=`echo $JS_FILE | sed -e 's/\.js$/\.min\.js/'`
|
||||
do_shrink "$JS_FILE" "$minfile" "$LANG_IN"
|
||||
exit
|
||||
fi
|
||||
|
||||
DIRS="$PWD/../program/js $PWD/../skins/* $PWD/../plugins/* $PWD/../plugins/*/skins/* $PWD/../plugins/managesieve/codemirror/lib"
|
||||
# default: compress application scripts
|
||||
for dir in $DIRS; do
|
||||
for file in $dir/*.js; do
|
||||
if echo "$file" | grep -q -e '.min.js$'; then
|
||||
continue
|
||||
fi
|
||||
if [ ! -f "$file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Shrinking $file"
|
||||
minfile=`echo $file | sed -e 's/\.js$/\.min\.js/'`
|
||||
do_shrink "$file" "$minfile" "$LANG_IN"
|
||||
done
|
||||
done
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -x
|
||||
|
||||
BIN_PHPDOC=`/usr/bin/which phpdoc`
|
||||
|
||||
if [ ! -x "$BIN_PHPDOC" ]
|
||||
then
|
||||
echo "phpdoc not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INSTALL_PATH="`dirname $0`/.."
|
||||
PATH_PROJECT=$INSTALL_PATH/program/include
|
||||
PATH_FRAMEWORK=$INSTALL_PATH/program/lib/Roundcube
|
||||
PATH_DOCS=$INSTALL_PATH/doc/phpdoc
|
||||
TITLE="Roundcube Webmail"
|
||||
PACKAGES="Webmail"
|
||||
OUTPUTFORMAT=HTML
|
||||
TEMPLATE=responsive-twig
|
||||
|
||||
# make documentation
|
||||
$BIN_PHPDOC -d $PATH_PROJECT,$PATH_FRAMEWORK -t $PATH_DOCS --title "$TITLE" \
|
||||
--defaultpackagename $PACKAGES --template=$TEMPLATE
|
||||
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Bulk-change settings stored in user preferences |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH.'program/include/clisetup.php';
|
||||
|
||||
function print_usage()
|
||||
{
|
||||
print "Usage: moduserprefs.sh [options] pref-name [pref-value]\n";
|
||||
print "Options:\n";
|
||||
print " --user=user-id User ID in local database\n";
|
||||
print " --config=path Location of additional configuration file\n";
|
||||
print " --delete Unset the given preference\n";
|
||||
print " --type=type Pref-value type: int, bool, string\n";
|
||||
}
|
||||
|
||||
|
||||
// get arguments
|
||||
$args = rcube_utils::get_opt([
|
||||
'u' => 'user',
|
||||
'd' => 'delete:bool',
|
||||
't' => 'type',
|
||||
'c' => 'config',
|
||||
]);
|
||||
|
||||
if (empty($_SERVER['argv'][1]) || $_SERVER['argv'][1] == 'help') {
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
else if (empty($args[0]) || (empty($args[1]) && empty($args['delete']))) {
|
||||
print "Missing required parameters.\n";
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
|
||||
$pref_name = trim($args[0]);
|
||||
$pref_value = !empty($args['delete']) ? null : trim($args[1]);
|
||||
|
||||
if ($pref_value === null) {
|
||||
$args['type'] = null;
|
||||
}
|
||||
|
||||
if (!empty($args['config'])) {
|
||||
$rcube = rcube::get_instance();
|
||||
$rcube->config->load_from_file($args['config']);
|
||||
}
|
||||
|
||||
$type = isset($args['type']) ? $args['type'] : null;
|
||||
$user = isset($args['user']) ? $args['user'] : null;
|
||||
|
||||
rcmail_utils::mod_pref($pref_name, $pref_value, $user, $type);
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <thomas@roundcube.net> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
ini_set('memory_limit', -1);
|
||||
|
||||
require_once INSTALL_PATH.'program/include/clisetup.php';
|
||||
|
||||
function print_usage()
|
||||
{
|
||||
print "Usage: msgexport.sh -h imap-host -u user-name -m mailbox name\n";
|
||||
print "--host IMAP host\n";
|
||||
print "--user IMAP user name\n";
|
||||
print "--mbox Folder name, set to '*' for all\n";
|
||||
print "--file Output file\n";
|
||||
}
|
||||
|
||||
function vputs($str)
|
||||
{
|
||||
$out = !empty($GLOBALS['args']['file']) ? STDOUT : STDERR;
|
||||
fwrite($out, $str);
|
||||
}
|
||||
|
||||
function progress_update($pos, $max)
|
||||
{
|
||||
$percent = round(100 * $pos / $max);
|
||||
vputs(sprintf("%3d%% [%-51s] %d/%d\033[K\r", $percent, @str_repeat('=', $percent / 2) . '>', $pos, $max));
|
||||
}
|
||||
|
||||
function export_mailbox($mbox, $filename)
|
||||
{
|
||||
global $IMAP;
|
||||
|
||||
$IMAP->set_folder($mbox);
|
||||
|
||||
vputs("Getting message list of {$mbox}...");
|
||||
|
||||
$index = $IMAP->index($mbox, null, 'ASC');
|
||||
$count = $index->count();
|
||||
$index = $index->get();
|
||||
|
||||
vputs("$count messages\n");
|
||||
|
||||
if ($filename) {
|
||||
if (!($out = fopen($filename, 'w'))) {
|
||||
vputs("Cannot write to output file\n");
|
||||
return;
|
||||
}
|
||||
vputs("Writing to $filename\n");
|
||||
}
|
||||
else {
|
||||
$out = STDOUT;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$headers = $IMAP->get_message_headers($index[$i]);
|
||||
$from = current(rcube_mime::decode_address_list($headers->from, 1, false));
|
||||
|
||||
fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid));
|
||||
$IMAP->get_raw_body($headers->uid, $out);
|
||||
fwrite($out, "\n\n\n");
|
||||
|
||||
progress_update($i+1, $count);
|
||||
}
|
||||
vputs("\ncomplete.\n");
|
||||
|
||||
if ($filename) {
|
||||
fclose($out);
|
||||
}
|
||||
}
|
||||
|
||||
// get arguments
|
||||
$opts = ['h' => 'host', 'u' => 'user', 'p' => 'pass', 'm' => 'mbox', 'f' => 'file'];
|
||||
$args = rcube_utils::get_opt($opts) + ['host' => 'localhost', 'mbox' => 'INBOX'];
|
||||
|
||||
if (!isset($_SERVER['argv'][1]) || $_SERVER['argv'][1] == 'help') {
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
else if (!$args['host']) {
|
||||
vputs("Missing required parameters.\n");
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
|
||||
// prompt for username if not set
|
||||
if (empty($args['user'])) {
|
||||
vputs("IMAP user: ");
|
||||
$args['user'] = trim(fgets(STDIN));
|
||||
}
|
||||
|
||||
// prompt for password
|
||||
$args['pass'] = rcube_utils::prompt_silent("Password: ");
|
||||
|
||||
|
||||
// parse $host URL
|
||||
$a_host = parse_url($args['host']);
|
||||
if (!empty($a_host['host'])) {
|
||||
$host = $a_host['host'];
|
||||
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], ['ssl','imaps','tls'])) ? TRUE : FALSE;
|
||||
$imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143);
|
||||
}
|
||||
else {
|
||||
$host = $args['host'];
|
||||
$imap_port = 143;
|
||||
$imap_ssl = false;
|
||||
}
|
||||
|
||||
// instantiate IMAP class
|
||||
$IMAP = new rcube_imap(null);
|
||||
|
||||
// try to connect to IMAP server
|
||||
if ($IMAP->connect($host, $args['user'], $args['pass'], $imap_port, $imap_ssl)) {
|
||||
vputs("IMAP login successful.\n");
|
||||
|
||||
$filename = null;
|
||||
$mailboxes = $args['mbox'] == '*' ? $IMAP->list_folders(null) : [$args['mbox']];
|
||||
|
||||
foreach ($mailboxes as $mbox) {
|
||||
if (!empty($args['file'])) {
|
||||
$filename = preg_replace('/\.[a-z0-9]{3,4}$/i', '', $args['file']) . asciiwords($mbox) . '.mbox';
|
||||
}
|
||||
else if ($args['mbox'] == '*') {
|
||||
$filename = asciiwords($mbox) . '.mbox';
|
||||
}
|
||||
|
||||
if ($args['mbox'] == '*' && in_array(strtolower($mbox), ['junk','spam','trash'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
export_mailbox($mbox, $filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
vputs("IMAP login failed.\n");
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <thomas@roundcube.net> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
ini_set('memory_limit', -1);
|
||||
|
||||
require_once INSTALL_PATH.'program/include/clisetup.php';
|
||||
|
||||
function print_usage()
|
||||
{
|
||||
print "Usage: msgimport.sh -h imap-host -u user-name -m mailbox -f message-file\n";
|
||||
print "--host IMAP host\n";
|
||||
print "--user IMAP user name\n";
|
||||
print "--mbox Target mailbox\n";
|
||||
print "--file Message file to upload\n";
|
||||
}
|
||||
|
||||
// get arguments
|
||||
$opts = ['h' => 'host', 'u' => 'user', 'p' => 'pass', 'm' => 'mbox', 'f' => 'file'];
|
||||
$args = rcube_utils::get_opt($opts) + ['host' => 'localhost', 'mbox' => 'INBOX'];
|
||||
|
||||
if (!isset($_SERVER['argv'][1]) || $_SERVER['argv'][1] == 'help') {
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
else if (empty($args['host']) || empty($args['file'])) {
|
||||
print "Missing required parameters.\n";
|
||||
print_usage();
|
||||
exit;
|
||||
}
|
||||
else if (!is_file($args['file'])) {
|
||||
rcube::raise_error("Cannot read message file.", false, true);
|
||||
}
|
||||
|
||||
// prompt for username if not set
|
||||
if (empty($args['user'])) {
|
||||
//fwrite(STDOUT, "Please enter your name\n");
|
||||
echo "IMAP user: ";
|
||||
$args['user'] = trim(fgets(STDIN));
|
||||
}
|
||||
|
||||
// prompt for password
|
||||
if (empty($args['pass'])) {
|
||||
$args['pass'] = rcube_utils::prompt_silent("Password: ");
|
||||
}
|
||||
|
||||
// parse $host URL
|
||||
$a_host = parse_url($args['host']);
|
||||
if (!empty($a_host['host'])) {
|
||||
$host = $a_host['host'];
|
||||
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], ['ssl','imaps','tls'])) ? TRUE : FALSE;
|
||||
$imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : 143);
|
||||
}
|
||||
else {
|
||||
$host = $args['host'];
|
||||
$imap_port = 143;
|
||||
$imap_ssl = false;
|
||||
}
|
||||
|
||||
// instantiate IMAP class
|
||||
$IMAP = new rcube_imap(null);
|
||||
|
||||
// try to connect to IMAP server
|
||||
if ($IMAP->connect($host, $args['user'], $args['pass'], $imap_port, $imap_ssl)) {
|
||||
print "IMAP login successful.\n";
|
||||
print "Uploading messages...\n";
|
||||
|
||||
$count = 0;
|
||||
$message = $lastline = '';
|
||||
|
||||
$fp = fopen($args['file'], 'r');
|
||||
while (($line = fgets($fp)) !== false) {
|
||||
if (preg_match('/^From\s+-/', $line) && $lastline == '') {
|
||||
if (!empty($message)) {
|
||||
if ($IMAP->save_message($args['mbox'], rtrim($message))) {
|
||||
$count++;
|
||||
}
|
||||
else {
|
||||
rcube::raise_error("Failed to save message to {$args['mbox']}", false, true);
|
||||
}
|
||||
$message = '';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$message .= $line;
|
||||
$lastline = rtrim($line);
|
||||
}
|
||||
|
||||
if (!empty($message) && $IMAP->save_message($args['mbox'], rtrim($message))) {
|
||||
$count++;
|
||||
}
|
||||
|
||||
// upload message from file
|
||||
if ($count) {
|
||||
print "$count messages successfully added to {$args['mbox']}.\n";
|
||||
}
|
||||
else {
|
||||
print "Adding messages failed!\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
rcube::raise_error("IMAP login failed.", false, true);
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Check local configuration and database schema after upgrading |
|
||||
| to a new version |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
// get arguments
|
||||
$opts = rcube_utils::get_opt(['v' => 'version', 'y' => 'accept:bool']);
|
||||
|
||||
// ask user if no version is specified
|
||||
if (empty($opts['version'])) {
|
||||
echo "What version are you upgrading from? Type '?' if you don't know.\n";
|
||||
|
||||
if (($input = trim(fgets(STDIN))) && preg_match('/^[0-9.]+[a-z0-9-]*$/', $input)) {
|
||||
$opts['version'] = $input;
|
||||
}
|
||||
else {
|
||||
$opts['version'] = RCMAIL_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
$RCI = rcmail_install::get_instance();
|
||||
$RCI->load_config();
|
||||
|
||||
if ($RCI->configured) {
|
||||
$success = true;
|
||||
|
||||
if (($messages = $RCI->check_config($opts['version'])) || $RCI->legacy_config) {
|
||||
$success = false;
|
||||
$err = 0;
|
||||
|
||||
// list old/replaced config options
|
||||
if (!empty($messages['replaced'])) {
|
||||
echo "WARNING: Replaced config options:\n";
|
||||
echo "(These config options have been replaced or renamed)\n";
|
||||
|
||||
foreach ($messages['replaced'] as $msg) {
|
||||
echo "- '" . $msg['prop'] . "' was replaced by '" . $msg['replacement'] . "'\n";
|
||||
$err++;
|
||||
}
|
||||
}
|
||||
|
||||
// list obsolete config options (just a notice)
|
||||
if (!empty($messages['obsolete'])) {
|
||||
echo "NOTICE: Obsolete config options:\n";
|
||||
echo "(You still have some obsolete or inexistent properties set."
|
||||
. " This isn't a problem but should be noticed)\n";
|
||||
|
||||
foreach ($messages['obsolete'] as $msg) {
|
||||
echo "- '" . $msg['prop'] . (!empty($msg['explain']) ? "': " . $msg['explain'] : "'") . "\n";
|
||||
$err++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$err && $RCI->legacy_config) {
|
||||
echo "WARNING: Your configuration needs to be migrated!\n";
|
||||
echo "We changed the configuration files structure and your two config files "
|
||||
. "main.inc.php and db.inc.php have to be merged into one single file.\n";
|
||||
$err++;
|
||||
}
|
||||
|
||||
// ask user to update config files
|
||||
if ($err) {
|
||||
if (empty($opts['accept'])) {
|
||||
echo "Do you want me to fix your local configuration? (y/N)\n";
|
||||
$input = trim(fgets(STDIN));
|
||||
}
|
||||
|
||||
// positive: merge the local config with the defaults
|
||||
if (!empty($opts['accept']) || strtolower($input) == 'y') {
|
||||
$error = $written = false;
|
||||
|
||||
echo "- backing up the current config file(s)...\n";
|
||||
|
||||
foreach (['config', 'main', 'db'] as $file) {
|
||||
if (file_exists(RCMAIL_CONFIG_DIR . '/' . $file . '.inc.php')) {
|
||||
if (!copy(RCMAIL_CONFIG_DIR . '/' . $file . '.inc.php', RCMAIL_CONFIG_DIR . '/' . $file . '.old.php')) {
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$RCI->merge_config();
|
||||
echo "- writing " . RCMAIL_CONFIG_DIR . "/config.inc.php...\n";
|
||||
$written = $RCI->save_configfile($RCI->create_config(false));
|
||||
}
|
||||
|
||||
// Success!
|
||||
if ($written) {
|
||||
echo "Done.\n";
|
||||
echo "Your configuration files are now up-to-date!\n";
|
||||
|
||||
if (!empty($messages['missing'])) {
|
||||
echo "But you still need to add the following missing options:\n";
|
||||
foreach ($messages['missing'] as $msg) {
|
||||
echo "- '" . $msg['prop'] . ($msg['name'] ? "': " . $msg['name'] : "'") . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($RCI->legacy_config) {
|
||||
foreach (['main', 'db'] as $file) {
|
||||
@unlink(RCMAIL_CONFIG_DIR . '/' . $file . '.inc.php');
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo "Failed to write config file(s)!\n";
|
||||
echo "Grant write privileges to the current user or update the files manually "
|
||||
. "according to the above messages.\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo "Please update your config files manually according to the above messages.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// list of config options with changed default (just a notice)
|
||||
if (!empty($messages['defaults'])) {
|
||||
echo "WARNING: Changed defaults (These config options have new default values):\n";
|
||||
|
||||
foreach ($messages['defaults'] as $opt) {
|
||||
echo "- '{$opt}'\n";
|
||||
}
|
||||
}
|
||||
|
||||
// check dependencies based on the current configuration
|
||||
if (!empty($messages['dependencies'])) {
|
||||
echo "WARNING: Dependency check failed!\n";
|
||||
echo "(Some of your configuration settings require other options to be configured "
|
||||
. "or additional PHP modules to be installed)\n";
|
||||
|
||||
foreach ($messages['dependencies'] as $msg) {
|
||||
echo "- " . $msg['prop'] . ': ' . $msg['explain'] . "\n";
|
||||
}
|
||||
|
||||
echo "Please fix your config files and run this script again!\n";
|
||||
echo "See ya.\n";
|
||||
}
|
||||
}
|
||||
|
||||
// check file type detection
|
||||
if ($RCI->check_mime_detection()) {
|
||||
echo "WARNING: File type detection doesn't work properly!\n";
|
||||
echo "Please check the 'mime_magic' config option or the finfo functions of PHP and run this script again.\n";
|
||||
}
|
||||
if ($RCI->check_mime_extensions()) {
|
||||
echo "WARNING: Mimetype to file extension mapping doesn't work properly!\n";
|
||||
echo "Please check the 'mime_types' config option and run this script again.\n";
|
||||
}
|
||||
|
||||
// check database schema
|
||||
if (!empty($RCI->config['db_dsnw'])) {
|
||||
echo "Executing database schema update.\n";
|
||||
$success = rcmail_utils::db_update(INSTALL_PATH . 'SQL', 'roundcube', $opts['version'], ['errors' => true]);
|
||||
}
|
||||
|
||||
// update composer dependencies
|
||||
if (is_file(INSTALL_PATH . 'composer.json') && is_readable(INSTALL_PATH . 'composer.json-dist')) {
|
||||
$composer_data = json_decode(file_get_contents(INSTALL_PATH . 'composer.json'), true);
|
||||
$composer_template = json_decode(file_get_contents(INSTALL_PATH . 'composer.json-dist'), true);
|
||||
$composer_json = null;
|
||||
|
||||
// update the require section with the new dependencies
|
||||
if (!empty($composer_data['require']) && !empty($composer_template['require'])) {
|
||||
$composer_data['require'] = array_merge($composer_data['require'], $composer_template['require']);
|
||||
|
||||
// remove obsolete packages
|
||||
$old_packages = [
|
||||
'pear-pear.php.net/net_socket',
|
||||
'pear-pear.php.net/auth_sasl',
|
||||
'pear-pear.php.net/net_idna2',
|
||||
'pear-pear.php.net/mail_mime',
|
||||
'pear-pear.php.net/net_smtp',
|
||||
'pear-pear.php.net/crypt_gpg',
|
||||
'pear-pear.php.net/net_sieve',
|
||||
'pear/mail_mime-decode',
|
||||
'roundcube/net_sieve',
|
||||
'endroid/qrcode',
|
||||
'endroid/qr-code',
|
||||
];
|
||||
|
||||
foreach ($old_packages as $pkg) {
|
||||
if (array_key_exists($pkg, $composer_data['require'])) {
|
||||
unset($composer_data['require'][$pkg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the repositories section with the new dependencies
|
||||
if (!empty($composer_template['repositories'])) {
|
||||
if (empty($composer_data['repositories'])) {
|
||||
$composer_data['repositories'] = [];
|
||||
}
|
||||
|
||||
foreach ($composer_template['repositories'] as $repo) {
|
||||
$rkey = repo_key($repo);
|
||||
$existing = false;
|
||||
|
||||
foreach ($composer_data['repositories'] as $k => $_repo) {
|
||||
if ($rkey == repo_key($_repo)) {
|
||||
// switch to https://
|
||||
if (isset($_repo['url']) && strpos($_repo['url'], 'http://') === 0) {
|
||||
$composer_data['repositories'][$k]['url'] = 'https:' . substr($_repo['url'], 5);
|
||||
}
|
||||
|
||||
$existing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// remove old repos
|
||||
if (isset($_repo['url']) && strpos($_repo['url'], 'git://git.kolab.org') === 0) {
|
||||
unset($composer_data['repositories'][$k]);
|
||||
}
|
||||
else if (
|
||||
$_repo['type'] == 'package'
|
||||
&& !empty($_repo['package']['name'])
|
||||
&& $_repo['package']['name'] == 'Net_SMTP'
|
||||
) {
|
||||
unset($composer_data['repositories'][$k]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$existing) {
|
||||
$composer_data['repositories'][] = $repo;
|
||||
}
|
||||
}
|
||||
|
||||
$composer_data['repositories'] = array_values($composer_data['repositories']);
|
||||
}
|
||||
|
||||
$composer_json = json_encode($composer_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
// write updated composer.json back to disk
|
||||
if ($composer_json && is_writeable(INSTALL_PATH . 'composer.json')) {
|
||||
$success &= (bool)file_put_contents(INSTALL_PATH . 'composer.json', $composer_json);
|
||||
}
|
||||
else {
|
||||
echo "WARNING: unable to update composer.json!\n";
|
||||
echo "Please replace the 'require' section in your composer.json with the following:\n";
|
||||
|
||||
$require_json = '';
|
||||
foreach ($composer_data['require'] as $pkg => $ver) {
|
||||
$require_json .= sprintf(' "%s": "%s",'."\n", $pkg, $ver);
|
||||
}
|
||||
|
||||
echo ' "require": {'."\n";
|
||||
echo rtrim($require_json, ",\n");
|
||||
echo "\n }\n\n";
|
||||
}
|
||||
|
||||
if (!rcmail_install::vendor_dir_untouched(INSTALL_PATH)) {
|
||||
$exit_code = 1;
|
||||
if ($composer_bin = find_composer()) {
|
||||
echo "Executing " . $composer_bin . " to update dependencies...\n";
|
||||
echo system("$composer_bin update -d " . escapeshellarg(INSTALL_PATH) . " --no-dev", $exit_code);
|
||||
}
|
||||
if ($exit_code != 0) {
|
||||
echo "-----------------------------------------------------------------------------\n";
|
||||
echo "ATTENTION: Update dependencies by running `php composer.phar update --no-dev`\n";
|
||||
echo "-----------------------------------------------------------------------------\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// index contacts for fulltext searching
|
||||
if ($opts['version'] && version_compare(version_parse($opts['version']), '0.6.0', '<')) {
|
||||
rcmail_utils::indexcontacts();
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
echo "This instance of Roundcube is up-to-date.\n";
|
||||
echo "Have fun!\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
echo "This instance of Roundcube is not yet configured!\n";
|
||||
echo "Open http://url-to-roundcube/installer/ in your browser and follow the instructions.\n";
|
||||
}
|
||||
|
||||
function repo_key($repo)
|
||||
{
|
||||
$key = $repo['type'];
|
||||
|
||||
if (!empty($repo['url'])) {
|
||||
$key .= preg_replace('/^https?:/', '', $repo['url']);
|
||||
}
|
||||
|
||||
if (!empty($repo['package']['name'])) {
|
||||
$key .= $repo['package']['name'];
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
function find_composer()
|
||||
{
|
||||
if (is_file(INSTALL_PATH . 'composer.phar')) {
|
||||
return 'php composer.phar';
|
||||
}
|
||||
|
||||
foreach (['composer', 'composer.phar'] as $check_file) {
|
||||
$which = trim(system("which $check_file"));
|
||||
if (!empty($which)) {
|
||||
return $which;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Update cache-baster marks for css background images |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
// get arguments
|
||||
$opts = rcube_utils::get_opt(['d' => 'dir']);
|
||||
|
||||
if (empty($opts['dir'])) {
|
||||
print "Skin directory not specified (--dir). Using skins/ and plugins/*/skins/.\n";
|
||||
|
||||
$dir = INSTALL_PATH . 'skins';
|
||||
$dir_p = INSTALL_PATH . 'plugins';
|
||||
$skins = glob("$dir/*", GLOB_ONLYDIR);
|
||||
$skins_p = glob("$dir_p/*/skins/*", GLOB_ONLYDIR);
|
||||
|
||||
$dirs = array_merge($skins, $skins_p);
|
||||
}
|
||||
// Check if directory exists
|
||||
else if (!file_exists($opts['dir'])) {
|
||||
rcube::raise_error("Specified directory doesn't exist.", false, true);
|
||||
}
|
||||
else {
|
||||
$dirs = [$opts['dir']];
|
||||
}
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
$img_dir = $dir . '/images';
|
||||
if (!file_exists($img_dir)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$files = get_files($dir);
|
||||
$images = get_images($img_dir);
|
||||
$find = [];
|
||||
$replace = [];
|
||||
|
||||
// build regexps array
|
||||
foreach ($images as $path => $sum) {
|
||||
$path_ex = str_replace('.', '\\.', $path);
|
||||
$find[] = "#url\(['\"]?images/$path_ex(\?v=[a-f0-9-\.]+)?['\"]?\)#";
|
||||
$replace[] = "url(images/$path?v=$sum)";
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file = $dir . '/' . $file;
|
||||
print "File: $file\n";
|
||||
$content = file_get_contents($file);
|
||||
$content = preg_replace($find, $replace, $content, -1, $count);
|
||||
if ($count) {
|
||||
file_put_contents($file, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function get_images($dir)
|
||||
{
|
||||
$images = [];
|
||||
$dh = opendir($dir);
|
||||
|
||||
while ($file = readdir($dh)) {
|
||||
if (preg_match('/^(.+)\.(gif|ico|png|jpg|jpeg)$/', $file, $m)) {
|
||||
$filepath = "$dir/$file";
|
||||
$images[$file] = substr(md5_file($filepath), 0, 4) . '.' . filesize($filepath);
|
||||
print "Image: $filepath ({$images[$file]})\n";
|
||||
}
|
||||
else if ($file != '.' && $file != '..' && is_dir($dir . '/' . $file)) {
|
||||
foreach (get_images($dir . '/' . $file) as $img => $sum) {
|
||||
$images[$file . '/' . $img] = $sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($dh);
|
||||
|
||||
return $images;
|
||||
}
|
||||
|
||||
function get_files($dir)
|
||||
{
|
||||
$files = [];
|
||||
$dh = opendir($dir);
|
||||
|
||||
while ($file = readdir($dh)) {
|
||||
if (preg_match('/^(.+)\.(css|html)$/', $file, $m)) {
|
||||
$files[] = $file;
|
||||
}
|
||||
else if ($file != '.' && $file != '..' && is_dir($dir . '/' . $file)) {
|
||||
foreach (get_files($dir . '/' . $file) as $f) {
|
||||
$files[] = $file . '/' . $f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir($dh);
|
||||
|
||||
return $files;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Update database schema |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' );
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
// get arguments
|
||||
$opts = rcube_utils::get_opt([
|
||||
'v' => 'version',
|
||||
'd' => 'dir',
|
||||
'p' => 'package',
|
||||
]);
|
||||
|
||||
if (empty($opts['dir'])) {
|
||||
rcube::raise_error("Database schema directory not specified (--dir).", false, true);
|
||||
}
|
||||
if (empty($opts['package'])) {
|
||||
rcube::raise_error("Database schema package name not specified (--package).", false, true);
|
||||
}
|
||||
|
||||
rcmail_utils::db_update($opts['dir'], $opts['package'], $opts['version'] ?? null, ['errors' => true]);
|
||||
@@ -0,0 +1,7 @@
|
||||
# deny webserver access to this directory
|
||||
<ifModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</ifModule>
|
||||
<ifModule !mod_authz_core.c>
|
||||
Deny from all
|
||||
</ifModule>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/* Local configuration for Roundcube Webmail */
|
||||
|
||||
// ----------------------------------
|
||||
// SQL DATABASE
|
||||
// ----------------------------------
|
||||
// Database connection string (DSN) for read+write operations
|
||||
// Format (compatible with PEAR MDB2): db_provider://user:password@host/database
|
||||
// Currently supported db_providers: mysql, pgsql, sqlite, mssql, sqlsrv, oracle
|
||||
// For examples see http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
|
||||
// Note: for SQLite use absolute path (Linux): 'sqlite:////full/path/to/sqlite.db?mode=0646'
|
||||
// or (Windows): 'sqlite:///C:/full/path/to/sqlite.db'
|
||||
// Note: Various drivers support various additional arguments for connection,
|
||||
// for Mysql: key, cipher, cert, capath, ca, verify_server_cert,
|
||||
// for Postgres: application_name, sslmode, sslcert, sslkey, sslrootcert, sslcrl, sslcompression, service.
|
||||
// e.g. 'mysql://roundcube:@localhost/roundcubemail?verify_server_cert=false'
|
||||
$config['db_dsnw'] = 'mysql://root:@localhost/roundcubemail';
|
||||
|
||||
// Syslog ident string to use, if using the 'syslog' log driver.
|
||||
$config['syslog_id'] = 'rutymail';
|
||||
|
||||
// ----------------------------------
|
||||
// IMAP
|
||||
// ----------------------------------
|
||||
// The IMAP host (and optionally port number) chosen to perform the log-in.
|
||||
// Leave blank to show a textbox at login, give a list of hosts
|
||||
// to display a pulldown menu or set one host as string.
|
||||
// Enter hostname with prefix ssl:// to use Implicit TLS, or use
|
||||
// prefix tls:// to use STARTTLS.
|
||||
// If port number is omitted it will be set to 993 (for ssl://) or 143 otherwise.
|
||||
// Supported replacement variables:
|
||||
// %n - hostname ($_SERVER['SERVER_NAME'])
|
||||
// %t - hostname without the first part
|
||||
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
|
||||
// %s - domain name after the '@' from e-mail address provided at login screen
|
||||
// For example %n = mail.domain.tld, %t = domain.tld
|
||||
// WARNING: After hostname change update of mail_host column in users table is
|
||||
// required to match old user data records with the new host.
|
||||
$config['imap_host'] = 'localhost:143';
|
||||
|
||||
// provide an URL where a user can get support for this Roundcube installation
|
||||
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
|
||||
$config['support_url'] = '';
|
||||
|
||||
// This key is used for encrypting purposes, like storing of imap password
|
||||
// in the session. For historical reasons it's called DES_key, but it's used
|
||||
// with any configured cipher_method (see below).
|
||||
// For the default cipher_method a required key length is 24 characters.
|
||||
$config['des_key'] = 'Sx7kK03ueoGCZU8Q58J8Kopv';
|
||||
|
||||
// Name your service. This is displayed on the login screen and in the window title
|
||||
$config['product_name'] = 'Ruty - Webmail';
|
||||
|
||||
// ----------------------------------
|
||||
// PLUGINS
|
||||
// ----------------------------------
|
||||
// List of active plugins (in plugins/ directory)
|
||||
$config['plugins'] = ['acl', 'additional_message_headers', 'archive', 'attachment_reminder', 'autologon', 'autologout', 'database_attachments', 'debug_logger', 'emoticons', 'enigma', 'example_addressbook', 'filesystem_attachments', 'help', 'hide_blockquote', 'http_authentication', 'identicon', 'identity_select', 'jqueryui', 'krb_authentication', 'managesieve', 'markasjunk', 'new_user_dialog', 'new_user_identity', 'newmail_notifier', 'password', 'reconnect', 'redundant_attachments', 'show_additional_headers', 'squirrelmail_usercopy', 'subscriptions_option', 'userinfo', 'vcard_attachments', 'virtuser_file', 'virtuser_query', 'zipdownload'];
|
||||
|
||||
// the default locale setting (leave empty for auto-detection)
|
||||
// RFC1766 formatted language name like en_US, de_DE, de_CH, fr_FR, pt_BR
|
||||
$config['language'] = 'fr_FR';
|
||||
|
||||
// store draft message is this mailbox
|
||||
// leave blank if draft messages should not be stored
|
||||
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
|
||||
$config['drafts_mbox'] = 'Brouillons';
|
||||
|
||||
// store spam messages in this mailbox
|
||||
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
|
||||
$config['junk_mbox'] = 'Spam';
|
||||
|
||||
// store sent message is this mailbox
|
||||
// leave blank if sent messages should not be stored
|
||||
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
|
||||
$config['sent_mbox'] = 'Envoyés';
|
||||
|
||||
// move messages to this folder when deleting them
|
||||
// leave blank if they should be deleted directly
|
||||
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
|
||||
$config['trash_mbox'] = 'Poubelle';
|
||||
|
||||
// Encoding of long/non-ascii attachment names:
|
||||
// 0 - Full RFC 2231 compatible
|
||||
// 1 - RFC 2047 for 'name' and RFC 2231 for 'filename' parameter (Thunderbird's default)
|
||||
// 2 - Full 2047 compatible
|
||||
$config['mime_param_folding'] = 0;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| Local configuration for the Roundcube Webmail installation. |
|
||||
| |
|
||||
| This is a sample configuration file only containing the minimum |
|
||||
| setup required for a functional installation. Copy more options |
|
||||
| from defaults.inc.php to this file to override the defaults. |
|
||||
| |
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
$config = [];
|
||||
|
||||
// Database connection string (DSN) for read+write operations
|
||||
// Format (compatible with PEAR MDB2): db_provider://user:password@host/database
|
||||
// Currently supported db_providers: mysql, pgsql, sqlite, mssql, sqlsrv, oracle
|
||||
// For examples see http://pear.php.net/manual/en/package.database.mdb2.intro-dsn.php
|
||||
// NOTE: for SQLite use absolute path (Linux): 'sqlite:////full/path/to/sqlite.db?mode=0646'
|
||||
// or (Windows): 'sqlite:///C:/full/path/to/sqlite.db'
|
||||
$config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
|
||||
|
||||
// IMAP host chosen to perform the log-in.
|
||||
// See defaults.inc.php for the option description.
|
||||
$config['imap_host'] = 'localhost:143';
|
||||
|
||||
// SMTP server host (for sending mails).
|
||||
// See defaults.inc.php for the option description.
|
||||
$config['smtp_host'] = 'localhost:587';
|
||||
|
||||
// SMTP username (if required) if you use %u as the username Roundcube
|
||||
// will use the current username for login
|
||||
$config['smtp_user'] = '%u';
|
||||
|
||||
// SMTP password (if required) if you use %p as the password Roundcube
|
||||
// will use the current user's password for login
|
||||
$config['smtp_pass'] = '%p';
|
||||
|
||||
// provide an URL where a user can get support for this Roundcube installation
|
||||
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
|
||||
$config['support_url'] = '';
|
||||
|
||||
// Name your service. This is displayed on the login screen and in the window title
|
||||
$config['product_name'] = 'Roundcube Webmail';
|
||||
|
||||
// This key is used to encrypt the users imap password which is stored
|
||||
// in the session record. For the default cipher method it must be
|
||||
// exactly 24 characters long.
|
||||
// YOUR KEY MUST BE DIFFERENT THAN THE SAMPLE VALUE FOR SECURITY REASONS
|
||||
$config['des_key'] = 'rcmail-!24ByteDESkey*Str';
|
||||
|
||||
// List of active plugins (in plugins/ directory)
|
||||
$config['plugins'] = [
|
||||
'archive',
|
||||
'zipdownload',
|
||||
];
|
||||
|
||||
// skin name: folder from skins/
|
||||
$config['skin'] = 'elastic';
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Local mapping file to specify mime-types based on common file-name extensions
|
||||
*
|
||||
* Please note that this mapping takes precedence over the content-based mime-type detection
|
||||
* and should only contain mappings which cannot be detected properly from the file contents.
|
||||
*/
|
||||
|
||||
return [
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlm' => 'application/vnd.ms-excel',
|
||||
'xla' => 'application/vnd.ms-excel',
|
||||
'xlc' => 'application/vnd.ms-excel',
|
||||
'xlt' => 'application/vnd.ms-excel',
|
||||
'xlw' => 'application/vnd.ms-excel',
|
||||
'pdf' => 'application/pdf',
|
||||
'ppt' => 'application/vnd.ms-powerpoint',
|
||||
'pps' => 'application/vnd.ms-powerpoint',
|
||||
'pot' => 'application/vnd.ms-powerpoint',
|
||||
'doc' => 'application/msword',
|
||||
'dot' => 'application/msword',
|
||||
'odc' => 'application/vnd.oasis.opendocument.chart',
|
||||
'otc' => 'application/vnd.oasis.opendocument.chart-template',
|
||||
'odf' => 'application/vnd.oasis.opendocument.formula',
|
||||
'otf' => 'application/vnd.oasis.opendocument.formula-template',
|
||||
'odg' => 'application/vnd.oasis.opendocument.graphics',
|
||||
'otg' => 'application/vnd.oasis.opendocument.graphics-template',
|
||||
'odi' => 'application/vnd.oasis.opendocument.image',
|
||||
'oti' => 'application/vnd.oasis.opendocument.image-template',
|
||||
'odp' => 'application/vnd.oasis.opendocument.presentation',
|
||||
'otp' => 'application/vnd.oasis.opendocument.presentation-template',
|
||||
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
|
||||
'odt' => 'application/vnd.oasis.opendocument.text',
|
||||
'otm' => 'application/vnd.oasis.opendocument.text-master',
|
||||
'ott' => 'application/vnd.oasis.opendocument.text-template',
|
||||
'oth' => 'application/vnd.oasis.opendocument.text-web',
|
||||
'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
|
||||
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
||||
'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12',
|
||||
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
|
||||
'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
|
||||
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
|
||||
'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'xps' => 'application/vnd.ms-xpsdocument',
|
||||
'rar' => 'application/x-rar-compressed',
|
||||
'7z' => 'application/x-7z-compressed',
|
||||
's7z' => 'application/x-7z-compressed',
|
||||
'vcf' => 'text/vcard',
|
||||
'ics' => 'text/calendar',
|
||||
];
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
if (!class_exists('rcmail_install', false) || !isset($RCI)) {
|
||||
die("Not allowed! Please open installer/index.php instead.");
|
||||
}
|
||||
|
||||
$required_php_exts = [
|
||||
'PCRE' => 'pcre',
|
||||
'DOM' => 'dom',
|
||||
'Session' => 'session',
|
||||
'XML' => 'xml',
|
||||
'Intl' => 'intl',
|
||||
'JSON' => 'json',
|
||||
'PDO' => 'PDO',
|
||||
'Multibyte' => 'mbstring',
|
||||
'OpenSSL' => 'openssl',
|
||||
'Filter' => 'filter',
|
||||
'Ctype' => 'ctype',
|
||||
];
|
||||
|
||||
$optional_php_exts = [
|
||||
'cURL' => 'curl',
|
||||
'FileInfo' => 'fileinfo',
|
||||
'Exif' => 'exif',
|
||||
'Iconv' => 'iconv',
|
||||
'LDAP' => 'ldap',
|
||||
'GD' => 'gd',
|
||||
'Imagick' => 'imagick',
|
||||
'XMLWriter' => 'xmlwriter',
|
||||
'Zip' => 'zip',
|
||||
];
|
||||
|
||||
$required_libs = [
|
||||
'PEAR' => 'pear.php.net',
|
||||
'Auth_SASL' => 'pear.php.net',
|
||||
'Net_SMTP' => 'pear.php.net',
|
||||
'Mail_mime' => 'pear.php.net',
|
||||
'GuzzleHttp\Client' => 'github.com/guzzle/guzzle',
|
||||
];
|
||||
|
||||
$optional_libs = [
|
||||
'Net_LDAP3' => 'git.kolab.org',
|
||||
];
|
||||
|
||||
$ini_checks = [
|
||||
'file_uploads' => 1,
|
||||
'session.auto_start' => 0,
|
||||
'mbstring.func_overload' => 0,
|
||||
'suhosin.session.encrypt' => 0,
|
||||
];
|
||||
|
||||
$optional_checks = [
|
||||
'date.timezone' => '-VALID-',
|
||||
];
|
||||
|
||||
$source_urls = [
|
||||
'cURL' => 'https://www.php.net/manual/en/book.curl.php',
|
||||
'Sockets' => 'https://www.php.net/manual/en/book.sockets.php',
|
||||
'Session' => 'https://www.php.net/manual/en/book.session.php',
|
||||
'PCRE' => 'https://www.php.net/manual/en/book.pcre.php',
|
||||
'FileInfo' => 'https://www.php.net/manual/en/book.fileinfo.php',
|
||||
'Multibyte' => 'https://www.php.net/manual/en/book.mbstring.php',
|
||||
'OpenSSL' => 'https://www.php.net/manual/en/book.openssl.php',
|
||||
'JSON' => 'https://www.php.net/manual/en/book.json.php',
|
||||
'DOM' => 'https://www.php.net/manual/en/book.dom.php',
|
||||
'Iconv' => 'https://www.php.net/manual/en/book.iconv.php',
|
||||
'Intl' => 'https://www.php.net/manual/en/book.intl.php',
|
||||
'Exif' => 'https://www.php.net/manual/en/book.exif.php',
|
||||
'oci8' => 'https://www.php.net/manual/en/book.oci8.php',
|
||||
'PDO' => 'https://www.php.net/manual/en/book.pdo.php',
|
||||
'LDAP' => 'https://www.php.net/manual/en/book.ldap.php',
|
||||
'GD' => 'https://www.php.net/manual/en/book.image.php',
|
||||
'Imagick' => 'https://www.php.net/manual/en/book.imagick.php',
|
||||
'XML' => 'https://www.php.net/manual/en/book.xml.php',
|
||||
'XMLWriter' => 'https://www.php.net/manual/en/book.xmlwriter.php',
|
||||
'Zip' => 'https://www.php.net/manual/en/book.zip.php',
|
||||
'Filter' => 'https://www.php.net/manual/en/book.filter.php',
|
||||
'Ctype' => 'https://www.php.net/manual/en/book.ctype.php',
|
||||
'pdo_mysql' => 'https://www.php.net/manual/en/ref.pdo-mysql.php',
|
||||
'pdo_pgsql' => 'https://www.php.net/manual/en/ref.pdo-pgsql.php',
|
||||
'pdo_sqlite' => 'https://www.php.net/manual/en/ref.pdo-sqlite.php',
|
||||
'pdo_sqlite2' => 'https://www.php.net/manual/en/ref.pdo-sqlite.php',
|
||||
'pdo_sqlsrv' => 'https://www.php.net/manual/en/ref.pdo-sqlsrv.php',
|
||||
'pdo_dblib' => 'https://www.php.net/manual/en/ref.pdo-dblib.php',
|
||||
'PEAR' => 'https://pear.php.net',
|
||||
'Net_SMTP' => 'https://pear.php.net/package/Net_SMTP',
|
||||
'Mail_mime' => 'https://pear.php.net/package/Mail_mime',
|
||||
'Net_LDAP3' => 'https://git.kolab.org/diffusion/PNL',
|
||||
];
|
||||
|
||||
?>
|
||||
<form action="index.php" method="get">
|
||||
|
||||
<?php
|
||||
echo '<input type="hidden" name="_step" value="' . ($RCI->configured ? 3 : 2) . '" />';
|
||||
?>
|
||||
|
||||
<h3>Checking PHP version</h3>
|
||||
<?php
|
||||
|
||||
define('MIN_PHP_VERSION', '7.3.0');
|
||||
if (version_compare(PHP_VERSION, MIN_PHP_VERSION, '>=')) {
|
||||
$RCI->pass('Version', 'PHP ' . PHP_VERSION . ' detected');
|
||||
}
|
||||
else {
|
||||
$RCI->fail('Version', 'PHP Version ' . MIN_PHP_VERSION . ' or greater is required ' . PHP_VERSION . ' detected');
|
||||
}
|
||||
?>
|
||||
|
||||
<h3>Checking PHP extensions</h3>
|
||||
<p class="hint">The following modules/extensions are <em>required</em> to run Roundcube:</p>
|
||||
<?php
|
||||
|
||||
// get extensions location
|
||||
$ext_dir = ini_get('extension_dir');
|
||||
|
||||
$prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
|
||||
foreach ($required_php_exts as $name => $ext) {
|
||||
if (extension_loaded($ext)) {
|
||||
$RCI->pass($name);
|
||||
}
|
||||
else {
|
||||
$_ext = $ext_dir . '/' . $prefix . $ext . '.' . PHP_SHLIB_SUFFIX;
|
||||
$msg = @is_readable($_ext) ? 'Could be loaded. Please add in php.ini' : '';
|
||||
$RCI->fail($name, $msg, $source_urls[$name]);
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<p class="hint">The next couple of extensions are <em>optional</em> and recommended to get the best performance:</p>
|
||||
<?php
|
||||
|
||||
foreach ($optional_php_exts as $name => $ext) {
|
||||
if (extension_loaded($ext)) {
|
||||
$RCI->pass($name);
|
||||
}
|
||||
else {
|
||||
$_ext = $ext_dir . '/' . $prefix . $ext . '.' . PHP_SHLIB_SUFFIX;
|
||||
$msg = @is_readable($_ext) ? 'Could be loaded. Please add in php.ini' : '';
|
||||
$RCI->na($name, $msg, $source_urls[$name]);
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Checking available databases</h3>
|
||||
<p class="hint">Check which of the supported extensions are installed. At least one of them is required.</p>
|
||||
|
||||
<?php
|
||||
|
||||
$prefix = PHP_SHLIB_SUFFIX === 'dll' ? 'php_' : '';
|
||||
foreach ($RCI->supported_dbs as $database => $ext) {
|
||||
if (extension_loaded($ext)) {
|
||||
$RCI->pass($database);
|
||||
$found_db_driver = true;
|
||||
}
|
||||
else {
|
||||
$_ext = $ext_dir . '/' . $prefix . $ext . '.' . PHP_SHLIB_SUFFIX;
|
||||
$msg = @is_readable($_ext) ? 'Could be loaded. Please add in php.ini' : '';
|
||||
$RCI->na($database, $msg, $source_urls[$ext]);
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
if (empty($found_db_driver)) {
|
||||
$RCI->failures++;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Check for required 3rd party libs</h3>
|
||||
<p class="hint">This also checks if the include path is set correctly.</p>
|
||||
|
||||
<?php
|
||||
|
||||
foreach ($required_libs as $classname => $vendor) {
|
||||
if (class_exists($classname)) {
|
||||
$RCI->pass($classname);
|
||||
}
|
||||
else {
|
||||
$RCI->fail($classname, "Failed to load class $classname from $vendor", $source_urls[$classname]);
|
||||
}
|
||||
echo "<br />";
|
||||
}
|
||||
|
||||
foreach ($optional_libs as $classname => $vendor) {
|
||||
if (class_exists($classname)) {
|
||||
$RCI->pass($classname);
|
||||
}
|
||||
else {
|
||||
$RCI->na($classname, "Recommended to install $classname from $vendor", $source_urls[$classname]);
|
||||
}
|
||||
echo "<br />";
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Checking php.ini/.htaccess settings</h3>
|
||||
<p class="hint">The following settings are <em>required</em> to run Roundcube:</p>
|
||||
|
||||
<?php
|
||||
|
||||
foreach ($ini_checks as $var => $val) {
|
||||
$status = ini_get($var);
|
||||
if ($val === '-NOTEMPTY-') {
|
||||
if (empty($status)) {
|
||||
$RCI->fail($var, "empty value detected");
|
||||
}
|
||||
else {
|
||||
$RCI->pass($var);
|
||||
}
|
||||
}
|
||||
else if (filter_var($status, FILTER_VALIDATE_BOOLEAN) == $val) {
|
||||
$RCI->pass($var);
|
||||
}
|
||||
else {
|
||||
$RCI->fail($var, "is '$status', should be '$val'");
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
?>
|
||||
|
||||
<p class="hint">The following settings are <em>optional</em> and recommended:</p>
|
||||
|
||||
<?php
|
||||
|
||||
foreach ($optional_checks as $var => $val) {
|
||||
$status = ini_get($var);
|
||||
if ($val === '-NOTEMPTY-') {
|
||||
if (empty($status)) {
|
||||
$RCI->optfail($var, "Could be set");
|
||||
}
|
||||
else {
|
||||
$RCI->pass($var);
|
||||
}
|
||||
echo '<br />';
|
||||
continue;
|
||||
}
|
||||
if ($val === '-VALID-') {
|
||||
if ($var == 'date.timezone') {
|
||||
try {
|
||||
$tz = new DateTimeZone($status);
|
||||
$RCI->pass($var);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$RCI->optfail($var, empty($status) ? "not set" : "invalid value detected: $status");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->pass($var);
|
||||
}
|
||||
}
|
||||
else if (filter_var($status, FILTER_VALIDATE_BOOLEAN) == $val) {
|
||||
$RCI->pass($var);
|
||||
}
|
||||
else {
|
||||
$RCI->optfail($var, "is '$status', could be '$val'");
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
|
||||
if ($RCI->failures) {
|
||||
echo '<p class="warning">Sorry but your webserver does not meet the requirements for Roundcube!<br />
|
||||
Please install the missing modules or fix the php.ini settings according to the above check results.<br />
|
||||
Hint: only checks showing <span class="fail">NOT OK</span> need to be fixed.</p>';
|
||||
}
|
||||
echo '<p><br /><input type="submit" value="NEXT" ' . ($RCI->failures ? 'disabled' : '') . ' /></p>';
|
||||
|
||||
?>
|
||||
|
||||
</form>
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| Roundcube installer client function |
|
||||
| |
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
function toggleblock(id, link)
|
||||
{
|
||||
var block = document.getElementById(id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function addhostfield()
|
||||
{
|
||||
var container = document.getElementById('defaulthostlist');
|
||||
var row = document.createElement('div');
|
||||
var input = document.createElement('input');
|
||||
var link = document.createElement('a');
|
||||
|
||||
input.name = '_imap_host[]';
|
||||
input.size = '30';
|
||||
link.href = '#';
|
||||
link.onclick = function() { removehostfield(this.parentNode); return false };
|
||||
link.className = 'removelink';
|
||||
link.innerHTML = 'remove';
|
||||
|
||||
row.appendChild(input);
|
||||
row.appendChild(link);
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
|
||||
function removehostfield(row)
|
||||
{
|
||||
var container = document.getElementById('defaulthostlist');
|
||||
container.removeChild(row);
|
||||
}
|
||||
@@ -0,0 +1,647 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
if (!class_exists('rcmail_install', false) || !isset($RCI)) {
|
||||
die("Not allowed! Please open installer/index.php instead.");
|
||||
}
|
||||
|
||||
// allow the current user to get to the next step
|
||||
$_SESSION['allowinstaller'] = true;
|
||||
|
||||
if (!empty($_POST['submit'])) {
|
||||
$_SESSION['config'] = $RCI->create_config();
|
||||
|
||||
if ($RCI->save_configfile($_SESSION['config'])) {
|
||||
echo '<p class="notice">The config file was saved successfully into'
|
||||
. ' <tt>'.RCMAIL_CONFIG_DIR.'</tt> directory of your Roundcube installation.';
|
||||
|
||||
if ($RCI->legacy_config) {
|
||||
echo '<br/><br/>Afterwards, please <b>remove</b> the old configuration files'
|
||||
. ' <tt>main.inc.php</tt> and <tt>db.inc.php</tt> from the config directory.';
|
||||
}
|
||||
|
||||
echo '</p>';
|
||||
}
|
||||
else {
|
||||
$save_button = '';
|
||||
if (($dir = sys_get_temp_dir()) && @is_writable($dir)) {
|
||||
echo '<iframe name="getconfig" style="display:none"></iframe>';
|
||||
echo '<form id="getconfig_form" action="index.php" method="get" target="getconfig" style="display:none">';
|
||||
echo '<input name="_getconfig" value="2" /></form>';
|
||||
|
||||
$button_txt = html::quote('Save in ' . $dir);
|
||||
$save_button = ' <input type="button" onclick="document.getElementById(\'getconfig_form\').submit()" value="' . $button_txt . '" />';
|
||||
}
|
||||
|
||||
echo '<p class="notice">Copy or download the following configuration and save it';
|
||||
echo ' as <tt><b>config.inc.php</b></tt> within the <tt>'.RCUBE_CONFIG_DIR.'</tt> directory of your Roundcube installation.<br/>';
|
||||
echo ' Make sure that there are no characters before the <tt><?php</tt> bracket when saving the file.';
|
||||
echo ' <input type="button" onclick="location.href=\'index.php?_getconfig=1\'" value="Download" />';
|
||||
echo $save_button;
|
||||
|
||||
if ($RCI->legacy_config) {
|
||||
echo '<br/><br/>Afterwards, please <b>remove</b> the old configuration files'
|
||||
. ' <tt>main.inc.php</tt> and <tt>db.inc.php</tt> from the config directory.';
|
||||
}
|
||||
|
||||
echo '</p>';
|
||||
|
||||
$textbox = new html_textarea(['rows' => 16, 'cols' => 60, 'class' => 'configfile']);
|
||||
echo $textbox->show(($_SESSION['config']));
|
||||
}
|
||||
|
||||
echo '<p class="hint">Of course there are more options to configure.
|
||||
Have a look at the defaults.inc.php file or visit <a href="https://github.com/roundcube/roundcubemail/wiki/Configuration" target="_blank">Howto_Config</a> to find out.</p>';
|
||||
|
||||
echo '<p><input type="button" onclick="location.href=\'./index.php?_step=3\'" value="CONTINUE" /></p>';
|
||||
|
||||
// echo '<style type="text/css"> .configblock { display:none } </style>';
|
||||
echo "\n<hr style='margin-bottom:1.6em' />\n";
|
||||
}
|
||||
|
||||
?>
|
||||
<form action="index.php" method="post">
|
||||
<input type="hidden" name="_step" value="2" />
|
||||
|
||||
<fieldset>
|
||||
<legend>General configuration</legend>
|
||||
<dl class="configblock">
|
||||
|
||||
<dt class="propname">product_name</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_prodname = new html_inputfield(['name' => '_product_name', 'size' => 30, 'id' => 'cfgprodname']);
|
||||
echo $input_prodname->show($RCI->getprop('product_name'));
|
||||
|
||||
?>
|
||||
<div>The name of your service (used to compose page titles)</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">support_url</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_support = new html_inputfield(['name' => '_support_url', 'size' => 50, 'id' => 'cfgsupporturl']);
|
||||
echo $input_support->show($RCI->getprop('support_url'));
|
||||
|
||||
?>
|
||||
<div>Provide a URL where a user can get support for this Roundcube installation.<br/>PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!</div>
|
||||
<p class="hint">Enter an absolute URL (including http://) to a support page/form or a mailto: link.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">temp_dir</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_tempdir = new html_inputfield(['name' => '_temp_dir', 'size' => 30, 'id' => 'cfgtempdir']);
|
||||
echo $input_tempdir->show($RCI->getprop('temp_dir'));
|
||||
|
||||
?>
|
||||
<div>Use this folder to store temp files (must be writeable for webserver)</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">des_key</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_deskey = new html_inputfield(['name' => '_des_key', 'size' => 30, 'id' => 'cfgdeskey']);
|
||||
echo $input_deskey->show($RCI->getprop('des_key'));
|
||||
|
||||
?>
|
||||
<div>This key is used to encrypt the users imap password before storing in the session record</div>
|
||||
<p class="hint">It's a random generated string to ensure that every installation has its own key.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">ip_check</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$check_ipcheck = new html_checkbox(['name' => '_ip_check', 'id' => 'cfgipcheck']);
|
||||
echo $check_ipcheck->show(intval($RCI->getprop('ip_check')), array('value' => 1));
|
||||
|
||||
?>
|
||||
<label for="cfgipcheck">Check client IP in session authorization</label><br />
|
||||
|
||||
<p class="hint">This increases security but can cause sudden logouts when someone uses a proxy with changing IPs.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">enable_spellcheck</dt>
|
||||
<dd>
|
||||
<?php
|
||||
$check_spell = new html_checkbox(['name' => '_enable_spellcheck', 'id' => 'cfgspellcheck']);
|
||||
echo $check_spell->show(intval($RCI->getprop('enable_spellcheck')), ['value' => 1]);
|
||||
?>
|
||||
|
||||
<label for="cfgspellcheck">Make use of the spell checker</label><br />
|
||||
</dd>
|
||||
|
||||
<dt class="propname">spellcheck_engine</dt>
|
||||
<dd>
|
||||
|
||||
<?php
|
||||
$select_spell = new html_select(['name' => '_spellcheck_engine', 'id' => 'cfgspellcheckengine']);
|
||||
if (extension_loaded('pspell')) {
|
||||
$select_spell->add('Pspell', 'pspell');
|
||||
}
|
||||
if (extension_loaded('enchant')) {
|
||||
$select_spell->add('Enchant', 'enchant');
|
||||
}
|
||||
$select_spell->add('Googie', 'googie');
|
||||
$select_spell->add('ATD', 'atd');
|
||||
|
||||
echo $select_spell->show($RCI->is_post ? $_POST['_spellcheck_engine'] : 'pspell');
|
||||
?>
|
||||
|
||||
<div>Which spell checker to use</div>
|
||||
<p class="hint">Googie implies that the message content will be sent to external server to check the spelling.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">identities_level</dt>
|
||||
<dd>
|
||||
|
||||
<?php
|
||||
|
||||
$input_ilevel = new html_select(['name' => '_identities_level', 'id' => 'cfgidentitieslevel']);
|
||||
$input_ilevel->add('many identities with possibility to edit all params', 0);
|
||||
$input_ilevel->add('many identities with possibility to edit all params but not email address', 1);
|
||||
$input_ilevel->add('one identity with possibility to edit all params', 2);
|
||||
$input_ilevel->add('one identity with possibility to edit all params but not email address', 3);
|
||||
$input_ilevel->add('one identity with possibility to edit only signature', 4);
|
||||
echo $input_ilevel->show($RCI->getprop('identities_level'), 0);
|
||||
|
||||
?>
|
||||
|
||||
<div>Level of identities access</div>
|
||||
<p class="hint">Defines what users can do with their identities.</p>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Logging & Debugging</legend>
|
||||
<dl class="loggingblock">
|
||||
|
||||
<dt class="propname">log_driver</dt>
|
||||
<dd>
|
||||
|
||||
<?php
|
||||
$select_log_driver = new html_select(['name' => '_log_driver', 'id' => 'cfglogdriver']);
|
||||
$select_log_driver->add(['file', 'syslog', 'stdout'], ['file', 'syslog', 'stdout']);
|
||||
echo $select_log_driver->show($RCI->getprop('log_driver', 'file'));
|
||||
?>
|
||||
|
||||
<div>How to do logging? 'file' - write to files in the log directory, 'syslog' - use the syslog facility, 'stdout' writes to the process' STDOUT file descriptor.</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">log_dir</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_logdir = new html_inputfield(['name' => '_log_dir', 'size' => 30, 'id' => 'cfglogdir']);
|
||||
echo $input_logdir->show($RCI->getprop('log_dir'));
|
||||
|
||||
?>
|
||||
<div>Use this folder to store log files (must be writeable for webserver). Note that this only applies if you are using the 'file' log_driver.</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">syslog_id</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_syslogid = new html_inputfield(['name' => '_syslog_id', 'size' => 30, 'id' => 'cfgsyslogid']);
|
||||
echo $input_syslogid->show($RCI->getprop('syslog_id', 'roundcube'));
|
||||
|
||||
?>
|
||||
<div>What ID to use when logging with syslog. Note that this only applies if you are using the 'syslog' log_driver.</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">syslog_facility</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_syslogfacility = new html_select(['name' => '_syslog_facility', 'id' => 'cfgsyslogfacility']);
|
||||
$input_syslogfacility->add('user-level messages', LOG_USER);
|
||||
if (defined('LOG_MAIL')) {
|
||||
$input_syslogfacility->add('mail subsystem', LOG_MAIL);
|
||||
}
|
||||
if (defined('LOG_LOCAL0')) {
|
||||
$input_syslogfacility->add('local level 0', LOG_LOCAL0);
|
||||
$input_syslogfacility->add('local level 1', LOG_LOCAL1);
|
||||
$input_syslogfacility->add('local level 2', LOG_LOCAL2);
|
||||
$input_syslogfacility->add('local level 3', LOG_LOCAL3);
|
||||
$input_syslogfacility->add('local level 4', LOG_LOCAL4);
|
||||
$input_syslogfacility->add('local level 5', LOG_LOCAL5);
|
||||
$input_syslogfacility->add('local level 6', LOG_LOCAL6);
|
||||
$input_syslogfacility->add('local level 7', LOG_LOCAL7);
|
||||
}
|
||||
echo $input_syslogfacility->show($RCI->getprop('syslog_facility'), LOG_USER);
|
||||
|
||||
?>
|
||||
<div>What ID to use when logging with syslog. Note that this only applies if you are using the 'syslog' log_driver.</div>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Database setup</legend>
|
||||
<dl class="configblock" id="cgfblockdb">
|
||||
<dt class="propname">db_dsnw</dt>
|
||||
<dd>
|
||||
<p>Database settings for read/write operations:</p>
|
||||
<?php
|
||||
|
||||
$select_dbtype = new html_select(['name' => '_dbtype', 'id' => 'cfgdbtype']);
|
||||
foreach ($RCI->supported_dbs as $database => $ext) {
|
||||
if (extension_loaded($ext)) {
|
||||
$select_dbtype->add($database, substr($ext, 4));
|
||||
}
|
||||
}
|
||||
|
||||
$input_dbhost = new html_inputfield(['name' => '_dbhost', 'size' => 20, 'id' => 'cfgdbhos']);
|
||||
$input_dbname = new html_inputfield(['name' => '_dbname', 'size' => 20, 'id' => 'cfgdbname']);
|
||||
$input_dbuser = new html_inputfield(['name' => '_dbuser', 'size' => 20, 'id' => 'cfgdbuser']);
|
||||
$input_dbpass = new html_inputfield(['name' => '_dbpass', 'size' => 20, 'id' => 'cfgdbpass']);
|
||||
|
||||
$dsnw = rcube_db::parse_dsn($RCI->getprop('db_dsnw'));
|
||||
|
||||
echo $select_dbtype->show($RCI->is_post ? $_POST['_dbtype'] : ($dsnw['phptype'] ?? ''));
|
||||
echo '<label for="cfgdbtype">Database type</label><br />';
|
||||
echo $input_dbhost->show($RCI->is_post ? $_POST['_dbhost'] : ($dsnw['hostspec'] ?? ''));
|
||||
echo '<label for="cfgdbhost">Database server (omit for sqlite)</label><br />';
|
||||
echo $input_dbname->show($RCI->is_post ? $_POST['_dbname'] : ($dsnw['database'] ?? ''));
|
||||
echo '<label for="cfgdbname">Database name (use absolute path and filename for sqlite)</label><br />';
|
||||
echo $input_dbuser->show($RCI->is_post ? $_POST['_dbuser'] : ($dsnw['username'] ?? ''));
|
||||
echo '<label for="cfgdbuser">Database user name (needs write permissions) (omit for sqlite)</label><br />';
|
||||
echo $input_dbpass->show($RCI->is_post ? $_POST['_dbpass'] : ($dsnw['password'] ?? ''));
|
||||
echo '<label for="cfgdbpass">Database password (omit for sqlite)</label><br />';
|
||||
|
||||
?>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">db_prefix</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_prefix = new html_inputfield(['name' => '_db_prefix', 'size' => 20, 'id' => 'cfgdbprefix']);
|
||||
echo $input_prefix->show($RCI->getprop('db_prefix'));
|
||||
|
||||
?>
|
||||
<div>Optional prefix that will be added to database object names (tables and sequences).</div>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<legend>IMAP Settings</legend>
|
||||
<dl class="configblock" id="cgfblockimap">
|
||||
<dt class="propname">imap_host</dt>
|
||||
<dd>
|
||||
<div id="defaulthostlist">
|
||||
<?php
|
||||
|
||||
$text_imaphost = new html_inputfield(['name' => '_imap_host[]', 'size' => 30]);
|
||||
$default_hosts = $RCI->get_hostlist();
|
||||
|
||||
if (empty($default_hosts)) {
|
||||
$default_hosts = [''];
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($default_hosts as $host) {
|
||||
echo '<div id="defaulthostentry'.$i.'">' . $text_imaphost->show($host);
|
||||
if ($i++ > 0) {
|
||||
echo '<a href="#" onclick="removehostfield(this.parentNode);return false" class="removelink" title="Remove this entry">remove</a>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
?>
|
||||
</div>
|
||||
<div><a href="javascript:addhostfield()" class="addlink" title="Add another field">add</a></div>
|
||||
|
||||
<div>The IMAP host(s) chosen to perform the log-in</div>
|
||||
<p class="hint">Leave blank to show a textbox at login. To use SSL/STARTTLS connection add ssl:// or tls:// prefix. It can also contain the port number, e.g. tls://imap.domain.tld:143.
|
||||
</dd>
|
||||
|
||||
<dt class="propname">username_domain</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_userdomain = new html_inputfield(['name' => '_username_domain', 'size' => 30, 'id' => 'cfguserdomain']);
|
||||
echo $text_userdomain->show($RCI->getprop('username_domain'));
|
||||
|
||||
?>
|
||||
<div>Automatically add this domain to user names for login</div>
|
||||
|
||||
<p class="hint">Only for IMAP servers that require full e-mail addresses for login</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">auto_create_user</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$check_autocreate = new html_checkbox(['name' => '_auto_create_user', 'id' => 'cfgautocreate']);
|
||||
echo $check_autocreate->show(intval($RCI->getprop('auto_create_user')), ['value' => 1]);
|
||||
|
||||
?>
|
||||
<label for="cfgautocreate">Automatically create a new Roundcube user when log-in the first time</label><br />
|
||||
|
||||
<p class="hint">A user is authenticated by the IMAP server but it requires a local record to store settings
|
||||
and contacts. With this option enabled a new user record will automatically be created once the IMAP login succeeds.</p>
|
||||
|
||||
<p class="hint">If this option is disabled, the login only succeeds if there's a matching user-record in the local Roundcube database
|
||||
what means that you have to create those records manually or disable this option after the first login.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">sent_mbox</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_sentmbox = new html_inputfield(['name' => '_sent_mbox', 'size' => 20, 'id' => 'cfgsentmbox']);
|
||||
echo $text_sentmbox->show($RCI->getprop('sent_mbox'));
|
||||
|
||||
?>
|
||||
<div>Store sent messages in this folder</div>
|
||||
|
||||
<p class="hint">Leave blank if sent messages should not be stored. Note: folder must include namespace prefix if any.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">trash_mbox</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_trashmbox = new html_inputfield(['name' => '_trash_mbox', 'size' => 20, 'id' => 'cfgtrashmbox']);
|
||||
echo $text_trashmbox->show($RCI->getprop('trash_mbox'));
|
||||
|
||||
?>
|
||||
<div>Move messages to this folder when deleting them</div>
|
||||
|
||||
<p class="hint">Leave blank if they should be deleted directly. Note: folder must include namespace prefix if any.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">drafts_mbox</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_draftsmbox = new html_inputfield(['name' => '_drafts_mbox', 'size' => 20, 'id' => 'cfgdraftsmbox']);
|
||||
echo $text_draftsmbox->show($RCI->getprop('drafts_mbox'));
|
||||
|
||||
?>
|
||||
<div>Store draft messages in this folder</div>
|
||||
|
||||
<p class="hint">Leave blank if they should not be stored. Note: folder must include namespace prefix if any.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">junk_mbox</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_junkmbox = new html_inputfield(['name' => '_junk_mbox', 'size' => 20, 'id' => 'cfgjunkmbox']);
|
||||
echo $text_junkmbox->show($RCI->getprop('junk_mbox'));
|
||||
|
||||
?>
|
||||
<div>Store spam messages in this folder</div>
|
||||
|
||||
<p class="hint">Note: folder must include namespace prefix if any.</p>
|
||||
</dd>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>SMTP Settings</legend>
|
||||
<dl class="configblock" id="cgfblocksmtp">
|
||||
<dt class="propname">smtp_host</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_smtphost = new html_inputfield(['name' => '_smtp_host', 'size' => 30, 'id' => 'cfgsmtphost']);
|
||||
echo $text_smtphost->show($RCI->getprop('smtp_host', 'localhost:587'));
|
||||
|
||||
?>
|
||||
<div>Use this host for sending mails</div>
|
||||
<p class="hint">To use SSL/STARTTLS connection add ssl:// or tls:// prefix. It can also contain the port number, e.g. tls://smtp.domain.tld:587.</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">smtp_user/smtp_pass</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$text_smtpuser = new html_inputfield(['name' => '_smtp_user', 'size' => 20, 'id' => 'cfgsmtpuser']);
|
||||
$text_smtppass = new html_inputfield(['name' => '_smtp_pass', 'size' => 20, 'id' => 'cfgsmtppass']);
|
||||
echo $text_smtpuser->show($RCI->getprop('smtp_user'));
|
||||
echo $text_smtppass->show($RCI->getprop('smtp_pass'));
|
||||
|
||||
?>
|
||||
<div>SMTP username and password (if required)</div>
|
||||
<p>
|
||||
<?php
|
||||
|
||||
$check_smtpuser = new html_checkbox(['name' => '_smtp_user_u', 'id' => 'cfgsmtpuseru']);
|
||||
echo $check_smtpuser->show($RCI->getprop('smtp_user') == '%u' || !empty($_POST['_smtp_user_u']) ? 1 : 0, ['value' => 1]);
|
||||
|
||||
?>
|
||||
<label for="cfgsmtpuseru">Use the current IMAP username and password for SMTP authentication</label>
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">smtp_log</dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$check_smtplog = new html_checkbox(['name' => '_smtp_log', 'id' => 'cfgsmtplog']);
|
||||
echo $check_smtplog->show(intval($RCI->getprop('smtp_log')), ['value' => 1]);
|
||||
|
||||
?>
|
||||
<label for="cfgsmtplog">Log sent messages in <tt>{log_dir}/sendmail</tt> or to syslog.</label><br />
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<fieldset>
|
||||
<legend>Display settings & user prefs</legend>
|
||||
<dl class="configblock" id="cgfblockdisplay">
|
||||
|
||||
<dt class="propname">language <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_locale = new html_inputfield(['name' => '_language', 'size' => 6, 'id' => 'cfglocale']);
|
||||
echo $input_locale->show($RCI->getprop('language'));
|
||||
|
||||
?>
|
||||
<div>The default locale setting. This also defines the language of the login screen.<br/>Leave it empty to auto-detect the user agent language.</div>
|
||||
<p class="hint">Enter a <a href="http://www.faqs.org/rfcs/rfc1766">RFC1766</a> formatted language name. Examples: en_US, de_DE, de_CH, fr_FR, pt_BR</p>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">skin <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$input_skin = new html_select(['name' => '_skin', 'id' => 'cfgskin']);
|
||||
$skins = $RCI->list_skins();
|
||||
$input_skin->add($skins, $skins);
|
||||
echo $input_skin->show($RCI->getprop('skin'));
|
||||
|
||||
?>
|
||||
<div>Name of interface skin (folder in /skins)</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">mail_pagesize <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$pagesize = $RCI->getprop('mail_pagesize');
|
||||
if (!$pagesize) {
|
||||
$pagesize = $RCI->getprop('pagesize');
|
||||
}
|
||||
$input_pagesize = new html_inputfield(['name' => '_mail_pagesize', 'size' => 6, 'id' => 'cfgmailpagesize']);
|
||||
echo $input_pagesize->show($pagesize);
|
||||
|
||||
?>
|
||||
<div>Show up to X items in the mail messages list view.</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">addressbook_pagesize <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$pagesize = $RCI->getprop('addressbook_pagesize');
|
||||
if (!$pagesize) {
|
||||
$pagesize = $RCI->getprop('pagesize');
|
||||
}
|
||||
$input_pagesize = new html_inputfield(['name' => '_addressbook_pagesize', 'size' => 6, 'id' => 'cfgabookpagesize']);
|
||||
echo $input_pagesize->show($pagesize);
|
||||
|
||||
?>
|
||||
<div>Show up to X items in the contacts list view.</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">prefer_html <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$check_htmlview = new html_checkbox(['name' => '_prefer_html', 'id' => 'cfghtmlview', 'value' => 1]);
|
||||
echo $check_htmlview->show(intval($RCI->getprop('prefer_html')));
|
||||
|
||||
?>
|
||||
<label for="cfghtmlview">Prefer displaying HTML messages</label><br />
|
||||
</dd>
|
||||
|
||||
<dt class="propname">htmleditor <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<label for="cfghtmlcompose">Compose HTML formatted messages</label>
|
||||
<?php
|
||||
|
||||
$select_htmlcomp = new html_select(['name' => '_htmleditor', 'id' => 'cfghtmlcompose']);
|
||||
$select_htmlcomp->add('never', 0);
|
||||
$select_htmlcomp->add('always', 1);
|
||||
$select_htmlcomp->add('on reply to HTML message only', 2);
|
||||
echo $select_htmlcomp->show(intval($RCI->getprop('htmleditor')));
|
||||
|
||||
?>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">draft_autosave <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<label for="cfgautosave">Save compose message every</label>
|
||||
<?php
|
||||
|
||||
$select_autosave = new html_select(['name' => '_draft_autosave', 'id' => 'cfgautosave']);
|
||||
$select_autosave->add('never', 0);
|
||||
foreach ([1, 3, 5, 10] as $i => $min) {
|
||||
$select_autosave->add("$min min", $min * 60);
|
||||
}
|
||||
|
||||
echo $select_autosave->show(intval($RCI->getprop('draft_autosave')));
|
||||
|
||||
?>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">mdn_requests <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$mdn_opts = [
|
||||
0 => 'ask the user',
|
||||
1 => 'send automatically',
|
||||
3 => 'send receipt to user contacts, otherwise ask the user',
|
||||
4 => 'send receipt to user contacts, otherwise ignore',
|
||||
2 => 'ignore',
|
||||
];
|
||||
|
||||
$select_mdnreq = new html_select(['name' => '_mdn_requests', 'id' => 'cfgmdnreq']);
|
||||
$select_mdnreq->add(array_values($mdn_opts), array_keys($mdn_opts));
|
||||
echo $select_mdnreq->show(intval($RCI->getprop('mdn_requests')));
|
||||
|
||||
?>
|
||||
<div>Behavior if a received message requests a message delivery notification (read receipt)</div>
|
||||
</dd>
|
||||
|
||||
<dt class="propname">mime_param_folding <span class="userconf">*</span></dt>
|
||||
<dd>
|
||||
<?php
|
||||
|
||||
$select_param_folding = new html_select(['name' => '_mime_param_folding', 'id' => 'cfgmimeparamfolding']);
|
||||
$select_param_folding->add('Full RFC 2231 (Roundcube, Thunderbird)', '0');
|
||||
$select_param_folding->add('RFC 2047/2231 (MS Outlook, OE)', '1');
|
||||
$select_param_folding->add('Full RFC 2047 (deprecated)', '2');
|
||||
|
||||
echo $select_param_folding->show(strval($RCI->getprop('mime_param_folding')));
|
||||
|
||||
?>
|
||||
<div>How to encode attachment long/non-ascii names</div>
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<p class="hint"><span class="userconf">*</span> These settings are defaults for the user preferences</p>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Plugins</legend>
|
||||
<dl class="configblock" id="cgfblockdisplay">
|
||||
|
||||
<?php
|
||||
$plugins = $RCI->list_plugins();
|
||||
foreach ($plugins as $p) {
|
||||
$p_check = new html_checkbox(['name' => '_plugins_'.$p['name'], 'id' => 'cfgplugin_'.$p['name'], 'value' => $p['name']]);
|
||||
echo '<dt class="propname"><label>';
|
||||
echo $p_check->show($p['enabled'] ? $p['name'] : 0);
|
||||
echo ' ' . $p['name'] . '</label></dt><dd>';
|
||||
echo '<label for="cfgplugin_'.$p['name'].'" class="hint">' . $p['desc'] . '</label><br/></dd>';
|
||||
}
|
||||
|
||||
?>
|
||||
</dl>
|
||||
|
||||
<p class="hint">Please consider checking dependencies of enabled plugins</p>
|
||||
</fieldset>
|
||||
|
||||
<?php
|
||||
|
||||
echo '<p><input type="submit" name="submit" value="' . ($RCI->configured ? 'UPDATE' : 'CREATE') . ' CONFIG" ' . ($RCI->failures ? 'disabled' : '') . ' /></p>';
|
||||
|
||||
?>
|
||||
</form>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 653 B |
Binary file not shown.
|
After Width: | Height: | Size: 506 B |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 637 B |
Binary file not shown.
|
After Width: | Height: | Size: 565 B |
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-------------------------------------------------------------------------+
|
||||
| Roundcube Webmail setup tool |
|
||||
| Version 1.6-git |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| This program is free software: you can redistribute it and/or modify |
|
||||
| it under the terms of the GNU General Public License (with exceptions |
|
||||
| for skins & plugins) as published by the Free Software Foundation, |
|
||||
| either version 3 of the License, or (at your option) any later version. |
|
||||
| |
|
||||
| This file forms part of the Roundcube Webmail Software for which the |
|
||||
| following exception is added: Plugins and Skins which merely make |
|
||||
| function calls to the Roundcube Webmail Software, and for that purpose |
|
||||
| include it by reference shall not be considered modifications of |
|
||||
| the software. |
|
||||
| |
|
||||
| If you wish to use this file in another project or create a modified |
|
||||
| version that will not be part of the Roundcube Webmail Software, you |
|
||||
| may remove the exception above and use this source code under the |
|
||||
| original version of the license. |
|
||||
| |
|
||||
| This program is distributed in the hope that it will be useful, |
|
||||
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||
| GNU General Public License for more details. |
|
||||
| |
|
||||
| You should have received a copy of the GNU General Public License |
|
||||
| along with this program. If not, see http://www.gnu.org/licenses/. |
|
||||
| |
|
||||
+-------------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/../').'/');
|
||||
|
||||
require INSTALL_PATH . 'program/include/iniset.php';
|
||||
|
||||
if (function_exists('session_start')) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
$RCI = rcmail_install::get_instance();
|
||||
$RCI->load_config();
|
||||
|
||||
if (isset($_GET['_getconfig'])) {
|
||||
$filename = 'config.inc.php';
|
||||
if (!empty($_SESSION['config']) && $_GET['_getconfig'] == 2) {
|
||||
$path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $filename;
|
||||
@unlink($path);
|
||||
file_put_contents($path, $_SESSION['config']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['config'])) {
|
||||
header('Content-type: text/plain');
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
echo $_SESSION['config'];
|
||||
exit;
|
||||
}
|
||||
|
||||
header('HTTP/1.0 404 Not found');
|
||||
die("The requested configuration was not found. Please run the installer from the beginning.");
|
||||
}
|
||||
|
||||
if (
|
||||
$RCI->configured
|
||||
&& !empty($_GET['_mergeconfig'])
|
||||
&& ($RCI->getprop('enable_installer') || !empty($_SESSION['allowinstaller']))
|
||||
) {
|
||||
$filename = 'config.inc.php';
|
||||
|
||||
header('Content-type: text/plain');
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
|
||||
$RCI->merge_config();
|
||||
echo $RCI->create_config();
|
||||
exit;
|
||||
}
|
||||
|
||||
// go to 'check env' step if we have a local configuration
|
||||
if ($RCI->configured && empty($_REQUEST['_step'])) {
|
||||
header("Location: ./?_step=1");
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>Roundcube Webmail Installer</title>
|
||||
<meta name="Robots" content="noindex,nofollow" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<link rel="stylesheet" type="text/css" href="styles.css" />
|
||||
<script type="text/javascript" src="client.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="banner">
|
||||
<div class="banner-bg"></div>
|
||||
<div class="banner-logo"><a href="http://roundcube.net"><img src="images/roundcube_logo.png" width="210" height="55" border="0" alt="Roundcube - open source webmail software" /></a></div>
|
||||
</div>
|
||||
|
||||
<div id="topnav">
|
||||
<a href="https://github.com/roundcube/roundcubemail/wiki/Installation">How-to Wiki</a>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
||||
<?php
|
||||
|
||||
// exit if installation is complete
|
||||
if ($RCI->configured && !$RCI->getprop('enable_installer') && empty($_SESSION['allowinstaller'])) {
|
||||
// header("HTTP/1.0 404 Not Found");
|
||||
if ($RCI->configured && $RCI->legacy_config) {
|
||||
echo '<h2 class="error">Your configuration needs to be migrated!</h2>';
|
||||
echo '<p>We changed the configuration files structure and your installation needs to be updated accordingly.</p>';
|
||||
echo '<p>Please run the <tt>bin/update.sh</tt> script from the command line or set <p> <tt>$rcube_config[\'enable_installer\'] = true;</tt></p>';
|
||||
echo ' in your RCUBE_CONFIG_DIR/main.inc.php to let the installer help you migrating it.</p>';
|
||||
}
|
||||
else {
|
||||
echo '<h2 class="error">The installer is disabled!</h2>';
|
||||
echo '<p>To enable it again, set <tt>$config[\'enable_installer\'] = true;</tt> in RCUBE_CONFIG_DIR/config.inc.php</p>';
|
||||
}
|
||||
|
||||
echo '</div></body></html>';
|
||||
exit;
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h1>Roundcube Webmail Installer</h1>
|
||||
|
||||
<ol id="progress">
|
||||
<?php
|
||||
$include_steps = [
|
||||
1 => './check.php',
|
||||
2 => './config.php',
|
||||
3 => './test.php',
|
||||
];
|
||||
|
||||
if (!in_array($RCI->step, array_keys($include_steps))) {
|
||||
$RCI->step = 1;
|
||||
}
|
||||
|
||||
foreach (['Check environment', 'Create config', 'Test config'] as $i => $item) {
|
||||
$j = $i + 1;
|
||||
$link = ($RCI->step >= $j || $RCI->configured) ? '<a href="./index.php?_step='.$j.'">' . rcube::Q($item) . '</a>' : rcube::Q($item);
|
||||
printf('<li class="step%d%s">%s</li>', $j+1, $RCI->step > $j ? ' passed' : ($RCI->step == $j ? ' current' : ''), $link);
|
||||
}
|
||||
?>
|
||||
</ol>
|
||||
|
||||
<?php
|
||||
|
||||
include $include_steps[$RCI->step];
|
||||
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div id="footer">
|
||||
Installer by the Roundcube Dev Team. Copyright © 2008-2021 – Published under the GNU Public License;
|
||||
Icons by <a href="http://famfamfam.com">famfamfam</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,240 @@
|
||||
body {
|
||||
background: white;
|
||||
font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: small;
|
||||
color: black;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#banner {
|
||||
position: relative;
|
||||
height: 58px;
|
||||
margin: 0 0 1em 0;
|
||||
padding: 10px 20px;
|
||||
background: url('images/banner_gradient.gif') top left repeat-x #d8edfd;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#banner .banner-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 630px;
|
||||
height: 78px;
|
||||
background: url('images/banner_schraffur.gif') top right no-repeat;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#banner .banner-logo {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 20px;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
#banner .banner-logo a {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
#topnav {
|
||||
position: absolute;
|
||||
top: 3.6em;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
#topnav a {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 2em 20px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin: 2em 20px 1em 20px;
|
||||
padding-top: 0.6em;
|
||||
font-size: smaller;
|
||||
text-align: center;
|
||||
border-top: 1px dotted #999;
|
||||
}
|
||||
|
||||
#progress {
|
||||
margin-bottom: 2em;
|
||||
border: 1px solid #aaa;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
#progress:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
font-size: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#progress li {
|
||||
float: left;
|
||||
color: #999;
|
||||
padding: 1em 5em 1em 0.2em;
|
||||
}
|
||||
|
||||
#progress li a {
|
||||
color: #999;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#progress li a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#progress li.current {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#progress li.passed,
|
||||
#progress li.passed a,
|
||||
#progress li.current a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin-bottom: 1.5em;
|
||||
border: 1px solid #aaa;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
fieldset p.hint {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
legend {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
textarea.configfile {
|
||||
background-color: #f9f9f9;
|
||||
font-family: monospace;
|
||||
font-size: 9pt;
|
||||
width: 100%;
|
||||
height: 30em;
|
||||
}
|
||||
|
||||
.propname {
|
||||
font-family: monospace;
|
||||
font-size: 9pt;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.6em;
|
||||
}
|
||||
|
||||
dd div {
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
dd label {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td > label {
|
||||
min-width: 6em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
ul li {
|
||||
margin: 0.3em 0 0.4em -1em;
|
||||
}
|
||||
|
||||
ul li ul li {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.1em;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.6em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
a.blocktoggle {
|
||||
color: #666;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.addlink {
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
padding: 1px 0 1px 20px;
|
||||
background: url('images/add.png') top left no-repeat;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.removelink {
|
||||
color: #999;
|
||||
font-size: 0.9em;
|
||||
padding: 1px 0 1px 24px;
|
||||
background: url('images/delete.png') 4px 0 no-repeat;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #666;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #006400;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: #ff0000 !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.na {
|
||||
color: #f60;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.indent {
|
||||
padding-left: 0.8em;
|
||||
}
|
||||
|
||||
.notice {
|
||||
padding: 1em;
|
||||
background-color: #f7fdcb;
|
||||
border: 2px solid #c2d071;
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
padding: 0.6em;
|
||||
background-color: #ebebeb;
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
p.warning,
|
||||
div.warning {
|
||||
padding: 1em;
|
||||
background-color: #ef9398;
|
||||
border: 2px solid #dc5757;
|
||||
}
|
||||
|
||||
h3.warning {
|
||||
color: #c00;
|
||||
background: url('images/error.png') top left no-repeat;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.userconf {
|
||||
color: #00c;
|
||||
font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
<?php
|
||||
/*
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
if (!class_exists('rcmail_install', false) || !isset($RCI)) {
|
||||
die("Not allowed! Please open installer/index.php instead.");
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Check config file</h3>
|
||||
<?php
|
||||
|
||||
if ($read_config = is_readable(RCUBE_CONFIG_DIR . 'defaults.inc.php')) {
|
||||
$config = $RCI->load_config_file(RCUBE_CONFIG_DIR . 'defaults.inc.php');
|
||||
if (!empty($config)) {
|
||||
$RCI->pass('defaults.inc.php');
|
||||
}
|
||||
else {
|
||||
$RCI->fail('defaults.inc.php', 'Syntax error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->fail('defaults.inc.php', 'Unable to read default config file?');
|
||||
}
|
||||
|
||||
echo '<br />';
|
||||
|
||||
if ($read_config = is_readable(RCUBE_CONFIG_DIR . 'config.inc.php')) {
|
||||
$config = $RCI->load_config_file(RCUBE_CONFIG_DIR . 'config.inc.php');
|
||||
if (!empty($config)) {
|
||||
$RCI->pass('config.inc.php');
|
||||
}
|
||||
else {
|
||||
$RCI->fail('config.inc.php', 'Syntax error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->fail('config.inc.php', 'Unable to read file. Did you create the config file?');
|
||||
}
|
||||
|
||||
echo '<br />';
|
||||
|
||||
if ($RCI->configured && ($messages = $RCI->check_config())) {
|
||||
if (is_array($messages['replaced'])) {
|
||||
echo '<h3 class="warning">Replaced config options</h3>';
|
||||
echo '<p class="hint">The following config options have been replaced or renamed. ';
|
||||
echo 'Please update them accordingly in your config files.</p>';
|
||||
|
||||
echo '<ul class="configwarnings">';
|
||||
foreach ($messages['replaced'] as $msg) {
|
||||
echo html::tag('li', null, html::span('propname', $msg['prop']) .
|
||||
' was replaced by ' . html::span('propname', $msg['replacement']));
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
if (is_array($messages['obsolete'])) {
|
||||
echo '<h3>Obsolete config options</h3>';
|
||||
echo '<p class="hint">You still have some obsolete or inexistent properties set. This isn\'t a problem but should be noticed.</p>';
|
||||
|
||||
echo '<ul class="configwarnings">';
|
||||
foreach ($messages['obsolete'] as $msg) {
|
||||
echo html::tag('li', null, html::span('propname', $msg['prop'])
|
||||
. (!empty($msg['explain']) ? ': ' . $msg['explain'] : ''));
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
|
||||
echo '<p class="suggestion">OK, lazy people can download the updated config file here: ';
|
||||
echo html::a(['href' => './?_mergeconfig=1'], 'config.inc.php') . ' ';
|
||||
echo "</p>";
|
||||
|
||||
if (is_array($messages['dependencies'])) {
|
||||
echo '<h3 class="warning">Dependency check failed</h3>';
|
||||
echo '<p class="hint">Some of your configuration settings require other options to be configured or additional PHP modules to be installed</p>';
|
||||
|
||||
echo '<ul class="configwarnings">';
|
||||
foreach ($messages['dependencies'] as $msg) {
|
||||
echo html::tag('li', null, html::span('propname', $msg['prop']) . ': ' . $msg['explain']);
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Check if directories are writable</h3>
|
||||
<p>Roundcube may need to write/save files into these directories</p>
|
||||
<?php
|
||||
|
||||
$dirs[] = !empty($RCI->config['temp_dir']) ? $RCI->config['temp_dir'] : 'temp';
|
||||
if ($RCI->config['log_driver'] != 'syslog') {
|
||||
$dirs[] = $RCI->config['log_dir'] ? $RCI->config['log_dir'] : 'logs';
|
||||
}
|
||||
|
||||
foreach ($dirs as $dir) {
|
||||
$dirpath = rcube_utils::is_absolute_path($dir) ? $dir : INSTALL_PATH . $dir;
|
||||
if (is_writable(realpath($dirpath))) {
|
||||
$RCI->pass($dir);
|
||||
$pass = true;
|
||||
}
|
||||
else {
|
||||
$RCI->fail($dir, 'not writeable for the webserver');
|
||||
}
|
||||
echo '<br />';
|
||||
}
|
||||
|
||||
if (empty($pass)) {
|
||||
echo '<p class="hint">Use <tt>chmod</tt> or <tt>chown</tt> to grant write privileges to the webserver</p>';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Check DB config</h3>
|
||||
<?php
|
||||
|
||||
$db_working = false;
|
||||
if ($RCI->configured) {
|
||||
if (!empty($RCI->config['db_dsnw'])) {
|
||||
$DB = rcube_db::factory($RCI->config['db_dsnw'], '', false);
|
||||
$DB->set_debug((bool)$RCI->config['sql_debug']);
|
||||
$DB->db_connect('w');
|
||||
|
||||
if (!($db_error_msg = $DB->is_error())) {
|
||||
$RCI->pass('DSN (write)');
|
||||
echo '<br />';
|
||||
$db_working = true;
|
||||
}
|
||||
else {
|
||||
$RCI->fail('DSN (write)', $db_error_msg);
|
||||
echo '<p class="hint">Make sure that the configured database exists and that the user has write privileges<br />';
|
||||
echo 'DSN: ' . rcube::Q($RCI->config['db_dsnw']) . '</p>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->fail('DSN (write)', 'not set');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->fail('DSN (write)', 'Could not read config file');
|
||||
}
|
||||
|
||||
// initialize db with schema found in /SQL/*
|
||||
if ($db_working && !empty($_POST['initdb'])) {
|
||||
if (!$RCI->init_db($DB)) {
|
||||
$db_working = false;
|
||||
echo '<p class="warning">Please try to initialize the database manually as described in the INSTALL guide.
|
||||
Make sure that the configured database exists and that the user as write privileges</p>';
|
||||
}
|
||||
}
|
||||
else if ($db_working && !empty($_POST['updatedb'])) {
|
||||
if (!$RCI->update_db($_POST['version'])) {
|
||||
echo '<p class="warning">Database schema update failed.</p>';
|
||||
}
|
||||
}
|
||||
|
||||
// test database
|
||||
if ($db_working) {
|
||||
$db_read = $DB->query("SELECT count(*) FROM " . $DB->quote_identifier($RCI->config['db_prefix'] . 'users'));
|
||||
if ($DB->is_error()) {
|
||||
$RCI->fail('DB Schema', "Database not initialized");
|
||||
echo '<form action="index.php?_step=3" method="post">'
|
||||
. '<p><input type="submit" name="initdb" value="Initialize database" /></p>'
|
||||
. '</form>';
|
||||
|
||||
$db_working = false;
|
||||
}
|
||||
else if ($err = $RCI->db_schema_check($DB, $update = !empty($_POST['updatedb']))) {
|
||||
$RCI->fail('DB Schema', "Database schema differs");
|
||||
echo '<ul style="margin:0"><li>' . join("</li>\n<li>", $err) . "</li></ul>";
|
||||
|
||||
$select = $RCI->versions_select(['name' => 'version']);
|
||||
$select->add('0.9 or newer', '');
|
||||
|
||||
echo '<form action="index.php?_step=3" method="post">'
|
||||
. '<p class="suggestion">You should run the update queries to get the schema fixed.'
|
||||
. '<br/><br/>Version to update from: ' . $select->show('')
|
||||
. ' <input type="submit" name="updatedb" value="Update" /></p>'
|
||||
. '</form>';
|
||||
|
||||
$db_working = false;
|
||||
}
|
||||
else {
|
||||
$RCI->pass('DB Schema');
|
||||
echo '<br />';
|
||||
}
|
||||
}
|
||||
|
||||
// more database tests
|
||||
if ($db_working) {
|
||||
// Using transactions to workaround SQLite bug (#7064)
|
||||
if ($DB->db_provider == 'sqlite') {
|
||||
$DB->startTransaction();
|
||||
}
|
||||
|
||||
// write test
|
||||
$insert_id = md5(uniqid());
|
||||
$db_write = $DB->query("INSERT INTO " . $DB->quote_identifier($RCI->config['db_prefix'] . 'session')
|
||||
. " (`sess_id`, `changed`, `ip`, `vars`) VALUES (?, ".$DB->now().", '127.0.0.1', 'foo')", $insert_id);
|
||||
|
||||
if ($db_write) {
|
||||
$RCI->pass('DB Write');
|
||||
$DB->query("DELETE FROM " . $DB->quote_identifier($RCI->config['db_prefix'] . 'session')
|
||||
. " WHERE `sess_id` = ?", $insert_id);
|
||||
}
|
||||
else {
|
||||
$RCI->fail('DB Write', $RCI->get_error());
|
||||
}
|
||||
echo '<br />';
|
||||
|
||||
// Transaction end
|
||||
if ($DB->db_provider == 'sqlite') {
|
||||
$DB->rollbackTransaction();
|
||||
}
|
||||
|
||||
// check timezone settings
|
||||
$tz_db = 'SELECT ' . $DB->unixtimestamp($DB->now()) . ' AS tz_db';
|
||||
$tz_db = $DB->query($tz_db);
|
||||
$tz_db = $DB->fetch_assoc($tz_db);
|
||||
$tz_db = (int) $tz_db['tz_db'];
|
||||
$tz_local = (int) time();
|
||||
$tz_diff = $tz_local - $tz_db;
|
||||
|
||||
// sometimes db and web servers are on separate hosts, so allow a 30 minutes delta
|
||||
if (abs($tz_diff) > 1800) {
|
||||
$RCI->fail('DB Time', "Database time differs {$tz_diff}s from PHP time");
|
||||
}
|
||||
else {
|
||||
$RCI->pass('DB Time');
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<h3>Test filetype detection</h3>
|
||||
|
||||
<?php
|
||||
|
||||
if ($errors = $RCI->check_mime_detection()) {
|
||||
$RCI->fail('Fileinfo/mime_content_type configuration');
|
||||
if (!empty($RCI->config['mime_magic'])) {
|
||||
echo '<p class="hint">Try setting the <tt>mime_magic</tt> config option to <tt>null</tt>.</p>';
|
||||
}
|
||||
else {
|
||||
echo '<p class="hint">Check the <a href="http://www.php.net/manual/en/function.finfo-open.php">Fileinfo functions</a> of your PHP installation.<br/>';
|
||||
echo 'The path to the magic.mime file can be set using the <tt>mime_magic</tt> config option in Roundcube.</p>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->pass('Fileinfo/mime_content_type configuration');
|
||||
echo "<br/>";
|
||||
}
|
||||
|
||||
|
||||
if ($errors = $RCI->check_mime_extensions()) {
|
||||
$RCI->fail('Mimetype to file extension mapping');
|
||||
echo '<p class="hint">Please set a valid path to your webserver\'s mime.types file to the <tt>mime_types</tt> config option.<br/>';
|
||||
echo 'If you can\'t find such a file, download it from <a href="http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types">svn.apache.org</a>.</p>';
|
||||
}
|
||||
else {
|
||||
$RCI->pass('Mimetype to file extension mapping');
|
||||
echo "<br/>";
|
||||
}
|
||||
|
||||
$smtp_hosts = $RCI->get_hostlist('smtp_host');
|
||||
if (!empty($smtp_hosts)) {
|
||||
$smtp_host_field = new html_select(['name' => '_smtp_host', 'id' => 'smtp_host']);
|
||||
$smtp_host_field->add($smtp_hosts, $smtp_hosts);
|
||||
}
|
||||
else {
|
||||
$smtp_host_field = new html_inputfield(['name' => '_smtp_host', 'id' => 'smtp_host']);
|
||||
}
|
||||
|
||||
$user = $RCI->getprop('smtp_user', '(none)');
|
||||
$pass = $RCI->getprop('smtp_pass', '(none)');
|
||||
|
||||
if ($user == '%u') {
|
||||
$user_field = new html_inputfield(['name' => '_smtp_user', 'id' => 'smtp_user']);
|
||||
$user = $user_field->show(isset($_POST['_smtp_user']) ? $_POST['_smtp_user'] : '');
|
||||
}
|
||||
else {
|
||||
$user = html::quote($user);
|
||||
}
|
||||
if ($pass == '%p') {
|
||||
$pass_field = new html_passwordfield(['name' => '_smtp_pass', 'id' => 'smtp_pass']);
|
||||
$pass = $pass_field->show();
|
||||
}
|
||||
else {
|
||||
$pass = html::quote($pass);
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<form action="index.php?_step=3" method="post">
|
||||
|
||||
<h3>Test SMTP config</h3>
|
||||
|
||||
<p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="smtp_host">Host</label></td>
|
||||
<td><?php echo $smtp_host_field->show(isset($_POST['_smtp_host']) ? $_POST['_smtp_host'] : ''); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="smtp_user">Username</label></td>
|
||||
<td><?php echo $user; ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="smtp_pass">Password</label></td>
|
||||
<td><?php echo $pass; ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
|
||||
$from_field = new html_inputfield(['name' => '_from', 'id' => 'sendmailfrom']);
|
||||
$to_field = new html_inputfield(['name' => '_to', 'id' => 'sendmailto']);
|
||||
|
||||
if (isset($_POST['sendmail'])) {
|
||||
|
||||
echo '<p>Trying to send email...<br />';
|
||||
|
||||
$smtp_host = trim($_POST['_smtp_host']);
|
||||
|
||||
$from = rcube_utils::idn_to_ascii(trim($_POST['_from']));
|
||||
$to = rcube_utils::idn_to_ascii(trim($_POST['_to']));
|
||||
|
||||
if (
|
||||
preg_match('/^' . $RCI->email_pattern . '$/i', $from)
|
||||
&& preg_match('/^' . $RCI->email_pattern . '$/i', $to)
|
||||
) {
|
||||
$headers = [
|
||||
'From' => $from,
|
||||
'To' => $to,
|
||||
'Subject' => 'Test message from Roundcube',
|
||||
];
|
||||
|
||||
$body = 'This is a test to confirm that Roundcube can send email.';
|
||||
|
||||
// send mail using configured SMTP server
|
||||
$CONFIG = $RCI->config;
|
||||
|
||||
if (!empty($_POST['_smtp_user'])) {
|
||||
$CONFIG['smtp_user'] = $_POST['_smtp_user'];
|
||||
}
|
||||
if (!empty($_POST['_smtp_pass'])) {
|
||||
$CONFIG['smtp_pass'] = $_POST['_smtp_pass'];
|
||||
}
|
||||
|
||||
$mail_object = new Mail_mime();
|
||||
$send_headers = $mail_object->headers($headers);
|
||||
$head = $mail_object->txtHeaders($send_headers);
|
||||
|
||||
$SMTP = new rcube_smtp();
|
||||
$SMTP->connect($smtp_host, null, $CONFIG['smtp_user'], $CONFIG['smtp_pass']);
|
||||
|
||||
$status = $SMTP->send_mail($headers['From'], $headers['To'], $head, $body);
|
||||
$smtp_response = $SMTP->get_response();
|
||||
|
||||
if ($status) {
|
||||
$RCI->pass('SMTP send');
|
||||
}
|
||||
else {
|
||||
$RCI->fail('SMTP send', join('; ', $smtp_response));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$RCI->fail('SMTP send', 'Invalid sender or recipient');
|
||||
}
|
||||
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="sendmailfrom">Sender</label></td>
|
||||
<td><?php echo $from_field->show(isset($_POST['_from']) ? $_POST['_from'] : ''); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="sendmailto">Recipient</label></td>
|
||||
<td><?php echo $to_field->show(isset($_POST['_to']) ? $_POST['_to'] : ''); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><input type="submit" name="sendmail" value="Send test mail" /></p>
|
||||
|
||||
</form>
|
||||
|
||||
<form action="index.php?_step=3" method="post">
|
||||
|
||||
<h3>Test IMAP config</h3>
|
||||
|
||||
<?php
|
||||
|
||||
$default_hosts = $RCI->get_hostlist();
|
||||
if (!empty($default_hosts)) {
|
||||
$host_field = new html_select(['name' => '_host', 'id' => 'imaphost']);
|
||||
$host_field->add($default_hosts, $default_hosts);
|
||||
}
|
||||
else {
|
||||
$host_field = new html_inputfield(['name' => '_host', 'id' => 'imaphost']);
|
||||
}
|
||||
|
||||
$user_field = new html_inputfield(['name' => '_user', 'id' => 'imapuser']);
|
||||
$pass_field = new html_passwordfield(['name' => '_pass', 'id' => 'imappass']);
|
||||
|
||||
?>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><label for="imaphost">Host</label></td>
|
||||
<td><?php echo $host_field->show(isset($_POST['_host']) ? $_POST['_host'] : ''); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="imapuser">Username</label></td>
|
||||
<td><?php echo $user_field->show(isset($_POST['_user']) ? $_POST['_user'] : ''); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="imappass">Password</label></td>
|
||||
<td><?php echo $pass_field->show(); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
|
||||
if (isset($_POST['imaptest']) && !empty($_POST['_host']) && !empty($_POST['_user'])) {
|
||||
|
||||
echo '<p>Connecting to ' . rcube::Q($_POST['_host']) . '...<br />';
|
||||
|
||||
$imap_host = trim($_POST['_host']);
|
||||
$imap_port = 143;
|
||||
$imap_ssl = false;
|
||||
|
||||
$a_host = parse_url($imap_host);
|
||||
if ($a_host['host']) {
|
||||
$imap_host = $a_host['host'];
|
||||
$imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], ['ssl','imaps','tls'])) ? $a_host['scheme'] : null;
|
||||
$imap_port = $a_host['port'] ?? ($imap_ssl && $imap_ssl != 'tls' ? 993 : 143);
|
||||
}
|
||||
|
||||
$imap_host = rcube_utils::idn_to_ascii($imap_host);
|
||||
$imap_user = rcube_utils::idn_to_ascii($_POST['_user']);
|
||||
|
||||
$imap = new rcube_imap;
|
||||
$imap->set_options([
|
||||
'auth_type' => $RCI->getprop('imap_auth_type'),
|
||||
'debug' => $RCI->getprop('imap_debug'),
|
||||
'socket_options' => $RCI->getprop('imap_conn_options'),
|
||||
]);
|
||||
|
||||
if ($imap->connect($imap_host, $imap_user, $_POST['_pass'], $imap_port, $imap_ssl)) {
|
||||
$RCI->pass('IMAP connect', 'SORT capability: ' . ($imap->get_capability('SORT') ? 'yes' : 'no'));
|
||||
$imap->close();
|
||||
}
|
||||
else {
|
||||
$RCI->fail('IMAP connect', $RCI->get_error());
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<p><input type="submit" name="imaptest" value="Check login" /></p>
|
||||
|
||||
</form>
|
||||
|
||||
<hr />
|
||||
|
||||
<p class="warning">
|
||||
|
||||
After completing the installation and the final tests please <b>remove</b> the whole
|
||||
installer folder from the document root of the webserver or make sure that
|
||||
<tt>enable_installer</tt> option in <tt>config.inc.php</tt> is disabled.<br />
|
||||
<br />
|
||||
|
||||
These files may expose sensitive configuration data like server passwords and encryption keys
|
||||
to the public. Make sure you cannot access this installer from your browser.
|
||||
|
||||
</p>
|
||||
@@ -0,0 +1,7 @@
|
||||
# deny webserver access to this directory
|
||||
<ifModule mod_authz_core.c>
|
||||
Require all denied
|
||||
</ifModule>
|
||||
<ifModule !mod_authz_core.c>
|
||||
Deny from all
|
||||
</ifModule>
|
||||
@@ -0,0 +1,11 @@
|
||||
[02-Oct-2023 18:04:08 +0000]: <13n3u2rh> DB Error: SQLSTATE[HY000] [1045] Accès refusé pour l'utilisateur: 'roundcube'@'@localhost' (mot de passe: NON) in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_db.php on line 201 (GET /ruty/mails/)
|
||||
[02-Oct-2023 18:24:21 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:24:41 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh@gmail.com against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:33:55 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:44:41 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:45:00 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:45:14 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:45:41 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 18:48:29 +0000]: <13n3u2rh> IMAP Error: Login failed for whykioh@gmail.com against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 19:02:57 +0000]: <13n3u2rh> IMAP Error: Login failed for root against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
[02-Oct-2023 19:11:53 +0000]: <13n3u2rh> IMAP Error: Login failed for whykorp@gmail.com against localhost from 192.168.0.254. Could not connect to localhost:143: Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée in C:\wamp64\www\ruty\mails\program\lib\Roundcube\rcube_imap.php on line 211 (POST /ruty/mails/?_task=login&_action=login)
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Copy a contact record from one directory to another |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_copy extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$cids = self::get_cids();
|
||||
$target = rcube_utils::get_input_string('_to', rcube_utils::INPUT_POST);
|
||||
$target_group = rcube_utils::get_input_string('_togid', rcube_utils::INPUT_POST);
|
||||
|
||||
$success = 0;
|
||||
$errormsg = 'copyerror';
|
||||
$maxnum = $rcmail->config->get('max_group_members', 0);
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
// Something wrong, target not specified
|
||||
if (!strlen($target)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// It might happen when copying records from search result
|
||||
// Do nothing, go to next source
|
||||
if ((string) $target == (string) $source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($source);
|
||||
$TARGET = $rcmail->get_address_book($target);
|
||||
|
||||
if (!$TARGET || !$TARGET->ready || $TARGET->readonly) {
|
||||
break;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($cid as $cid) {
|
||||
$a_record = $CONTACTS->get_record($cid, true);
|
||||
|
||||
// avoid copying groups
|
||||
if (isset($a_record['_type']) && $a_record['_type'] == 'group') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if contact exists, if so, we'll need it's ID
|
||||
// Note: Some addressbooks allows empty email address field
|
||||
// @TODO: should we check all email addresses?
|
||||
$email = $CONTACTS->get_col_values('email', $a_record, true);
|
||||
if (!empty($email)) {
|
||||
$result = $TARGET->search('email', $email[0], 1, true, true);
|
||||
}
|
||||
else if (!empty($a_record['name'])) {
|
||||
$result = $TARGET->search('name', $a_record['name'], 1, true, true);
|
||||
}
|
||||
else {
|
||||
$result = new rcube_result_set();
|
||||
}
|
||||
|
||||
// insert contact record
|
||||
if (!$result->count) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $a_record,
|
||||
'source' => $target,
|
||||
'group' => $target_group
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
if ($insert_id = $TARGET->insert($plugin['record'], false)) {
|
||||
$ids[] = $insert_id;
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else if ($plugin['result']) {
|
||||
$ids = array_merge($ids, $plugin['result']);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$record = $result->first();
|
||||
$ids[] = $record['ID'];
|
||||
$errormsg = empty($email) ? 'contactnameexists' : 'contactexists';
|
||||
}
|
||||
}
|
||||
|
||||
// assign to group
|
||||
if ($target_group && $TARGET->groups && !empty($ids)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $target_group,
|
||||
'ids' => $ids,
|
||||
'source' => $target
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$TARGET->reset();
|
||||
$TARGET->set_group($target_group);
|
||||
|
||||
if ($maxnum && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) {
|
||||
$success = $cnt;
|
||||
}
|
||||
}
|
||||
else if (!empty($plugin['result'])) {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
$errormsg = !empty($plugin['message']) ? $plugin['message'] : 'copyerror';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$rcmail->output->show_message($errormsg, 'error');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('copysuccess', 'confirmation', ['nr' => $success]);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delete the submitted contacts (CIDs) from the users address book |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_delete extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$cids = self::get_cids(null, rcube_utils::INPUT_POST);
|
||||
$delcnt = 0;
|
||||
|
||||
// remove previous deletes
|
||||
$undo_time = $rcmail->config->get('undo_timeout', 0);
|
||||
$rcmail->session->remove('contact_undo');
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
$CONTACTS = self::contact_source($source);
|
||||
|
||||
if ($CONTACTS->readonly && empty($CONTACTS->deletable)) {
|
||||
// more sources? do nothing, probably we have search results from
|
||||
// more than one source, some of these sources can be readonly
|
||||
if (count($cids) == 1) {
|
||||
$rcmail->output->show_message('contactdelerror', 'error');
|
||||
$rcmail->output->command('list_contacts');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_delete', [
|
||||
'id' => $cid,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$deleted = !$plugin['abort'] ? $CONTACTS->delete($cid, $undo_time < 1) : $plugin['result'];
|
||||
|
||||
if (!$deleted) {
|
||||
if (!empty($plugin['message'])) {
|
||||
$error = $plugin['message'];
|
||||
}
|
||||
else if (($error = $CONTACTS->get_error()) && !empty($error['message'])) {
|
||||
$error = $error['message'];
|
||||
}
|
||||
else {
|
||||
$error = 'contactdelerror';
|
||||
}
|
||||
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GP);
|
||||
$group = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GP);
|
||||
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
$rcmail->output->command('list_contacts', $source, $group);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$delcnt += $deleted;
|
||||
|
||||
// store deleted contacts IDs in session for undo action
|
||||
if ($undo_time > 0 && $CONTACTS->undelete) {
|
||||
$_SESSION['contact_undo']['data'][$source] = $cid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_SESSION['contact_undo'])) {
|
||||
$_SESSION['contact_undo']['ts'] = time();
|
||||
$msg = html::span(null, $rcmail->gettext('contactdeleted'))
|
||||
. ' ' . html::a(
|
||||
['onclick' => rcmail_output::JS_OBJECT_NAME.".command('undo', '', this)"],
|
||||
$rcmail->gettext('undo')
|
||||
);
|
||||
|
||||
$rcmail->output->show_message($msg, 'confirmation', null, true, $undo_time);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('contactdeleted', 'confirmation');
|
||||
}
|
||||
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$page = $_SESSION['page'] ?? 1;
|
||||
|
||||
// update saved search after data changed
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
$pages = ceil((count($records) + $delcnt) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
$first += $page_size;
|
||||
// create resultset object
|
||||
$res = new rcube_result_set($count, $first - $delcnt);
|
||||
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first - $delcnt, $delcnt);
|
||||
}
|
||||
|
||||
$res->records = array_values($records);
|
||||
$records = $res;
|
||||
}
|
||||
else {
|
||||
unset($records);
|
||||
}
|
||||
}
|
||||
else if (isset($CONTACTS)) {
|
||||
// count contacts for this user
|
||||
$result = $CONTACTS->count();
|
||||
$pages = ceil(($result->count + $delcnt) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
$CONTACTS->set_page($page);
|
||||
$records = $CONTACTS->list_records(null, -$delcnt);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($rowcount)) {
|
||||
$rowcount = isset($result) ? self::get_rowcount_text($result) : '';
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0);
|
||||
$rcmail->output->command('set_rowcount', $rowcount);
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if (!empty($records)) {
|
||||
self::js_contacts_list($records);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show edit form for a contact entry |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_edit extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($rcmail->action == 'edit') {
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = array_first($cids[$source]);
|
||||
|
||||
// Initialize addressbook
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
|
||||
// Contact edit
|
||||
if ($cid && (self::$contact = $CONTACTS->get_record($cid, true))) {
|
||||
$rcmail->output->set_env('cid', self::$contact['ID']);
|
||||
}
|
||||
|
||||
// editing not allowed here
|
||||
if ($CONTACTS->readonly || !empty(self::$contact['readonly'])) {
|
||||
$rcmail->output->show_message('sourceisreadonly');
|
||||
$rcmail->overwrite_action('show');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty(self::$contact)) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
|
||||
if (strlen($source)) {
|
||||
$CONTACTS = $rcmail->get_address_book($source, true);
|
||||
}
|
||||
|
||||
if (empty($CONTACTS) || $CONTACTS->readonly) {
|
||||
$CONTACTS = $rcmail->get_address_book(rcube_addressbook::TYPE_DEFAULT, true);
|
||||
$source = $rcmail->get_address_book_id($CONTACTS);
|
||||
}
|
||||
|
||||
// Initialize addressbook
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
}
|
||||
|
||||
self::$SOURCE_ID = $source;
|
||||
self::$CONTACTS = $CONTACTS;
|
||||
self::set_sourcename($CONTACTS);
|
||||
|
||||
// check if we have a valid result
|
||||
if (!empty($args['contact'])) {
|
||||
self::$contact = $args['contact'];
|
||||
}
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contactedithead' => [$this, 'contact_edithead'],
|
||||
'contacteditform' => [$this, 'contact_editform'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
'photouploadform' => [$this, 'upload_photo_form'],
|
||||
'sourceselector' => [$this, 'source_selector'],
|
||||
'filedroparea' => [$this, 'photo_drop_area'],
|
||||
]);
|
||||
|
||||
$rcmail->output->set_pagetitle($rcmail->gettext(($rcmail->action == 'add' ? 'addcontact' : 'editcontact')));
|
||||
|
||||
if ($rcmail->action == 'add' && $rcmail->output->template_exists('contactadd')) {
|
||||
$rcmail->output->send('contactadd');
|
||||
}
|
||||
|
||||
// this will be executed if no template for addcontact exists
|
||||
$rcmail->output->send('contactedit');
|
||||
}
|
||||
|
||||
public static function contact_edithead($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$business_mode = $rcmail->config->get('contact_form_mode') === 'business';
|
||||
|
||||
// check if we have a valid result
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 20;
|
||||
|
||||
$form = [
|
||||
'head' => [
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'source' => ['id' => '_source', 'label' => $rcmail->gettext('addressbook')],
|
||||
'prefix' => ['size' => $i_size],
|
||||
'firstname' => ['size' => $i_size, 'visible' => true],
|
||||
'middlename' => ['size' => $i_size],
|
||||
'surname' => ['size' => $i_size, 'visible' => true],
|
||||
'suffix' => ['size' => $i_size],
|
||||
'name' => ['size' => $i_size * 2],
|
||||
'nickname' => ['size' => $i_size * 2],
|
||||
'organization' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
'department' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
'jobtitle' => ['size' => $i_size * 2, 'visible' => $business_mode],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
list($form_start, $form_end) = self::get_form_tags($attrib);
|
||||
unset($attrib['form'], $attrib['name'], $attrib['size']);
|
||||
|
||||
// return the address edit form
|
||||
$out = self::contact_form($form, self::$contact, $attrib);
|
||||
|
||||
return $form_start . $out . $form_end;
|
||||
}
|
||||
|
||||
public static function contact_editform($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$addr_tpl = $rcmail->config->get('address_template', '');
|
||||
|
||||
// copy (parsed) address template to client
|
||||
if (preg_match_all('/\{([a-z0-9]+)\}([^{]*)/i', $addr_tpl, $templ, PREG_SET_ORDER)) {
|
||||
$rcmail->output->set_env('address_template', $templ);
|
||||
}
|
||||
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
|
||||
$t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 10;
|
||||
$t_cols = !empty($attrib['textareacols']) ? $attrib['textareacols'] : 40;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'email' => ['size' => $i_size, 'maxlength' => 254, 'visible' => true],
|
||||
'phone' => ['size' => $i_size, 'visible' => true],
|
||||
'address' => ['visible' => true],
|
||||
'website' => ['size' => $i_size],
|
||||
'im' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [
|
||||
'gender' => ['visible' => true],
|
||||
'maidenname' => ['size' => $i_size],
|
||||
'birthday' => ['visible' => true],
|
||||
'anniversary' => [],
|
||||
'manager' => ['size' => $i_size],
|
||||
'assistant' => ['size' => $i_size],
|
||||
'spouse' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(self::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'single' => true,
|
||||
'content' => [
|
||||
'notes' => ['size' => $t_cols, 'rows' => $t_rows, 'label' => false, 'visible' => true, 'limit' => 1],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
list($form_start, $form_end) = self::get_form_tags($attrib);
|
||||
unset($attrib['form']);
|
||||
|
||||
// return the complete address edit form as table
|
||||
$out = self::contact_form($form, self::$contact, $attrib);
|
||||
|
||||
return $form_start . $out . $form_end;
|
||||
}
|
||||
|
||||
public static function upload_photo_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$hidden = new html_hiddenfield(['name' => '_cid', 'value' => $rcmail->output->get_env('cid')]);
|
||||
|
||||
$attrib['prefix'] = $hidden->show();
|
||||
$input_attr = ['name' => '_photo', 'accept' => 'image/*'];
|
||||
|
||||
$rcmail->output->add_label('addphoto','replacephoto');
|
||||
|
||||
return self::upload_form($attrib, 'uploadform', 'upload-photo', $input_attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* similar function as in /steps/settings/edit_identity.inc
|
||||
* @todo: Use rcmail_action::get_form_tags()
|
||||
*/
|
||||
public static function get_form_tags($attrib, $action = null, $id = null, $hidden = null)
|
||||
{
|
||||
static $edit_form;
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$form_start = $form_end = '';
|
||||
|
||||
if (empty($edit_form)) {
|
||||
$hiddenfields = new html_hiddenfield();
|
||||
|
||||
if ($rcmail->action == 'edit') {
|
||||
$hiddenfields->add(['name' => '_source', 'value' => self::$SOURCE_ID]);
|
||||
}
|
||||
|
||||
$hiddenfields->add(['name' => '_gid', 'value' => self::$CONTACTS->group_id]);
|
||||
$hiddenfields->add(['name' => '_search', 'value' => rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC)]);
|
||||
|
||||
if ($cid = $rcmail->output->get_env('cid')) {
|
||||
$hiddenfields->add(['name' => '_cid', 'value' => $cid]);
|
||||
}
|
||||
|
||||
$form_attrib = [
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'save',
|
||||
'request' => 'save.' . intval($cid),
|
||||
'noclose' => true,
|
||||
];
|
||||
|
||||
$form_start = $rcmail->output->request_form($form_attrib + $attrib, $hiddenfields->show());
|
||||
$form_end = empty($attrib['form']) ? '</form>' : '';
|
||||
$edit_form = !empty($attrib['form']) ? $attrib['form'] : 'form';
|
||||
|
||||
$rcmail->output->add_gui_object('editform', $edit_form);
|
||||
}
|
||||
|
||||
return [$form_start, $form_end];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register container as active area to drop photos onto
|
||||
*/
|
||||
public static function photo_drop_area($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!empty($attrib['id'])) {
|
||||
$rcmail->output->add_gui_object('filedrop', $attrib['id']);
|
||||
$rcmail->output->set_env('filedrop', [
|
||||
'action' => 'upload-photo',
|
||||
'fieldname' => '_photo',
|
||||
'single' => 1,
|
||||
'filter' => '^image/.+'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Export the selected address book as vCard file |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_export extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$rcmail->request_security_check(rcube_utils::INPUT_GET);
|
||||
|
||||
$sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
|
||||
// Use search result
|
||||
if (!empty($_REQUEST['_search']) && isset($_SESSION['contact_search'][$_REQUEST['_search']])) {
|
||||
$search = (array) $_SESSION['contact_search'][$_REQUEST['_search']];
|
||||
$records = [];
|
||||
|
||||
// Get records from all sources
|
||||
foreach ($search as $s => $set) {
|
||||
$source = $rcmail->get_address_book($s);
|
||||
|
||||
// reset page
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(99999);
|
||||
$source->set_search_set($set);
|
||||
|
||||
// get records
|
||||
$result = $source->list_records();
|
||||
|
||||
while ($record = $result->next()) {
|
||||
// because vcard_map is per-source we need to create vcard here
|
||||
self::prepare_for_export($record, $source);
|
||||
|
||||
$record['sourceid'] = $s;
|
||||
$key = rcube_addressbook::compose_contact_key($record, $sort_col);
|
||||
$records[$key] = $record;
|
||||
}
|
||||
|
||||
unset($result);
|
||||
}
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// selected contacts
|
||||
else if (!empty($_REQUEST['_cid'])) {
|
||||
$records = [];
|
||||
|
||||
// Selected contact IDs (with multi-source support)
|
||||
$cids = self::get_cids();
|
||||
|
||||
foreach ($cids as $s => $ids) {
|
||||
$source = $rcmail->get_address_book($s);
|
||||
|
||||
// reset page and page size (#6103)
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(count($ids));
|
||||
|
||||
$result = $source->search('ID', $ids, 1, true, true);
|
||||
|
||||
while ($record = $result->next()) {
|
||||
// because vcard_map is per-source we need to create vcard here
|
||||
self::prepare_for_export($record, $source);
|
||||
|
||||
$record['sourceid'] = $s;
|
||||
$key = rcube_addressbook::compose_contact_key($record, $sort_col);
|
||||
$records[$key] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// selected directory/group
|
||||
else {
|
||||
$CONTACTS = self::contact_source(null, true);
|
||||
|
||||
// get contacts for this user
|
||||
$CONTACTS->set_page(1);
|
||||
$CONTACTS->set_pagesize(99999);
|
||||
$result = $CONTACTS->list_records(null, 0, true);
|
||||
}
|
||||
|
||||
// Give plugins a possibility to implement other output formats or modify the result
|
||||
$plugin = $rcmail->plugins->exec_hook('addressbook_export', ['result' => $result]);
|
||||
$result = $plugin['result'];
|
||||
|
||||
if ($plugin['abort']) {
|
||||
$rcmail->output->sendExit();
|
||||
}
|
||||
|
||||
// send download headers
|
||||
$rcmail->output->header('Content-Type: text/vcard; charset=' . RCUBE_CHARSET);
|
||||
$rcmail->output->header('Content-Disposition: attachment; filename="contacts.vcf"');
|
||||
|
||||
while ($result && ($row = $result->next())) {
|
||||
if (!empty($CONTACTS)) {
|
||||
self::prepare_for_export($row, $CONTACTS);
|
||||
}
|
||||
|
||||
// fix folding and end-of-line chars
|
||||
$row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']);
|
||||
$row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']);
|
||||
|
||||
echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol;
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contact record properties into a vcard object
|
||||
*/
|
||||
public static function prepare_for_export(&$record, $source = null)
|
||||
{
|
||||
$groups = $source && $source->groups && $source->export_groups ? $source->get_record_groups($record['ID']) : null;
|
||||
$fieldmap = $source ? $source->vcard_map : null;
|
||||
|
||||
if (empty($record['vcard'])) {
|
||||
$vcard = new rcube_vcard(null, RCUBE_CHARSET, false, $fieldmap);
|
||||
$vcard->reset();
|
||||
|
||||
foreach ($record as $key => $values) {
|
||||
list($field, $section) = rcube_utils::explode(':', $key);
|
||||
// avoid unwanted casting of DateTime objects to an array
|
||||
// (same as in rcube_contacts::convert_save_data())
|
||||
if (is_object($values) && is_a($values, 'DateTime')) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if (is_array($value) || is_a($value, 'DateTime') || @strlen($value)) {
|
||||
$vcard->set($field, $value, $section ? strtoupper($section) : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append group names
|
||||
if ($groups) {
|
||||
$vcard->set('groups', join(',', $groups), null);
|
||||
}
|
||||
|
||||
$record['vcard'] = $vcard->export();
|
||||
}
|
||||
// patch categories to already existing vcard block
|
||||
else {
|
||||
$vcard = new rcube_vcard($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
|
||||
|
||||
// unset CATEGORIES entry, it might be not up-to-date (#1490277)
|
||||
$vcard->set('groups', null);
|
||||
$record['vcard'] = $vcard->export();
|
||||
|
||||
if (!empty($groups)) {
|
||||
$vgroups = 'CATEGORIES:' . rcube_vcard::vcard_quote($groups, ',') . rcube_vcard::$eol;
|
||||
$record['vcard'] = str_replace('END:VCARD', $vgroups . 'END:VCARD', $record['vcard']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handle adding members to a contact group |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_addmembers extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST);
|
||||
$ids = self::get_cids($source);
|
||||
$result = false;
|
||||
|
||||
if ($gid && $ids) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $gid,
|
||||
'ids' => $ids,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
$contacts->set_group($gid);
|
||||
$num2add = count($plugin['ids']);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
if (
|
||||
($maxnum = $rcmail->config->get('max_group_members'))
|
||||
&& ($contacts->count()->count + $num2add > $maxnum)
|
||||
) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$result = $contacts->add_to_group($gid, $plugin['ids']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$rcmail->output->show_message('contactaddedtogroup', 'confirmation');
|
||||
}
|
||||
else if (!empty($plugin['abort']) || $contacts->get_error()) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
else {
|
||||
$message = !empty($plugin['message']) ? $plugin['message'] : 'nogroupassignmentschanged';
|
||||
$rcmail->output->show_message($message);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact groups creation |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_create extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if ($name = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true))) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_create', [
|
||||
'name' => $name,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$created = $contacts->create_group($plugin['name']);
|
||||
}
|
||||
else {
|
||||
$created = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($created)) {
|
||||
$rcmail->output->show_message('groupcreated', 'confirmation');
|
||||
$rcmail->output->command('insert_contact_group', ['source' => $source] + $created);
|
||||
}
|
||||
else if (empty($created)) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact group delete action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_delete extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if ($gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_delete', [
|
||||
'group_id' => $gid,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$deleted = $contacts->delete_group($gid);
|
||||
}
|
||||
else {
|
||||
$deleted = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($deleted)) {
|
||||
$rcmail->output->show_message('groupdeleted', 'confirmation');
|
||||
$rcmail->output->command('remove_group_item', ['source' => $source, 'id' => $gid]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Removing members from a contact group |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_delmembers extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST);
|
||||
$ids = self::get_cids($source);
|
||||
|
||||
if ($gid && $ids) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_delmembers', [
|
||||
'group_id' => $gid,
|
||||
'ids' => $ids,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $contacts->remove_from_group($gid, $plugin['ids']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
$rcmail->output->show_message('contactremovedfromgroup', 'confirmation');
|
||||
$rcmail->output->command('remove_group_contacts', ['source' => $source, 'gid' => $gid]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| A handler for contact groups rename action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_group_rename extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
if ($contacts->readonly || !$contacts->groups) {
|
||||
$rcmail->output->show_message('sourceisreadonly', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (
|
||||
($gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_POST))
|
||||
&& ($name = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true)))
|
||||
) {
|
||||
$newgid = null;
|
||||
$plugin = $rcmail->plugins->exec_hook('group_rename', [
|
||||
'group_id' => $gid,
|
||||
'name' => $name,
|
||||
'source' => $source,
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$newname = $contacts->rename_group($gid, $plugin['name'], $newgid);
|
||||
}
|
||||
else {
|
||||
$newname = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($newname)) {
|
||||
$rcmail->output->show_message('grouprenamed', 'confirmation');
|
||||
$rcmail->output->command('update_contact_group', [
|
||||
'source' => $source,
|
||||
'id' => $gid,
|
||||
'name' => $newname,
|
||||
'newid' => $newgid ?? null
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'errorsaving';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Import contacts from a vCard or CSV file |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_import extends rcmail_action_contacts_index
|
||||
{
|
||||
const UPLOAD_ERR_CSV_FIELDS = 101;
|
||||
|
||||
protected static $stats;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$importstep = 'import_form';
|
||||
$has_map = isset($_POST['_map']) && is_array($_POST['_map']);
|
||||
|
||||
if ($has_map || (isset($_FILES['_file']) && is_array($_FILES['_file']))) {
|
||||
$replace = (bool) rcube_utils::get_input_string('_replace', rcube_utils::INPUT_GPC);
|
||||
$target = rcube_utils::get_input_string('_target', rcube_utils::INPUT_GPC);
|
||||
$with_groups = (int) rcube_utils::get_input_string('_groups', rcube_utils::INPUT_GPC);
|
||||
|
||||
// reload params for CSV field mapping screen
|
||||
if ($has_map && !empty($_SESSION['contactcsvimport']['params'])) {
|
||||
$params = $_SESSION['contactcsvimport']['params'];
|
||||
|
||||
$replace = $params['replace'];
|
||||
$target = $params['target'];
|
||||
$with_groups = $params['with_groups'];
|
||||
}
|
||||
|
||||
$vcards = [];
|
||||
$csvs = [];
|
||||
$map = [];
|
||||
$upload_error = null;
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($target, true);
|
||||
|
||||
if (!$CONTACTS->groups) {
|
||||
$with_groups = false;
|
||||
}
|
||||
|
||||
if ($CONTACTS->readonly) {
|
||||
$rcmail->output->show_message('addresswriterror', 'error');
|
||||
}
|
||||
else {
|
||||
$filepaths = [];
|
||||
if ($has_map) {
|
||||
$filepaths = $_SESSION['contactcsvimport']['files'];
|
||||
}
|
||||
else {
|
||||
foreach ((array) $_FILES['_file']['tmp_name'] as $i => $filepath) {
|
||||
// Process uploaded file if there is no error
|
||||
$err = $_FILES['_file']['error'][$i];
|
||||
|
||||
if ($err) {
|
||||
$upload_error = $err;
|
||||
}
|
||||
else {
|
||||
$filepaths[] = $filepath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($filepaths as $filepath) {
|
||||
$file_content = file_get_contents($filepath);
|
||||
|
||||
// let rcube_vcard do the hard work :-)
|
||||
$vcard_o = new rcube_vcard();
|
||||
$vcard_o->extend_fieldmap($CONTACTS->vcard_map);
|
||||
$v_list = $vcard_o->import($file_content);
|
||||
|
||||
if (!empty($v_list)) {
|
||||
$vcards = array_merge($vcards, $v_list);
|
||||
continue;
|
||||
}
|
||||
|
||||
// no vCards found, try CSV
|
||||
$csv = new rcube_csv2vcard($_SESSION['language']);
|
||||
|
||||
if ($has_map) {
|
||||
$skip_head = isset($_POST['_skip_header']);
|
||||
$map = rcube_utils::get_input_value('_map', rcube_utils::INPUT_GPC);
|
||||
$map = array_filter($map);
|
||||
|
||||
$csv->set_map($map);
|
||||
$csv->import($file_content, false, $skip_head);
|
||||
|
||||
unlink($filepath);
|
||||
}
|
||||
else {
|
||||
// save uploaded file for the real import in the next step
|
||||
$temp_csv = rcube_utils::temp_filename('csvimpt');
|
||||
if (move_uploaded_file($filepath, $temp_csv) && file_exists($temp_csv)) {
|
||||
$fields = $csv->get_fields();
|
||||
$last_map = $map;
|
||||
$map = $csv->import($file_content, true);
|
||||
|
||||
// when multiple CSV files are uploaded check they all have the same structure
|
||||
if ($last_map && $last_map !== $map) {
|
||||
$csvs = [];
|
||||
$upload_error = self::UPLOAD_ERR_CSV_FIELDS;
|
||||
break;
|
||||
}
|
||||
|
||||
$csvs[] = $temp_csv;
|
||||
}
|
||||
else {
|
||||
$upload_error = UPLOAD_ERR_CANT_WRITE;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$v_list = $csv->export();
|
||||
|
||||
if (!empty($v_list)) {
|
||||
$vcards = array_merge($vcards, $v_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($csvs) > 0) {
|
||||
// csv import, show field mapping options
|
||||
$importstep = 'import_map';
|
||||
|
||||
$_SESSION['contactcsvimport']['files'] = $csvs;
|
||||
$_SESSION['contactcsvimport']['params'] = [
|
||||
'replace' => $replace,
|
||||
'target' => $target,
|
||||
'with_groups' => $with_groups,
|
||||
'fields' => !empty($fields) ? $fields : [],
|
||||
];
|
||||
|
||||
// Stored separately due to nested array limitations in session
|
||||
$_SESSION['contactcsvimport']['map'] = $map;
|
||||
|
||||
// Re-enable the import button
|
||||
$rcmail->output->command('parent.import_state_set', 'error');
|
||||
}
|
||||
elseif (count($vcards) > 0) {
|
||||
// import vcards
|
||||
self::$stats = new stdClass;
|
||||
self::$stats->names = [];
|
||||
self::$stats->skipped_names = [];
|
||||
self::$stats->count = count($vcards);
|
||||
self::$stats->inserted = 0;
|
||||
self::$stats->skipped = 0;
|
||||
self::$stats->invalid = 0;
|
||||
self::$stats->errors = 0;
|
||||
|
||||
if ($replace) {
|
||||
$CONTACTS->delete_all($CONTACTS->groups && $with_groups < 2);
|
||||
}
|
||||
|
||||
if ($with_groups) {
|
||||
$import_groups = $CONTACTS->list_groups();
|
||||
}
|
||||
|
||||
foreach ($vcards as $vcard) {
|
||||
$a_record = $vcard->get_assoc();
|
||||
|
||||
// Generate contact's display name (must be before validation), the same we do in save.inc
|
||||
if (empty($a_record['name'])) {
|
||||
$a_record['name'] = rcube_addressbook::compose_display_name($a_record, true);
|
||||
// Reset it if equals to email address (from compose_display_name())
|
||||
if ($a_record['name'] == ($a_record['email'][0] ?? null)) {
|
||||
$a_record['name'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
// skip invalid (incomplete) entries
|
||||
if (!$CONTACTS->validate($a_record, true)) {
|
||||
self::$stats->invalid++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// We're using UTF8 internally
|
||||
$email = null;
|
||||
if (isset($vcard->email[0])) {
|
||||
$email = $vcard->email[0];
|
||||
$email = rcube_utils::idn_to_utf8($email);
|
||||
}
|
||||
|
||||
if (!$replace) {
|
||||
$existing = null;
|
||||
// compare e-mail address
|
||||
if ($email) {
|
||||
$existing = $CONTACTS->search('email', $email, 1, false);
|
||||
}
|
||||
// compare display name if email not found
|
||||
if ((!$existing || !$existing->count) && $vcard->displayname) {
|
||||
$existing = $CONTACTS->search('name', $vcard->displayname, 1, false);
|
||||
}
|
||||
if ($existing && $existing->count) {
|
||||
self::$stats->skipped++;
|
||||
self::$stats->skipped_names[] = $vcard->displayname ?: $email;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$a_record['vcard'] = $vcard->export();
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', ['record' => $a_record, 'source' => null]);
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
// insert record and send response
|
||||
if (empty($plugin['abort'])) {
|
||||
$success = $CONTACTS->insert($a_record);
|
||||
}
|
||||
else {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
// assign groups for this contact (if enabled)
|
||||
if ($with_groups && !empty($a_record['groups'])) {
|
||||
foreach (explode(',', $a_record['groups'][0]) as $group_name) {
|
||||
if ($group_id = self::import_group_id($group_name, $CONTACTS, $with_groups == 1, $import_groups)) {
|
||||
$CONTACTS->add_to_group($group_id, $success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self::$stats->inserted++;
|
||||
self::$stats->names[] = $a_record['name'] ?: $email;
|
||||
}
|
||||
else {
|
||||
self::$stats->errors++;
|
||||
}
|
||||
}
|
||||
|
||||
$importstep = 'import_confirm';
|
||||
$_SESSION['contactcsvimport'] = null;
|
||||
|
||||
$rcmail->output->command('parent.import_state_set', self::$stats->inserted ? 'reload' : 'ok');
|
||||
}
|
||||
else {
|
||||
if ($upload_error == self::UPLOAD_ERR_CSV_FIELDS) {
|
||||
$rcmail->output->show_message('csvfilemismatch', 'error');
|
||||
}
|
||||
else {
|
||||
self::upload_error($upload_error);
|
||||
}
|
||||
|
||||
$rcmail->output->command('parent.import_state_set', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->set_pagetitle($rcmail->gettext('importcontacts'));
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'importstep' => [$this, $importstep],
|
||||
]);
|
||||
|
||||
// render page
|
||||
if ($rcmail->output->template_exists('contactimport')) {
|
||||
$rcmail->output->send('contactimport');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->send('importcontacts'); // deprecated
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler function to display the import/upload form
|
||||
*/
|
||||
public static function import_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$target = rcube_utils::get_input_string('_target', rcube_utils::INPUT_GPC);
|
||||
|
||||
$attrib += ['id' => 'rcmImportForm'];
|
||||
|
||||
$writable_books = $rcmail->get_address_sources(true, true);
|
||||
$max_filesize = self::upload_init();
|
||||
|
||||
$form = '';
|
||||
$hint = $rcmail->gettext(['id' => 'importfile', 'name' => 'maxuploadsize', 'vars' => ['size' => $max_filesize]]);
|
||||
$table = new html_table(['cols' => 2]);
|
||||
$upload = new html_inputfield([
|
||||
'type' => 'file',
|
||||
'name' => '_file[]',
|
||||
'id' => 'rcmimportfile',
|
||||
'size' => 40,
|
||||
'multiple' => 'multiple',
|
||||
'class' => 'form-control-file',
|
||||
]);
|
||||
|
||||
$table->add('title', html::label('rcmimportfile', $rcmail->gettext('importfromfile')));
|
||||
$table->add(null, $upload->show() . html::div('hint', $hint));
|
||||
|
||||
// addressbook selector
|
||||
if (count($writable_books) > 1) {
|
||||
$select = new html_select([
|
||||
'name' => '_target',
|
||||
'id' => 'rcmimporttarget',
|
||||
'is_escaped' => true,
|
||||
'class' => 'custom-select'
|
||||
]);
|
||||
|
||||
foreach ($writable_books as $book) {
|
||||
$select->add($book['name'], $book['id']);
|
||||
}
|
||||
|
||||
$table->add('title', html::label('rcmimporttarget', $rcmail->gettext('importtarget')));
|
||||
$table->add(null, $select->show($target));
|
||||
}
|
||||
else {
|
||||
$abook = new html_hiddenfield(['name' => '_target', 'value' => key($writable_books)]);
|
||||
$form .= $abook->show();
|
||||
}
|
||||
|
||||
$form .= html::tag('input', ['type' => 'hidden', 'name' => '_unlock', 'value' => '']);
|
||||
|
||||
// selector for group import options
|
||||
if (count($writable_books) >= 1 || $writable_books[0]->groups) {
|
||||
$select = new html_select([
|
||||
'name' => '_groups',
|
||||
'id' => 'rcmimportgroups',
|
||||
'is_escaped' => true,
|
||||
'class' => 'custom-select'
|
||||
]);
|
||||
$select->add($rcmail->gettext('none'), '0');
|
||||
$select->add($rcmail->gettext('importgroupsall'), '1');
|
||||
$select->add($rcmail->gettext('importgroupsexisting'), '2');
|
||||
|
||||
$table->add('title', html::label('rcmimportgroups', $rcmail->gettext('importgroups')));
|
||||
$table->add(null, $select->show(rcube_utils::get_input_value('_groups', rcube_utils::INPUT_GPC)));
|
||||
}
|
||||
|
||||
// checkbox to replace the entire address book
|
||||
$check_replace = new html_checkbox(['name' => '_replace', 'value' => 1, 'id' => 'rcmimportreplace']);
|
||||
$table->add('title', html::label('rcmimportreplace', $rcmail->gettext('importreplace')));
|
||||
$table->add(null, $check_replace->show(rcube_utils::get_input_string('_replace', rcube_utils::INPUT_GPC)));
|
||||
|
||||
$form .= $table->show(['id' => null] + $attrib);
|
||||
|
||||
// remove any info left over info from previous import attempts
|
||||
$_SESSION['contactcsvimport'] = null;
|
||||
|
||||
$rcmail->output->set_env('writable_source', !empty($writable_books));
|
||||
$rcmail->output->add_label('selectimportfile','importwait');
|
||||
$rcmail->output->add_gui_object('importform', $attrib['id']);
|
||||
|
||||
$attrib = [
|
||||
'action' => $rcmail->url('import'),
|
||||
'method' => 'post',
|
||||
'enctype' => 'multipart/form-data'
|
||||
] + $attrib;
|
||||
|
||||
return html::p(null, rcube::Q($rcmail->gettext('importdesc'), 'show'))
|
||||
. $rcmail->output->form_tag($attrib, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the field mapping page for the CSV import process
|
||||
*/
|
||||
public static function import_map($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$params = $_SESSION['contactcsvimport']['params'];
|
||||
|
||||
// hide groups field from list when group import disabled
|
||||
if (empty($params['with_groups'])) {
|
||||
unset($params['fields']['groups']);
|
||||
}
|
||||
|
||||
$fieldlist = new html_select(['name' => '_map[]']);
|
||||
$fieldlist->add($rcmail->gettext('fieldnotmapped'), '');
|
||||
foreach ($params['fields'] as $id => $name) {
|
||||
$fieldlist->add($name, $id);
|
||||
}
|
||||
|
||||
$field_table = new html_table(['cols' => 2] + $attrib);
|
||||
|
||||
if ($classes = $attrib['table-header-class']) {
|
||||
$field_table->set_header_attribs($classes);
|
||||
}
|
||||
|
||||
$field_table->add_header($attrib['table-col-source-class'] ?: null, $rcmail->gettext('source'));
|
||||
$field_table->add_header($attrib['table-col-destination-class'] ?: null, $rcmail->gettext('destination'));
|
||||
|
||||
$map = $_SESSION['contactcsvimport']['map'];
|
||||
foreach ($map['source'] as $i => $name) {
|
||||
$field_table->add('title', html::label('rcmimportmap' . $i, rcube::Q($name)));
|
||||
$field_table->add(null, $fieldlist->show(array_key_exists($i, $map['destination']) ? $map['destination'][$i] : '', ['id' => 'rcmimportmap' . $i]));
|
||||
}
|
||||
|
||||
$form = '';
|
||||
$form .= html::tag('input', ['type' => 'hidden', 'name' => '_unlock', 'value' => '']);
|
||||
|
||||
// show option to import data from first line of the file
|
||||
$check_header = new html_checkbox(['name' => '_skip_header', 'value' => 1, 'id' => 'rcmskipheader']);
|
||||
$form .= html::p(null, html::label('rcmskipheader', $check_header->show(1) . $rcmail->gettext('skipheader')));
|
||||
|
||||
$form .= $field_table->show();
|
||||
|
||||
$attrib = ['action' => $rcmail->url('import'), 'method' => 'post'] + $attrib + ['id' => 'rcmImportFormMap'];
|
||||
|
||||
$rcmail->output->add_gui_object('importformmap', $attrib['id']);
|
||||
|
||||
return html::p(null, rcube::Q($rcmail->gettext('importmapdesc'), 'show'))
|
||||
. $rcmail->output->form_tag($attrib, $form);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the confirmation page for the import process
|
||||
*/
|
||||
public static function import_confirm($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$vars = get_object_vars(self::$stats);
|
||||
$vars['names'] = $vars['skipped_names'] = '';
|
||||
|
||||
$content = html::p(null, $rcmail->gettext([
|
||||
'name' => 'importconfirm',
|
||||
'nr' => self::$stats->inserted,
|
||||
'vars' => $vars,
|
||||
]) . (self::$stats->names ? ':' : '.')
|
||||
);
|
||||
|
||||
if (self::$stats->names) {
|
||||
$content .= html::p('em', join(', ', array_map(['rcube', 'Q'], self::$stats->names)));
|
||||
}
|
||||
|
||||
if (self::$stats->skipped) {
|
||||
$content .= html::p(null, $rcmail->gettext([
|
||||
'name' => 'importconfirmskipped',
|
||||
'nr' => self::$stats->skipped,
|
||||
'vars' => $vars,
|
||||
]) . ':')
|
||||
. html::p('em', join(', ', array_map(['rcube', 'Q'], self::$stats->skipped_names)));
|
||||
}
|
||||
|
||||
return html::div($attrib, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching group id. If group doesn't exist, it'll be created if allowed.
|
||||
*/
|
||||
public static function import_group_id($group_name, $contacts, $create, &$import_groups)
|
||||
{
|
||||
$group_id = 0;
|
||||
foreach ($import_groups as $group) {
|
||||
if (strtolower($group['name']) === strtolower($group_name)) {
|
||||
$group_id = $group['ID'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// create a new group
|
||||
if (!$group_id && $create) {
|
||||
$new_group = $contacts->create_group($group_name);
|
||||
|
||||
if (empty($new_group['ID'])) {
|
||||
$new_group['ID'] = $new_group['id'];
|
||||
}
|
||||
|
||||
$import_groups[] = $new_group;
|
||||
$group_id = $new_group['ID'];
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Send contacts list to client (as remote response) |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_list extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!empty($_GET['_page'])) {
|
||||
$page = intval($_GET['_page']);
|
||||
}
|
||||
else {
|
||||
$page = !empty($_SESSION['page']) ? $_SESSION['page'] : 1;
|
||||
}
|
||||
|
||||
$_SESSION['page'] = $page;
|
||||
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$group_data = null;
|
||||
|
||||
// Use search result
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
|
||||
// we need only records for current page
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// List selected directory
|
||||
else {
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$contacts = self::contact_source(null, true);
|
||||
|
||||
// get contacts for this user
|
||||
$result = $contacts->list_records($afields);
|
||||
|
||||
if (!$result->count && $result->searchonly) {
|
||||
$rcmail->output->show_message('contactsearchonly', 'notice');
|
||||
// Don't invoke advanced search dialog automatically from here (#6679)
|
||||
}
|
||||
|
||||
if (!empty($contacts->group_id)) {
|
||||
$group_data = ['ID' => $contacts->group_id]
|
||||
+ array_intersect_key((array) $contacts->get_group($contacts->group_id), ['name' => 1,'email' => 1]);
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->command('set_group_prop', $group_data);
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', ceil($result->count / $page_size));
|
||||
$rcmail->output->command('set_rowcount', self::get_rowcount_text($result));
|
||||
|
||||
// create javascript list
|
||||
self::js_contacts_list($result);
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Compose a recipient list with all selected contacts |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_mailto extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$cids = self::get_cids();
|
||||
$mailto = [];
|
||||
$sources = [];
|
||||
|
||||
foreach ($cids as $source => $cid) {
|
||||
$contacts = $rcmail->get_address_book($source);
|
||||
|
||||
if ($contacts->ready) {
|
||||
$contacts->set_page(1);
|
||||
$contacts->set_pagesize(count($cid) + 2); // +2 to skip counting query
|
||||
$sources[] = $contacts->search($contacts->primary_key, $cid, 0, true, true, 'email');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_REQUEST['_gid']) && isset($_REQUEST['_source'])) {
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GP);
|
||||
$group_id = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GP);
|
||||
|
||||
$contacts = $rcmail->get_address_book($source);
|
||||
$group_data = $contacts->get_group($group_id);
|
||||
|
||||
// group has an email address assigned: use that
|
||||
if (!empty($group_data['email'])) {
|
||||
$mailto[] = format_email_recipient($group_data['email'][0], $group_data['name']);
|
||||
}
|
||||
else if ($contacts->ready) {
|
||||
$maxnum = (int) $rcmail->config->get('max_group_members');
|
||||
|
||||
$contacts->set_group($group_id);
|
||||
$contacts->set_page(1);
|
||||
$contacts->set_pagesize($maxnum ?: 999);
|
||||
$sources[] = $contacts->list_records();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($sources as $source) {
|
||||
while (is_object($source) && ($rec = $source->iterate())) {
|
||||
$emails = rcube_addressbook::get_col_values('email', $rec, true);
|
||||
|
||||
if (!empty($emails)) {
|
||||
$mailto[] = format_email_recipient($emails[0], $rec['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($mailto)) {
|
||||
$mailto_str = join(', ', $mailto);
|
||||
$mailto_id = substr(md5($mailto_str), 0, 16);
|
||||
$_SESSION['mailto'][$mailto_id] = urlencode($mailto_str);
|
||||
$rcmail->output->command('open_compose_step', ['_mailto' => $mailto_id]);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('nocontactsfound', 'warning');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Move a contact record from one directory to another |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_move extends rcmail_action_contacts_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$cids = self::get_cids();
|
||||
$target = rcube_utils::get_input_string('_to', rcube_utils::INPUT_POST);
|
||||
$target_group = rcube_utils::get_input_string('_togid', rcube_utils::INPUT_POST);
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$all = 0;
|
||||
$deleted = 0;
|
||||
$success = 0;
|
||||
$errormsg = 'moveerror';
|
||||
$maxnum = $rcmail->config->get('max_group_members', 0);
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$page = !empty($_SESSION['page']) ? $_SESSION['page'] : 1;
|
||||
|
||||
foreach ($cids as $source => $source_cids) {
|
||||
// Something wrong, target not specified
|
||||
if (!strlen($target)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// It might happen when moving records from search result
|
||||
// Do nothing, go to the next source
|
||||
if ((string) $target === (string) $source) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$CONTACTS = $rcmail->get_address_book($source);
|
||||
$TARGET = $rcmail->get_address_book($target);
|
||||
|
||||
if (!$TARGET || !$TARGET->ready || $TARGET->readonly) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$CONTACTS || !$CONTACTS->ready || ($CONTACTS->readonly && empty($CONTACTS->deletable))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ids = [];
|
||||
|
||||
foreach ($source_cids as $idx => $cid) {
|
||||
$record = $CONTACTS->get_record($cid, true);
|
||||
|
||||
// avoid moving groups
|
||||
if (isset($record['_type']) && $record['_type'] == 'group') {
|
||||
unset($source_cids[$idx]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if contact exists, if so, we'll need it's ID
|
||||
// Note: Some addressbooks allows empty email address field
|
||||
// @TODO: should we check all email addresses?
|
||||
$email = $CONTACTS->get_col_values('email', $record, true);
|
||||
|
||||
if (!empty($email)) {
|
||||
$result = $TARGET->search('email', $email[0], 1, true, true);
|
||||
}
|
||||
else if (!empty($record['name'])) {
|
||||
$result = $TARGET->search('name', $record['name'], 1, true, true);
|
||||
}
|
||||
else {
|
||||
$result = new rcube_result_set();
|
||||
}
|
||||
|
||||
// insert contact record
|
||||
if (!$result->count) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $record,
|
||||
'source' => $target,
|
||||
'group' => $target_group
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
if ($insert_id = $TARGET->insert($plugin['record'], false)) {
|
||||
$ids[] = $insert_id;
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else if (!empty($plugin['result'])) {
|
||||
$ids = array_merge($ids, $plugin['result']);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$record = $result->first();
|
||||
$ids[] = $record['ID'];
|
||||
$errormsg = empty($email) ? 'contactnameexists' : 'contactexists';
|
||||
}
|
||||
}
|
||||
|
||||
// remove source contacts
|
||||
if ($success && !empty($source_cids)) {
|
||||
$all += count($source_cids);
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_delete', [
|
||||
'id' => $source_cids,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$del_status = !$plugin['abort'] ? $CONTACTS->delete($source_cids) : $plugin['result'];
|
||||
|
||||
if ($del_status) {
|
||||
$deleted += $del_status;
|
||||
}
|
||||
}
|
||||
|
||||
// assign to group
|
||||
if ($target_group && $TARGET->groups && !empty($ids)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $target_group,
|
||||
'ids' => $ids,
|
||||
'source' => $target
|
||||
]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$TARGET->reset();
|
||||
$TARGET->set_group($target_group);
|
||||
|
||||
if ($maxnum && ($TARGET->count()->count + count($plugin['ids']) > $maxnum)) {
|
||||
$rcmail->output->show_message('maxgroupmembersreached', 'warning', ['max' => $maxnum]);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if (($cnt = $TARGET->add_to_group($target_group, $plugin['ids'])) && $cnt > $success) {
|
||||
$success = $cnt;
|
||||
}
|
||||
}
|
||||
else if ($plugin['result']) {
|
||||
$success = $plugin['result'];
|
||||
}
|
||||
|
||||
$errormsg = !empty($plugin['message']) ? $plugin['message'] : 'moveerror';
|
||||
}
|
||||
}
|
||||
|
||||
if (!$deleted || $deleted != $all) {
|
||||
$rcmail->output->command('list_contacts');
|
||||
}
|
||||
else {
|
||||
// update saved search after data changed
|
||||
if (($records = self::search_update(true)) !== false) {
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
$pages = ceil((count($records) + $deleted) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
$first += $page_size;
|
||||
// create resultset object
|
||||
$res = new rcube_result_set($count, $first - $deleted);
|
||||
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first - $deleted, $deleted);
|
||||
}
|
||||
|
||||
$res->records = array_values($records);
|
||||
$records = $res;
|
||||
}
|
||||
else {
|
||||
unset($records);
|
||||
}
|
||||
}
|
||||
else if (isset($CONTACTS)) {
|
||||
// count contacts for this user
|
||||
$result = $CONTACTS->count();
|
||||
$pages = ceil(($result->count + $deleted) / $page_size);
|
||||
|
||||
// last page and it's empty, display previous one
|
||||
if ($result->count && $result->count <= ($page_size * ($page - 1))) {
|
||||
$rcmail->output->command('list_page', 'prev');
|
||||
$rowcount = $rcmail->gettext('loading');
|
||||
}
|
||||
// get records from the next page to add to the list
|
||||
else if ($pages > 1 && $page < $pages) {
|
||||
$CONTACTS->set_page($page);
|
||||
$records = $CONTACTS->list_records(null, -$deleted);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($rowcount)) {
|
||||
$rowcount = isset($result) ? self::get_rowcount_text($result) : 0;
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 0);
|
||||
$rcmail->output->command('set_rowcount', $rowcount);
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if (!empty($records)) {
|
||||
self::js_contacts_list($records);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
$rcmail->output->show_message($errormsg, 'error');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('movesuccess', 'confirmation', ['nr' => $success]);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact photo |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_photo extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
$file_id = rcube_utils::get_input_string('_photo', rcube_utils::INPUT_GPC);
|
||||
|
||||
// read the referenced file
|
||||
if ($file_id && !empty($_SESSION['contacts']['files'][$file_id])) {
|
||||
$tempfile = $_SESSION['contacts']['files'][$file_id];
|
||||
$tempfile = $rcmail->plugins->exec_hook('attachment_display', $tempfile);
|
||||
|
||||
if (!empty($tempfile['status'])) {
|
||||
if (!empty($tempfile['data'])) {
|
||||
$data = $tempfile['data'];
|
||||
}
|
||||
else if ($tempfile['path']) {
|
||||
$data = file_get_contents($tempfile['path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// by email, search for contact first
|
||||
if ($email = rcube_utils::get_input_string('_email', rcube_utils::INPUT_GPC)) {
|
||||
foreach ($rcmail->get_address_sources() as $s) {
|
||||
$abook = $rcmail->get_address_book($s['id']);
|
||||
$result = $abook->search(['email'], $email, 1, true, true, 'photo');
|
||||
while ($result && ($record = $result->iterate())) {
|
||||
if (!empty($record['photo'])) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// by contact id
|
||||
if (empty($record) && $cid) {
|
||||
// Initialize addressbook source
|
||||
$CONTACTS = self::contact_source($source, true);
|
||||
// read contact record
|
||||
$record = $CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
if (!empty($record['photo'])) {
|
||||
$data = is_array($record['photo']) ? $record['photo'][0] : $record['photo'];
|
||||
if (!preg_match('![^a-z0-9/=+-]!i', $data)) {
|
||||
$data = base64_decode($data, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let plugins do fancy things with contact photos
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_photo', [
|
||||
'record' => $record ?? null,
|
||||
'email' => $email ?? null,
|
||||
'data' => $data ?? null,
|
||||
]);
|
||||
|
||||
// redirect to url provided by a plugin
|
||||
if (!empty($plugin['url'])) {
|
||||
$rcmail->output->redirect($plugin['url']);
|
||||
}
|
||||
|
||||
$data = $plugin['data'];
|
||||
|
||||
// detect if photo data is a URL
|
||||
if ($data && strlen($data) < 1024 && filter_var($data, FILTER_VALIDATE_URL)) {
|
||||
$rcmail->output->redirect($data);
|
||||
}
|
||||
|
||||
// cache for one day if requested by email
|
||||
if (!$cid && !empty($email)) {
|
||||
$rcmail->output->future_expire_header(86400);
|
||||
}
|
||||
|
||||
if ($data) {
|
||||
$rcmail->output->sendExit($data, ['Content-Type: ' . rcube_mime::image_content_type($data)]);
|
||||
}
|
||||
|
||||
if (!empty($_GET['_error'])) {
|
||||
$rcmail->output->sendExit('', ['HTTP/1.0 204 Photo not found']);
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit(base64_decode(rcmail_output::BLANK_GIF), ['Content-Type: image/gif']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Print contact details |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_print extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
|
||||
// Initialize addressbook source
|
||||
self::$CONTACTS = self::contact_source($source, true);
|
||||
self::$SOURCE_ID = $source;
|
||||
|
||||
// read contact record
|
||||
if ($cid && self::$CONTACTS) {
|
||||
self::$contact = self::$CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contacthead' => [$this, 'contact_head'],
|
||||
'contactdetails' => [$this, 'contact_details'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
]);
|
||||
|
||||
$rcmail->output->send('contactprint');
|
||||
}
|
||||
|
||||
public static function contact_head($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = [
|
||||
'head' => [ // section 'head' is magic!
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'prefix' => [],
|
||||
'name' => [],
|
||||
'firstname' => [],
|
||||
'middlename' => [],
|
||||
'surname' => [],
|
||||
'suffix' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
unset($attrib['name']);
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function contact_details($attrib)
|
||||
{
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'organization' => [],
|
||||
'department' => [],
|
||||
'jobtitle' => [],
|
||||
'email' => [],
|
||||
'phone' => [],
|
||||
'address' => [],
|
||||
'website' => [],
|
||||
'im' => [],
|
||||
'groups' => [],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext('personalinfo'),
|
||||
'content' => [
|
||||
'nickname' => [],
|
||||
'gender' => [],
|
||||
'maidenname' => [],
|
||||
'birthday' => [],
|
||||
'anniversary' => [],
|
||||
'manager' => [],
|
||||
'assistant' => [],
|
||||
'spouse' => [],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(rcmail_action_contacts_index::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'content' => [
|
||||
'notes' => ['type' => 'textarea', 'label' => false],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (self::$CONTACTS->groups) {
|
||||
$groups = self::$CONTACTS->get_record_groups(self::$contact['ID']);
|
||||
if (!empty($groups)) {
|
||||
$form['contact']['content']['groups'] = [
|
||||
'value' => rcube::Q(implode(', ', $groups)),
|
||||
'label' => $rcmail->gettext('groups')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact data as QR code |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_qrcode extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// read contact record
|
||||
$abook = self::contact_source($source, true);
|
||||
$contact = $abook->get_record($cid, true);
|
||||
|
||||
// generate QR code image
|
||||
if ($data = self::contact_qrcode($contact)) {
|
||||
$headers = [
|
||||
'Content-Type: ' . self::check_support(),
|
||||
'Content-Length: ' . strlen($data)
|
||||
];
|
||||
|
||||
$rcmail->output->sendExit($data, $headers);
|
||||
}
|
||||
|
||||
$rcmail->output->sendExit('', ['HTTP/1.0 404 Contact not found']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a QR-code image for a contact
|
||||
*
|
||||
* @param array $contact Contact record
|
||||
*
|
||||
* @return string|null Image content, Null on error or missing PHP extensions
|
||||
*/
|
||||
public static function contact_qrcode($contact)
|
||||
{
|
||||
if (empty($contact)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = self::check_support();
|
||||
|
||||
if (empty($type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$vcard = new rcube_vcard();
|
||||
|
||||
// QR code input is limited, use only common fields
|
||||
$fields = ['name', 'firstname', 'surname', 'middlename', 'nickname',
|
||||
'organization', 'phone', 'email', 'jobtitle', 'prefix', 'suffix'];
|
||||
|
||||
foreach ($contact as $field => $value) {
|
||||
if (strpos($field, ':') !== false) {
|
||||
list($field, $section) = explode(':', $field, 2);
|
||||
}
|
||||
else {
|
||||
$section = null;
|
||||
}
|
||||
|
||||
if (in_array($field, $fields)) {
|
||||
foreach ((array) $value as $v) {
|
||||
$vcard->set($field, $v, $section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = $vcard->export();
|
||||
|
||||
if (empty($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$renderer_style = new BaconQrCode\Renderer\RendererStyle\RendererStyle(300, 1);
|
||||
$renderer_image = $type == 'image/png'
|
||||
? new BaconQrCode\Renderer\Image\ImagickImageBackEnd()
|
||||
: new BaconQrCode\Renderer\Image\SvgImageBackEnd();
|
||||
|
||||
$renderer = new BaconQrCode\Renderer\ImageRenderer($renderer_style, $renderer_image);
|
||||
$writer = new BaconQrCode\Writer($renderer);
|
||||
|
||||
return $writer->writeString($data, RCUBE_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check required extensions and classes for QR code generation
|
||||
*
|
||||
* @return string|null Content-type of the image result
|
||||
*/
|
||||
public static function check_support()
|
||||
{
|
||||
if (extension_loaded('iconv') && class_exists('BaconQrCode\Renderer\ImageRenderer')) {
|
||||
if (extension_loaded('xmlwriter')) {
|
||||
return 'image/svg+xml';
|
||||
}
|
||||
|
||||
if (extension_loaded('imagick')) {
|
||||
return 'image/png';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Save a contact entry or to add a new one |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_save extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$contacts = self::contact_source(null, true);
|
||||
$cid = rcube_utils::get_input_string('_cid', rcube_utils::INPUT_POST);
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$return_action = empty($cid) ? 'add' : 'edit';
|
||||
|
||||
// Source changed, display the form again
|
||||
if (!empty($_GET['_reload'])) {
|
||||
$rcmail->overwrite_action($return_action);
|
||||
return;
|
||||
}
|
||||
|
||||
// cannot edit record
|
||||
if (!$contacts || $contacts->readonly) {
|
||||
$rcmail->output->show_message('contactreadonly', 'error');
|
||||
$rcmail->overwrite_action($return_action);
|
||||
return;
|
||||
}
|
||||
|
||||
// read POST values into hash array
|
||||
$a_record = self::process_input();
|
||||
|
||||
// do input checks (delegated to $contacts instance)
|
||||
if (!$contacts->validate($a_record)) {
|
||||
$err = (array) $contacts->get_error();
|
||||
$rcmail->output->show_message(!empty($err['message']) ? rcube::Q($err['message']) : 'formincomplete', 'warning');
|
||||
$rcmail->overwrite_action($return_action, ['contact' => $a_record]);
|
||||
return;
|
||||
}
|
||||
|
||||
// get raw photo data if changed
|
||||
if (isset($a_record['photo'])) {
|
||||
if ($a_record['photo'] == '-del-') {
|
||||
$a_record['photo'] = '';
|
||||
}
|
||||
else if (!empty($_SESSION['contacts']['files'][$a_record['photo']])) {
|
||||
$tempfile = $_SESSION['contacts']['files'][$a_record['photo']];
|
||||
$tempfile = $rcmail->plugins->exec_hook('attachment_get', $tempfile);
|
||||
if ($tempfile['status']) {
|
||||
$a_record['photo'] = $tempfile['data'] ?: @file_get_contents($tempfile['path']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
unset($a_record['photo']);
|
||||
}
|
||||
|
||||
// cleanup session data
|
||||
$rcmail->plugins->exec_hook('attachments_cleanup', ['group' => 'contact']);
|
||||
$rcmail->session->remove('contacts');
|
||||
}
|
||||
|
||||
// update an existing contact
|
||||
if (!empty($cid)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_update', [
|
||||
'id' => $cid,
|
||||
'record' => $a_record,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$result = $contacts->update($cid, $a_record);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
// show confirmation
|
||||
$rcmail->output->show_message('successfullysaved', 'confirmation', null, false);
|
||||
|
||||
// in search mode, just reload the list (#1490015)
|
||||
if (!empty($_REQUEST['_search'])) {
|
||||
$rcmail->output->command('parent.command', 'list');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
$newcid = null;
|
||||
|
||||
// LDAP DN change
|
||||
if (is_string($result) && strlen($result) > 1) {
|
||||
$newcid = $result;
|
||||
// change cid in POST for 'show' action
|
||||
$_POST['_cid'] = $newcid;
|
||||
}
|
||||
|
||||
// refresh contact data for list update and 'show' action
|
||||
$contact = $contacts->get_record($newcid ?: $cid, true);
|
||||
|
||||
// Plugins can decide to remove the contact on edit, e.g. automatic_addressbook
|
||||
// Best we can do is to refresh the list (#5522)
|
||||
if (empty($contact)) {
|
||||
$rcmail->output->command('parent.command', 'list');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
// Update contacts list
|
||||
$a_js_cols = [];
|
||||
$record = $contact;
|
||||
$record['email'] = array_first($contacts->get_col_values('email', $record, true));
|
||||
$record['name'] = rcube_addressbook::compose_list_name($record);
|
||||
|
||||
foreach (['name'] as $col) {
|
||||
$a_js_cols[] = rcube::Q((string) $record[$col]);
|
||||
}
|
||||
|
||||
// performance: unset some big data items we don't need here
|
||||
$record = array_intersect_key($record, ['ID' => 1,'email' => 1,'name' => 1]);
|
||||
$record['_type'] = 'person';
|
||||
|
||||
// update the changed col in list
|
||||
$rcmail->output->command('parent.update_contact_row', $cid, $a_js_cols, $newcid, $source, $record);
|
||||
|
||||
$rcmail->overwrite_action('show', ['contact' => $contact]);
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
$error = self::error_str($contacts, $plugin);
|
||||
|
||||
$rcmail->output->show_message($error, 'error', null, false);
|
||||
$rcmail->overwrite_action('show');
|
||||
}
|
||||
}
|
||||
// insert a new contact
|
||||
else {
|
||||
// Name of the addressbook already selected on the list
|
||||
$orig_source = rcube_utils::get_input_string('_orig_source', rcube_utils::INPUT_GPC);
|
||||
|
||||
if (!strlen($source)) {
|
||||
$source = $orig_source;
|
||||
}
|
||||
|
||||
// show notice if existing contacts with same e-mail are found
|
||||
foreach ($contacts->get_col_values('email', $a_record, true) as $email) {
|
||||
if ($email && ($res = $contacts->search('email', $email, 1, false, true)) && $res->count) {
|
||||
$rcmail->output->show_message('contactexists', 'notice', null, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_create', [
|
||||
'record' => $a_record,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$a_record = $plugin['record'];
|
||||
|
||||
// insert record and send response
|
||||
if (!$plugin['abort']) {
|
||||
$insert_id = $contacts->insert($a_record);
|
||||
}
|
||||
else {
|
||||
$insert_id = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($insert_id) {
|
||||
$contacts->reset();
|
||||
|
||||
// add new contact to the specified group
|
||||
if ($contacts->groups && $contacts->group_id) {
|
||||
$plugin = $rcmail->plugins->exec_hook('group_addmembers', [
|
||||
'group_id' => $contacts->group_id,
|
||||
'ids' => $insert_id,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
if (($maxnum = $rcmail->config->get('max_group_members', 0)) && ($contacts->count()->count + 1 > $maxnum)) {
|
||||
// @FIXME: should we remove the contact?
|
||||
$msgtext = $rcmail->gettext(['name' => 'maxgroupmembersreached', 'vars' => ['max' => $maxnum]]);
|
||||
$rcmail->output->command('parent.display_message', $msgtext, 'warning');
|
||||
}
|
||||
else {
|
||||
$contacts->add_to_group($plugin['group_id'], $plugin['ids']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// show confirmation
|
||||
$rcmail->output->show_message('successfullysaved', 'confirmation', null, false);
|
||||
|
||||
$rcmail->output->command('parent.set_rowcount', $rcmail->gettext('loading'));
|
||||
$rcmail->output->command('parent.list_contacts');
|
||||
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
$error = self::error_str($contacts, $plugin);
|
||||
$rcmail->output->show_message($error, 'error', null, false);
|
||||
$rcmail->overwrite_action('add');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function process_input()
|
||||
{
|
||||
$record = [];
|
||||
|
||||
foreach (rcmail_action_contacts_index::$CONTACT_COLTYPES as $col => $colprop) {
|
||||
if (!empty($colprop['composite'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fname = '_' . $col;
|
||||
|
||||
// gather form data of composite fields
|
||||
if (!empty($colprop['childs'])) {
|
||||
$values = [];
|
||||
foreach ($colprop['childs'] as $childcol => $cp) {
|
||||
$vals = rcube_utils::get_input_value('_' . $childcol, rcube_utils::INPUT_POST, true);
|
||||
foreach ((array) $vals as $i => $val) {
|
||||
$values[$i][$childcol] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['_subtype_' . $col])) {
|
||||
$subtypes = (array) rcube_utils::get_input_value('_subtype_' . $col, rcube_utils::INPUT_POST);
|
||||
}
|
||||
else {
|
||||
$subtypes = [''];
|
||||
}
|
||||
|
||||
foreach ($subtypes as $i => $subtype) {
|
||||
$suffix = $subtype ? ":$subtype" : '';
|
||||
if ($values[$i]) {
|
||||
$record[$col . $suffix][] = $values[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
// assign values and subtypes
|
||||
else if (isset($_POST[$fname]) && is_array($_POST[$fname])) {
|
||||
$values = rcube_utils::get_input_value($fname, rcube_utils::INPUT_POST, true);
|
||||
$subtypes = rcube_utils::get_input_value('_subtype_' . $col, rcube_utils::INPUT_POST);
|
||||
|
||||
foreach ($values as $i => $val) {
|
||||
if ($col == 'email') {
|
||||
// extract email from full address specification, e.g. "Name" <addr@domain.tld>
|
||||
$addr = rcube_mime::decode_address_list($val, 1, false);
|
||||
if (!empty($addr) && ($addr = array_pop($addr)) && $addr['mailto']) {
|
||||
$val = $addr['mailto'];
|
||||
}
|
||||
}
|
||||
|
||||
$subtype = $subtypes[$i] ? ':'.$subtypes[$i] : '';
|
||||
$record[$col.$subtype][] = $val;
|
||||
}
|
||||
}
|
||||
else if (isset($_POST[$fname])) {
|
||||
$record[$col] = rcube_utils::get_input_value($fname, rcube_utils::INPUT_POST, true);
|
||||
|
||||
// normalize the submitted date strings
|
||||
if ($colprop['type'] == 'date') {
|
||||
if ($record[$col] && ($dt = rcube_utils::anytodatetime($record[$col]))) {
|
||||
$record[$col] = $dt->format('Y-m-d');
|
||||
}
|
||||
else {
|
||||
unset($record[$col]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate contact's display name (must be before validation)
|
||||
if (empty($record['name'])) {
|
||||
$record['name'] = rcube_addressbook::compose_display_name($record, true);
|
||||
|
||||
// Reset it if equals to email address (from compose_display_name())
|
||||
$email = rcube_addressbook::get_col_values('email', $record, true);
|
||||
if (isset($email[0]) && $record['name'] == $email[0]) {
|
||||
$record['name'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $record;
|
||||
}
|
||||
|
||||
public static function error_str($contacts, $plugin)
|
||||
{
|
||||
if (!empty($plugin['message'])) {
|
||||
return $plugin['message'];
|
||||
}
|
||||
|
||||
$err = $contacts->get_error();
|
||||
|
||||
if (!empty($err['message'])) {
|
||||
return $err['message'];
|
||||
}
|
||||
|
||||
return 'errorsaving';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Search action (and form) for address book contacts |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (empty($_GET['_form'])) {
|
||||
self::contact_search();
|
||||
}
|
||||
|
||||
$rcmail->output->add_handler('searchform', [$this, 'contact_search_form']);
|
||||
$rcmail->output->send('contactsearch');
|
||||
}
|
||||
|
||||
public static function contact_search()
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$adv = isset($_POST['_adv']);
|
||||
$sid = rcube_utils::get_input_string('_sid', rcube_utils::INPUT_GET);
|
||||
$search = null;
|
||||
|
||||
// get search criteria from saved search
|
||||
if ($sid && ($search = $rcmail->user->get_search($sid))) {
|
||||
$fields = $search['data']['fields'];
|
||||
$search = $search['data']['search'];
|
||||
}
|
||||
// get fields/values from advanced search form
|
||||
else if ($adv) {
|
||||
foreach (array_keys($_POST) as $key) {
|
||||
$s = trim(rcube_utils::get_input_string($key, rcube_utils::INPUT_POST, true));
|
||||
if (strlen($s) && preg_match('/^_search_([a-zA-Z0-9_-]+)$/', $key, $m)) {
|
||||
$search[] = $s;
|
||||
$fields[] = $m[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($fields)) {
|
||||
// do nothing, show the form again
|
||||
return;
|
||||
}
|
||||
}
|
||||
// quick-search
|
||||
else {
|
||||
$search = trim(rcube_utils::get_input_string('_q', rcube_utils::INPUT_GET, true));
|
||||
$fields = rcube_utils::get_input_string('_headers', rcube_utils::INPUT_GET);
|
||||
|
||||
if (empty($fields)) {
|
||||
$fields = array_keys(self::$SEARCH_MODS_DEFAULT);
|
||||
}
|
||||
else {
|
||||
$fields = array_filter(explode(',', $fields));
|
||||
}
|
||||
|
||||
// update search_mods setting
|
||||
$old_mods = $rcmail->config->get('addressbook_search_mods');
|
||||
$search_mods = array_fill_keys($fields, 1);
|
||||
|
||||
if ($old_mods != $search_mods) {
|
||||
$rcmail->user->save_prefs(['addressbook_search_mods' => $search_mods]);
|
||||
}
|
||||
|
||||
if (in_array('*', $fields)) {
|
||||
$fields = '*';
|
||||
}
|
||||
}
|
||||
|
||||
// Values matching mode
|
||||
$mode = (int) $rcmail->config->get('addressbook_search_mode');
|
||||
$mode |= rcube_addressbook::SEARCH_GROUPS;
|
||||
|
||||
// get sources list
|
||||
$sources = $rcmail->get_address_sources();
|
||||
$sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$search_set = [];
|
||||
$records = [];
|
||||
|
||||
foreach ($sources as $s) {
|
||||
$source = $rcmail->get_address_book($s['id']);
|
||||
|
||||
// check if search fields are supported....
|
||||
if (is_array($fields)) {
|
||||
$cols = !empty($source->coltypes[0]) ? array_flip($source->coltypes) : $source->coltypes;
|
||||
$supported = 0;
|
||||
|
||||
foreach ($fields as $f) {
|
||||
if (array_key_exists($f, $cols)) {
|
||||
$supported ++;
|
||||
}
|
||||
}
|
||||
|
||||
// in advanced search we require all fields (AND operator)
|
||||
// in quick search we require at least one field (OR operator)
|
||||
if (($adv && $supported < count($fields)) || (!$adv && !$supported)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// reset page
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(9999);
|
||||
|
||||
// get contacts count
|
||||
$result = $source->search($fields, $search, $mode, false);
|
||||
|
||||
if (empty($result) || !$result->count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get records
|
||||
$result = $source->list_records($afields);
|
||||
|
||||
while ($row = $result->next()) {
|
||||
$row['sourceid'] = $s['id'];
|
||||
$key = rcube_addressbook::compose_contact_key($row, $sort_col);
|
||||
$records[$key] = $row;
|
||||
}
|
||||
|
||||
unset($result);
|
||||
$search_set[$s['id']] = $source->get_search_set();
|
||||
}
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
|
||||
// cut first-page records
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, 0, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
|
||||
// search request ID
|
||||
$search_request = md5('addr'
|
||||
. (is_array($fields) ? implode(',', $fields) : $fields)
|
||||
. (is_array($search) ? implode(',', $search) : $search)
|
||||
);
|
||||
|
||||
// save search settings in session
|
||||
$_SESSION['contact_search'][$search_request] = $search_set;
|
||||
$_SESSION['contact_search_params'] = ['id' => $search_request, 'data' => [$fields, $search]];
|
||||
$_SESSION['page'] = 1;
|
||||
|
||||
if ($adv) {
|
||||
$rcmail->output->command('list_contacts_clear');
|
||||
}
|
||||
|
||||
if ($result->count > 0) {
|
||||
// create javascript list
|
||||
self::js_contacts_list($result);
|
||||
$rcmail->output->show_message('contactsearchsuccessful', 'confirmation', ['nr' => $result->count]);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('nocontactsfound', 'notice');
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('search_request', $search_request);
|
||||
$rcmail->output->set_env('pagecount', ceil($result->count / $page_size));
|
||||
$rcmail->output->command('set_rowcount', self::get_rowcount_text($result));
|
||||
// Re-set current source
|
||||
$rcmail->output->set_env('search_id', $sid);
|
||||
$rcmail->output->set_env('source', '');
|
||||
$rcmail->output->set_env('group', '');
|
||||
// Re-set list header
|
||||
$rcmail->output->command('set_group_prop', null);
|
||||
|
||||
if (!$sid) {
|
||||
// unselect currently selected directory/group
|
||||
$rcmail->output->command('unselect_directory');
|
||||
// enable "Save search" command
|
||||
$rcmail->output->command('enable_command', 'search-create', true);
|
||||
}
|
||||
|
||||
$rcmail->output->command('update_group_commands');
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
public static function contact_search_form($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 30;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'main' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [],
|
||||
],
|
||||
'other' => [
|
||||
'name' => $rcmail->gettext('other'),
|
||||
'content' => [],
|
||||
],
|
||||
];
|
||||
|
||||
// get supported coltypes from all address sources
|
||||
$sources = $rcmail->get_address_sources();
|
||||
$coltypes = [];
|
||||
|
||||
foreach ($sources as $s) {
|
||||
$CONTACTS = $rcmail->get_address_book($s['id']);
|
||||
|
||||
if (!empty($CONTACTS->coltypes)) {
|
||||
$contact_cols = isset($CONTACTS->coltypes[0]) ? array_flip($CONTACTS->coltypes) : $CONTACTS->coltypes;
|
||||
$coltypes = array_merge($coltypes, $contact_cols);
|
||||
}
|
||||
}
|
||||
|
||||
// merge supported coltypes with global coltypes
|
||||
foreach ($coltypes as $col => $colprop) {
|
||||
if (!empty(rcmail_action_contacts_index::$CONTACT_COLTYPES[$col])) {
|
||||
$coltypes[$col] = array_merge(rcmail_action_contacts_index::$CONTACT_COLTYPES[$col], (array) $colprop);
|
||||
}
|
||||
else {
|
||||
$coltypes[$col] = (array) $colprop;
|
||||
}
|
||||
}
|
||||
|
||||
// build form fields list
|
||||
foreach ($coltypes as $col => $colprop) {
|
||||
if (!isset($colprop['type'])) {
|
||||
$colprop['type'] = 'text';
|
||||
}
|
||||
if ($colprop['type'] != 'image' && empty($colprop['nosearch'])) {
|
||||
$ftype = $colprop['type'] == 'select' ? 'select' : 'text';
|
||||
$label = $colprop['label'] ?? $rcmail->gettext($col);
|
||||
$category = !empty($colprop['category']) ? $colprop['category'] : 'other';
|
||||
|
||||
// load jquery UI datepicker for date fields
|
||||
if ($colprop['type'] == 'date') {
|
||||
$colprop['class'] = (!empty($colprop['class']) ? $colprop['class'] . ' ' : '') . 'datepicker';
|
||||
}
|
||||
else if ($ftype == 'text') {
|
||||
$colprop['size'] = $i_size;
|
||||
}
|
||||
|
||||
$colprop['id'] = '_search_' . $col;
|
||||
|
||||
$content = html::div('row',
|
||||
html::label(['class' => 'contactfieldlabel label', 'for' => $colprop['id']], rcube::Q($label))
|
||||
. html::div('contactfieldcontent', rcube_output::get_edit_field('search_' . $col, '', $colprop, $ftype))
|
||||
);
|
||||
|
||||
$form[$category]['content'][] = $content;
|
||||
}
|
||||
}
|
||||
|
||||
$hiddenfields = new html_hiddenfield();
|
||||
$hiddenfields->add(['name' => '_adv', 'value' => 1]);
|
||||
|
||||
$out = $rcmail->output->request_form([
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'search',
|
||||
'noclose' => true,
|
||||
] + $attrib, $hiddenfields->show()
|
||||
);
|
||||
|
||||
$rcmail->output->add_gui_object('editform', $attrib['id']);
|
||||
|
||||
unset($attrib['name']);
|
||||
unset($attrib['id']);
|
||||
|
||||
foreach ($form as $f) {
|
||||
if (!empty($f['content'])) {
|
||||
$content = html::div('contactfieldgroup', join("\n", $f['content']));
|
||||
$legend = html::tag('legend', null, rcube::Q($f['name']));
|
||||
|
||||
$out .= html::tag('fieldset', $attrib, $legend . $content) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return $out . '</form>';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Create saved search |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search_create extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$id = rcube_utils::get_input_value('_search', rcube_utils::INPUT_POST);
|
||||
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (
|
||||
!empty($_SESSION['contact_search_params'])
|
||||
&& ($params = $_SESSION['contact_search_params'])
|
||||
&& $params['id'] == $id
|
||||
) {
|
||||
$data = [
|
||||
'type' => rcube_user::SEARCH_ADDRESSBOOK,
|
||||
'name' => $name,
|
||||
'data' => [
|
||||
'fields' => $params['data'][0],
|
||||
'search' => $params['data'][1],
|
||||
],
|
||||
];
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('saved_search_create', ['data' => $data]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $rcmail->user->insert_search($plugin['data']);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
$rcmail->output->show_message('savedsearchcreated', 'confirmation');
|
||||
$rcmail->output->command('insert_saved_search', rcube::Q($name), rcube::Q($result));
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchcreateerror';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delete saved search |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_search_delete extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$id = rcube_utils::get_input_string('_sid', rcube_utils::INPUT_POST);
|
||||
$result = false;
|
||||
|
||||
if (!empty($id)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('saved_search_delete', ['id' => $id]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$result = $rcmail->user->delete_search($id);
|
||||
}
|
||||
else {
|
||||
$result = $plugin['result'];
|
||||
}
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
$rcmail->output->show_message('savedsearchdeleted', 'confirmation');
|
||||
$rcmail->output->command('remove_search_item', rcube::Q($id));
|
||||
// contact list will be cleared, clear also page counter
|
||||
$rcmail->output->command('set_rowcount', $rcmail->gettext('nocontactsfound'));
|
||||
$rcmail->output->set_env('pagecount', 0);
|
||||
}
|
||||
else {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'savedsearchdeleteerror';
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Show contact details |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_show extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// Get contact ID and source ID from request
|
||||
$cids = self::get_cids();
|
||||
$source = key($cids);
|
||||
$cid = $cids ? array_first($cids[$source]) : null;
|
||||
|
||||
// Initialize addressbook source
|
||||
self::$CONTACTS = self::contact_source($source, true);
|
||||
self::$SOURCE_ID = $source;
|
||||
|
||||
// read contact record (or get the one defined in 'save' action)
|
||||
if (!empty($args['contact'])) {
|
||||
self::$contact = $args['contact'];
|
||||
}
|
||||
else if ($cid) {
|
||||
self::$contact = self::$CONTACTS->get_record($cid, true);
|
||||
}
|
||||
|
||||
if ($cid && self::$contact) {
|
||||
$rcmail->output->set_env('readonly', self::$CONTACTS->readonly || !empty(self::$contact['readonly']));
|
||||
$rcmail->output->set_env('cid', self::$contact['ID']);
|
||||
|
||||
// remember current search request ID (if in search mode)
|
||||
if ($search = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GET)) {
|
||||
$rcmail->output->set_env('search_request', $search);
|
||||
}
|
||||
}
|
||||
|
||||
// get address book name (for display)
|
||||
self::set_sourcename(self::$CONTACTS);
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'contacthead' => [$this, 'contact_head'],
|
||||
'contactdetails' => [$this, 'contact_details'],
|
||||
'contactphoto' => [$this, 'contact_photo'],
|
||||
]);
|
||||
|
||||
$rcmail->output->send('contact');
|
||||
}
|
||||
|
||||
public static function contact_head($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
$rcmail->output->show_message('contactnotfound', 'error');
|
||||
return false;
|
||||
}
|
||||
|
||||
$form = [
|
||||
'head' => [ // section 'head' is magic!
|
||||
'name' => $rcmail->gettext('contactnameandorg'),
|
||||
'content' => [
|
||||
'source' => ['type' => 'text'],
|
||||
'prefix' => ['type' => 'text'],
|
||||
'firstname' => ['type' => 'text'],
|
||||
'middlename' => ['type' => 'text'],
|
||||
'surname' => ['type' => 'text'],
|
||||
'suffix' => ['type' => 'text'],
|
||||
'name' => ['type' => 'text'],
|
||||
'nickname' => ['type' => 'text'],
|
||||
'organization' => ['type' => 'text'],
|
||||
'department' => ['type' => 'text'],
|
||||
'jobtitle' => ['type' => 'text'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
unset($attrib['name']);
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function contact_details($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// check if we have a valid result
|
||||
if (!self::$contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$i_size = !empty($attrib['size']) ? $attrib['size'] : 40;
|
||||
$short_labels = self::get_bool_attr($attrib, 'short-legend-labels');
|
||||
|
||||
$form = [
|
||||
'contact' => [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
'content' => [
|
||||
'email' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_email_value'],
|
||||
'phone' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_phone_value'],
|
||||
'address' => [],
|
||||
'website' => ['size' => $i_size, 'render_func' => 'rcmail_action_contacts_show::render_url_value'],
|
||||
'im' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
'personal' => [
|
||||
'name' => $rcmail->gettext($short_labels ? 'personal' : 'personalinfo'),
|
||||
'content' => [
|
||||
'gender' => ['size' => $i_size],
|
||||
'maidenname' => ['size' => $i_size],
|
||||
'birthday' => ['size' => $i_size],
|
||||
'anniversary' => ['size' => $i_size],
|
||||
'manager' => ['size' => $i_size],
|
||||
'assistant' => ['size' => $i_size],
|
||||
'spouse' => ['size' => $i_size],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset(rcmail_action_contacts_index::$CONTACT_COLTYPES['notes'])) {
|
||||
$form['notes'] = [
|
||||
'name' => $rcmail->gettext('notes'),
|
||||
'content' => [
|
||||
'notes' => ['type' => 'textarea', 'label' => false],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
if (self::$CONTACTS->groups) {
|
||||
$form['groups'] = [
|
||||
'name' => $rcmail->gettext('groups'),
|
||||
'content' => self::contact_record_groups(self::$contact['ID']),
|
||||
];
|
||||
}
|
||||
|
||||
return self::contact_form($form, self::$contact, $attrib);
|
||||
}
|
||||
|
||||
public static function render_email_value($email)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
return html::a([
|
||||
'href' => 'mailto:' . $email,
|
||||
'onclick' => sprintf(
|
||||
"return %s.command('compose','%s',this)",
|
||||
rcmail_output::JS_OBJECT_NAME,
|
||||
rcube::JQ($email)
|
||||
),
|
||||
'title' => $rcmail->gettext('composeto'),
|
||||
'class' => 'email',
|
||||
],
|
||||
rcube::Q($email)
|
||||
);
|
||||
}
|
||||
|
||||
public static function render_phone_value($phone)
|
||||
{
|
||||
$attrs = [
|
||||
'href' => 'tel:' . preg_replace('/[^0-9+,;-]/', '', $phone),
|
||||
'class' => 'phone',
|
||||
];
|
||||
|
||||
return html::a($attrs, rcube::Q($phone));
|
||||
}
|
||||
|
||||
public static function render_url_value($url)
|
||||
{
|
||||
$prefix = preg_match('!^(http|ftp)s?://!', $url) ? '' : 'http://';
|
||||
|
||||
return html::a([
|
||||
'href' => $prefix . $url,
|
||||
'target' => '_blank',
|
||||
'class' => 'url',
|
||||
],
|
||||
rcube::Q($url)
|
||||
);
|
||||
}
|
||||
|
||||
public static function contact_record_groups($contact_id)
|
||||
{
|
||||
$groups = self::$CONTACTS->list_groups();
|
||||
|
||||
if (empty($groups)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$members = self::$CONTACTS->get_record_groups($contact_id);
|
||||
$table = new html_table(['tagname' => 'ul', 'cols' => 1, 'class' => 'proplist simplelist']);
|
||||
$checkbox = new html_checkbox(['name' => '_gid[]', 'class' => 'groupmember', 'disabled' => self::$CONTACTS->readonly]);
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$gid = $group['ID'];
|
||||
$input = $checkbox->show(!empty($members[$gid]) ? $gid : null, ['value' => $gid]);
|
||||
$table->add(null, html::label(null, $input . rcube::Q($group['name'])));
|
||||
}
|
||||
|
||||
$hiddenfields = new html_hiddenfield(['name' => '_source', 'value' => $source]);
|
||||
$hiddenfields->add(['name' => '_cid', 'value' => $contact_id]);
|
||||
|
||||
$form_attrs = [
|
||||
'name' => 'form',
|
||||
'method' => 'post',
|
||||
'task' => $rcmail->task,
|
||||
'action' => 'save',
|
||||
'request' => 'save.' . intval($contact_id),
|
||||
'noclose' => true,
|
||||
];
|
||||
|
||||
$form_start = $rcmail->output->request_form($form_attrs, $hiddenfields->show());
|
||||
$form_end = '</form>';
|
||||
|
||||
$rcmail->output->add_gui_object('editform', 'form');
|
||||
$rcmail->output->add_label('addingmember', 'removingmember');
|
||||
|
||||
return $form_start . html::tag('fieldset', 'contactfieldgroup contactgroups', $table->show()) . $form_end;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Undelete contacts (CIDs) from last delete action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <machniak@kolabsys.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_undo extends rcmail_action_contacts_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$delcnt = 0;
|
||||
|
||||
if (!empty($_SESSION['contact_undo']) && !empty($_SESSION['contact_undo']['data'])) {
|
||||
foreach ((array) $_SESSION['contact_undo']['data'] as $source => $cid) {
|
||||
$contacts = self::contact_source($source);
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('contact_undelete', [
|
||||
'id' => $cid,
|
||||
'source' => $source
|
||||
]);
|
||||
|
||||
$restored = empty($plugin['abort']) ? $contacts->undelete($cid) : $plugin['result'];
|
||||
|
||||
if (!$restored) {
|
||||
$error = !empty($plugin['message']) ? $plugin['message'] : 'contactrestoreerror';
|
||||
|
||||
$rcmail->output->show_message($error, 'error');
|
||||
$rcmail->output->command('list_contacts');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$delcnt += $restored;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->session->remove('contact_undo');
|
||||
|
||||
if ($delcnt) {
|
||||
$rcmail->output->show_message('contactrestored', 'confirmation');
|
||||
$rcmail->output->command('list_contacts');
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handles contact photo uploads |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_contacts_upload_photo extends rcmail_action_contacts_index
|
||||
{
|
||||
/**
|
||||
* Supported image format types
|
||||
* ImageMagick works with other non-image types (e.g. pdf), we don't want that here
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $IMAGE_TYPES = ['jpeg','jpg','jp2','tiff','tif','bmp','eps','gif','png','png8','png24','png32','svg','ico'];
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// clear all stored output properties (like scripts and env vars)
|
||||
$rcmail->output->reset();
|
||||
|
||||
if (!empty($_FILES['_photo']['tmp_name'])) {
|
||||
$filepath = $_FILES['_photo']['tmp_name'];
|
||||
|
||||
// check file type and resize image
|
||||
$image = new rcube_image($_FILES['_photo']['tmp_name']);
|
||||
$imageprop = $image->props();
|
||||
|
||||
if (
|
||||
in_array(strtolower($imageprop['type']), self::$IMAGE_TYPES)
|
||||
&& $imageprop['width']
|
||||
&& $imageprop['height']
|
||||
) {
|
||||
$maxsize = intval($rcmail->config->get('contact_photo_size', 160));
|
||||
$tmpfname = rcube_utils::temp_filename('imgconvert');
|
||||
$save_hook = 'attachment_upload';
|
||||
|
||||
// scale image to a maximum size
|
||||
if (($imageprop['width'] > $maxsize || $imageprop['height'] > $maxsize) && $image->resize($maxsize, $tmpfname)) {
|
||||
$filepath = $tmpfname;
|
||||
$save_hook = 'attachment_save';
|
||||
}
|
||||
|
||||
// save uploaded file in storage backend
|
||||
$attachment = $rcmail->plugins->exec_hook($save_hook, [
|
||||
'path' => $filepath,
|
||||
'size' => $_FILES['_photo']['size'],
|
||||
'name' => $_FILES['_photo']['name'],
|
||||
'mimetype' => 'image/' . $imageprop['type'],
|
||||
'group' => 'contact',
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$attachment = ['error' => $rcmail->gettext('invalidimageformat')];
|
||||
}
|
||||
|
||||
if (!empty($attachment['status']) && empty($attachment['abort'])) {
|
||||
$file_id = $attachment['id'];
|
||||
$_SESSION['contacts']['files'][$file_id] = $attachment;
|
||||
$rcmail->output->command('replace_contact_photo', $file_id);
|
||||
}
|
||||
else {
|
||||
// upload failed
|
||||
self::upload_error($_FILES['_photo']['error'], $attachment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::upload_failure();
|
||||
}
|
||||
|
||||
$rcmail->output->command('photo_upload_end');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Perform OAuth2 user login |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_login_oauth extends rcmail_action
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$auth_code = rcube_utils::get_input_string('code', rcube_utils::INPUT_GET);
|
||||
$auth_error = rcube_utils::get_input_string('error', rcube_utils::INPUT_GET);
|
||||
$auth_state = rcube_utils::get_input_string('state', rcube_utils::INPUT_GET);
|
||||
|
||||
// auth code return from oauth login
|
||||
if (!empty($auth_code)) {
|
||||
$auth = $rcmail->oauth->request_access_token($auth_code, $auth_state);
|
||||
|
||||
// oauth success
|
||||
if ($auth && isset($auth['username'], $auth['authorization'], $auth['token'])) {
|
||||
// enforce XOAUTH2 auth type
|
||||
$rcmail->config->set('imap_auth_type', 'XOAUTH2');
|
||||
$rcmail->config->set('login_password_maxlen', strlen($auth['authorization']));
|
||||
|
||||
// use access_token and user info for IMAP login
|
||||
$storage_host = $rcmail->autoselect_host();
|
||||
if ($rcmail->login($auth['username'], $auth['authorization'], $storage_host, true)) {
|
||||
// replicate post-login tasks from index.php
|
||||
$rcmail->session->remove('temp');
|
||||
$rcmail->session->regenerate_id(false);
|
||||
|
||||
// send auth cookie if necessary
|
||||
$rcmail->session->set_auth_cookie();
|
||||
|
||||
// save OAuth token in session
|
||||
$_SESSION['oauth_token'] = $auth['token'];
|
||||
|
||||
// log successful login
|
||||
$rcmail->log_login();
|
||||
|
||||
// allow plugins to control the redirect url after login success
|
||||
$redir = $rcmail->plugins->exec_hook('login_after', ['_task' => 'mail']);
|
||||
unset($redir['abort'], $redir['_err']);
|
||||
|
||||
// send redirect
|
||||
header('Location: ' . $rcmail->url($redir, true, false));
|
||||
exit;
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('loginfailed', 'warning');
|
||||
|
||||
// log failed login
|
||||
$error_code = $rcmail->login_error();
|
||||
$rcmail->log_login($auth['username'], true, $error_code);
|
||||
|
||||
$rcmail->plugins->exec_hook('login_failed', [
|
||||
'code' => $error_code,
|
||||
'host' => $storage_host,
|
||||
'user' => $auth['username'],
|
||||
]);
|
||||
|
||||
$rcmail->kill_session();
|
||||
// fall through -> login page
|
||||
}
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('oauthloginfailed', 'warning');
|
||||
}
|
||||
}
|
||||
// error return from oauth login
|
||||
else if (!empty($auth_error)) {
|
||||
$error_message = rcube_utils::get_input_string('error_description', rcube_utils::INPUT_GET) ?: $auth_error;
|
||||
$rcmail->output->show_message($error_message, 'warning');
|
||||
}
|
||||
// login action: redirect to `oauth_auth_uri`
|
||||
else if ($rcmail->task === 'login') {
|
||||
// this will always exit() the process
|
||||
$rcmail->oauth->login_redirect();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Add the submitted contact to the user's address book |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_addcontact extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$address = rcube_utils::get_input_string('_address', rcube_utils::INPUT_POST, true);
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_POST);
|
||||
|
||||
// Get the default addressbook
|
||||
$CONTACTS = null;
|
||||
$SENDERS = null;
|
||||
$type = 0;
|
||||
|
||||
if ($source != rcube_addressbook::TYPE_TRUSTED_SENDER) {
|
||||
$CONTACTS = $rcmail->get_address_book(rcube_addressbook::TYPE_DEFAULT, true);
|
||||
$type = rcube_addressbook::TYPE_DEFAULT;
|
||||
}
|
||||
|
||||
// Get the trusted senders addressbook
|
||||
if (!empty($_POST['_reload']) || $source == rcube_addressbook::TYPE_TRUSTED_SENDER) {
|
||||
$collected_senders = $rcmail->config->get('collected_senders');
|
||||
|
||||
if (strlen($collected_senders)) {
|
||||
$type |= rcube_addressbook::TYPE_TRUSTED_SENDER;
|
||||
$SENDERS = $rcmail->get_address_book($collected_senders);
|
||||
if ($CONTACTS == $SENDERS) {
|
||||
$SENDERS = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$contact = rcube_mime::decode_address_list($address, 1, false);
|
||||
|
||||
if (empty($contact[1]['mailto'])) {
|
||||
$rcmail->output->show_message('errorsavingcontact', 'error', null, false);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$contact = [
|
||||
'email' => $contact[1]['mailto'],
|
||||
'name' => $contact[1]['name'],
|
||||
];
|
||||
|
||||
$email = rcube_utils::idn_to_ascii($contact['email']);
|
||||
|
||||
if (!rcube_utils::check_email($email, false)) {
|
||||
$rcmail->output->show_message('emailformaterror', 'error', ['email' => $contact['email']], false);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
if ($rcmail->contact_exists($contact['email'], $type)) {
|
||||
$rcmail->output->show_message('contactexists', 'warning');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$done = $rcmail->contact_create($contact, $SENDERS ?: $CONTACTS, $error);
|
||||
|
||||
if ($done) {
|
||||
$rcmail->output->show_message('addedsuccessfully', 'confirmation');
|
||||
|
||||
if (!empty($_POST['_reload'])) {
|
||||
$rcmail->output->command('command', 'load-remote');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message($error ?: 'errorsavingcontact', 'error', null, false);
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delete attachments from compose form |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_attachment_delete extends rcmail_action_mail_attachment_upload
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
self::init();
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$attachment = self::get_attachment();
|
||||
|
||||
if (is_array($attachment)) {
|
||||
$attachment = $rcmail->plugins->exec_hook('attachment_delete', $attachment);
|
||||
|
||||
if (!empty($attachment['status'])) {
|
||||
$rcmail->session->remove(self::$SESSION_KEY . '.attachments.' . self::$file_id);
|
||||
$rcmail->output->command('remove_from_attachment_list', 'rcmfile' . self::$file_id);
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Display attachments in compose form |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_attachment_display extends rcmail_action_mail_attachment_upload
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
self::init();
|
||||
self::display_uploaded_file(self::get_attachment());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Rename attachments in compose form |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_attachment_rename extends rcmail_action_mail_attachment_upload
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
self::init();
|
||||
|
||||
$filename = rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST);
|
||||
$filename = trim($filename);
|
||||
|
||||
if (
|
||||
strlen($filename)
|
||||
&& ($attachment = self::get_attachment())
|
||||
&& is_array($attachment)
|
||||
) {
|
||||
$attachment['name'] = $filename;
|
||||
|
||||
$rcmail->session->remove(self::$SESSION_KEY . '.attachments. ' . self::$file_id);
|
||||
$rcmail->session->append(self::$SESSION_KEY . '.attachments', $attachment['id'], $attachment);
|
||||
|
||||
$rcmail->output->command('rename_attachment_handler', 'rcmfile' . self::$file_id, $filename);
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Attachment uploads handler for the compose form |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_attachment_upload extends rcmail_action_mail_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
protected static $SESSION_KEY;
|
||||
protected static $COMPOSE;
|
||||
protected static $COMPOSE_ID;
|
||||
protected static $file_id;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
self::init();
|
||||
|
||||
// clear all stored output properties (like scripts and env vars)
|
||||
$rcmail->output->reset();
|
||||
|
||||
$uploadid = rcube_utils::get_input_string('_uploadid', rcube_utils::INPUT_GPC);
|
||||
$uri = rcube_utils::get_input_string('_uri', rcube_utils::INPUT_POST);
|
||||
|
||||
// handle dropping a reference to an attachment part of some message
|
||||
if ($uri) {
|
||||
$attachment = null;
|
||||
|
||||
$url = parse_url($uri);
|
||||
|
||||
if (!empty($url['query'])) {
|
||||
parse_str($url['query'], $params);
|
||||
}
|
||||
|
||||
if (
|
||||
!empty($params) && isset($params['_mbox']) && strlen($params['_mbox'])
|
||||
&& !empty($params['_uid']) && !empty($params['_part'])
|
||||
) {
|
||||
// @TODO: at some point we might support drag-n-drop between
|
||||
// two different accounts on the same server, for now make sure
|
||||
// this is the same server and the same user
|
||||
list($host, $port) = rcube_utils::explode(':', $_SERVER['HTTP_HOST']);
|
||||
|
||||
if (
|
||||
$host == $url['host']
|
||||
&& $port == $url['port']
|
||||
&& $rcmail->get_user_name() == rawurldecode($url['user'])
|
||||
) {
|
||||
$message = new rcube_message($params['_uid'], $params['_mbox']);
|
||||
|
||||
if ($message && !empty($message->headers)) {
|
||||
$attachment = rcmail_action_mail_compose::save_attachment($message, $params['_part'], self::$COMPOSE_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('attachment_from_uri', [
|
||||
'attachment' => $attachment,
|
||||
'uri' => $uri,
|
||||
'compose_id' => self::$COMPOSE_ID
|
||||
]);
|
||||
|
||||
if ($plugin['attachment']) {
|
||||
self::attachment_success($plugin['attachment'], $uploadid);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->command('display_message', $rcmail->gettext('filelinkerror'), 'error');
|
||||
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
// handle file(s) upload
|
||||
if (is_array($_FILES['_attachments']['tmp_name'])) {
|
||||
$multiple = count($_FILES['_attachments']['tmp_name']) > 1;
|
||||
$errors = [];
|
||||
|
||||
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
|
||||
// Process uploaded attachment if there is no error
|
||||
$err = $_FILES['_attachments']['error'][$i];
|
||||
|
||||
if (!$err) {
|
||||
$filename = $_FILES['_attachments']['name'][$i];
|
||||
$filesize = $_FILES['_attachments']['size'][$i];
|
||||
$filetype = rcube_mime::file_content_type($filepath, $filename, $_FILES['_attachments']['type'][$i]);
|
||||
|
||||
if ($err = self::check_message_size($filesize, $filetype)) {
|
||||
if (!in_array($err, $errors)) {
|
||||
$rcmail->output->command('display_message', $err, 'error');
|
||||
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
||||
$errors[] = $err;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$attachment = $rcmail->plugins->exec_hook('attachment_upload', [
|
||||
'path' => $filepath,
|
||||
'name' => $filename,
|
||||
'size' => $filesize,
|
||||
'mimetype' => $filetype,
|
||||
'group' => self::$COMPOSE_ID,
|
||||
]);
|
||||
}
|
||||
|
||||
if (!$err && !empty($attachment['status']) && empty($attachment['abort'])) {
|
||||
// store new attachment in session
|
||||
unset($attachment['status'], $attachment['abort']);
|
||||
$rcmail->session->append(self::$SESSION_KEY . '.attachments', $attachment['id'], $attachment);
|
||||
|
||||
self::attachment_success($attachment, $uploadid);
|
||||
}
|
||||
else { // upload failed
|
||||
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
|
||||
$size = self::show_bytes(rcube_utils::max_upload_size());
|
||||
$msg = $rcmail->gettext(['name' => 'filesizeerror', 'vars' => ['size' => $size]]);
|
||||
}
|
||||
else if (!empty($attachment['error'])) {
|
||||
$msg = $attachment['error'];
|
||||
}
|
||||
else {
|
||||
$msg = $rcmail->gettext('fileuploaderror');
|
||||
}
|
||||
|
||||
if (!empty($attachment['error']) || $err != UPLOAD_ERR_NO_FILE) {
|
||||
if (!in_array($msg, $errors)) {
|
||||
$rcmail->output->command('display_message', $msg, 'error');
|
||||
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
||||
$errors[] = $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (self::upload_failure()) {
|
||||
$rcmail->output->command('remove_from_attachment_list', $uploadid);
|
||||
}
|
||||
|
||||
// send html page with JS calls as response
|
||||
$rcmail->output->command('auto_save_start', false);
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
public static function init()
|
||||
{
|
||||
self::$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
|
||||
self::$COMPOSE = null;
|
||||
self::$SESSION_KEY = 'compose_data_' . self::$COMPOSE_ID;
|
||||
|
||||
if (self::$COMPOSE_ID && !empty($_SESSION[self::$SESSION_KEY])) {
|
||||
self::$COMPOSE =& $_SESSION[self::$SESSION_KEY];
|
||||
}
|
||||
|
||||
if (!self::$COMPOSE) {
|
||||
die("Invalid session var!");
|
||||
}
|
||||
|
||||
self::$file_id = rcube_utils::get_input_string('_file', rcube_utils::INPUT_GPC);
|
||||
self::$file_id = preg_replace('/^rcmfile/', '', self::$file_id) ?: 'unknown';
|
||||
}
|
||||
|
||||
public static function get_attachment()
|
||||
{
|
||||
return self::$COMPOSE['attachments'][self::$file_id];
|
||||
}
|
||||
|
||||
public static function attachment_success($attachment, $uploadid)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$id = $attachment['id'];
|
||||
|
||||
if (!empty(self::$COMPOSE['deleteicon']) && is_file(self::$COMPOSE['deleteicon'])) {
|
||||
$button = html::img([
|
||||
'src' => self::$COMPOSE['deleteicon'],
|
||||
'alt' => $rcmail->gettext('delete')
|
||||
]);
|
||||
}
|
||||
else if (!empty(self::$COMPOSE['textbuttons'])) {
|
||||
$button = rcube::Q($rcmail->gettext('delete'));
|
||||
}
|
||||
else {
|
||||
$button = '';
|
||||
}
|
||||
|
||||
$link_content = sprintf(
|
||||
'<span class="attachment-name">%s</span><span class="attachment-size">(%s)</span>',
|
||||
rcube::Q($attachment['name']), self::show_bytes($attachment['size'])
|
||||
);
|
||||
|
||||
$content_link = html::a([
|
||||
'href' => "#load",
|
||||
'class' => 'filename',
|
||||
'onclick' => sprintf(
|
||||
"return %s.command('load-attachment','rcmfile%s', this, event)",
|
||||
rcmail_output::JS_OBJECT_NAME,
|
||||
$id
|
||||
),
|
||||
], $link_content);
|
||||
|
||||
$delete_link = html::a([
|
||||
'href' => "#delete",
|
||||
'onclick' => sprintf(
|
||||
"return %s.command('remove-attachment','rcmfile%s', this, event)",
|
||||
rcmail_output::JS_OBJECT_NAME,
|
||||
$id
|
||||
),
|
||||
'title' => $rcmail->gettext('delete'),
|
||||
'class' => 'delete',
|
||||
'aria-label' => $rcmail->gettext('delete') . ' ' . $attachment['name'],
|
||||
], $button);
|
||||
|
||||
if (!empty(self::$COMPOSE['icon_pos']) && self::$COMPOSE['icon_pos'] == 'left') {
|
||||
$content = $delete_link . $content_link;
|
||||
}
|
||||
else {
|
||||
$content = $content_link . $delete_link;
|
||||
}
|
||||
|
||||
$rcmail->output->command('add2attachment_list', "rcmfile$id", [
|
||||
'html' => $content,
|
||||
'name' => $attachment['name'],
|
||||
'mimetype' => $attachment['mimetype'],
|
||||
'classname' => rcube_utils::file2class($attachment['mimetype'], $attachment['name']),
|
||||
'complete' => true
|
||||
],
|
||||
$uploadid
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the attached file will fit in message size limit.
|
||||
* Calculates size of all attachments and compares with the limit.
|
||||
*
|
||||
* @param int $filesize File size
|
||||
* @param string $filetype File mimetype
|
||||
*
|
||||
* @return string Error message if the limit is exceeded
|
||||
*/
|
||||
public static function check_message_size($filesize, $filetype)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$limit = parse_bytes($rcmail->config->get('max_message_size'));
|
||||
$size = 10 * 1024; // size of message body
|
||||
|
||||
if (!$limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// add size of already attached files
|
||||
if (!empty(self::$COMPOSE['attachments'])) {
|
||||
foreach ((array) self::$COMPOSE['attachments'] as $att) {
|
||||
// All attachments are base64-encoded except message/rfc822 (see sendmail.inc)
|
||||
$multip = $att['mimetype'] == 'message/rfc822' ? 1 : 1.33;
|
||||
$size += $att['size'] * $multip;
|
||||
}
|
||||
}
|
||||
|
||||
// add size of the new attachment
|
||||
$multip = $filetype == 'message/rfc822' ? 1 : 1.33;
|
||||
$size += $filesize * $multip;
|
||||
|
||||
if ($size > $limit) {
|
||||
$limit = self::show_bytes($limit);
|
||||
return $rcmail->gettext(['name' => 'msgsizeerror', 'vars' => ['size' => $limit]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Perform a search on configured address books for the email |
|
||||
| address autocompletion |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_autocomplete extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$MAXNUM = (int) $rcmail->config->get('autocomplete_max', 15);
|
||||
$mode = (int) $rcmail->config->get('addressbook_search_mode');
|
||||
$single = (bool) $rcmail->config->get('autocomplete_single');
|
||||
$search = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC, true);
|
||||
$reqid = rcube_utils::get_input_string('_reqid', rcube_utils::INPUT_GPC);
|
||||
|
||||
$contacts = [];
|
||||
|
||||
if (strlen($search) && ($book_types = self::autocomplete_addressbooks())) {
|
||||
$sort_keys = [];
|
||||
$books_num = count($book_types);
|
||||
$search_lc = mb_strtolower($search);
|
||||
$mode |= rcube_addressbook::SEARCH_GROUPS;
|
||||
$fields = $rcmail->config->get('contactlist_fields');
|
||||
|
||||
foreach ($book_types as $abook_id) {
|
||||
$abook = $rcmail->get_address_book($abook_id);
|
||||
$abook->set_pagesize($MAXNUM);
|
||||
|
||||
if ($result = $abook->search($fields, $search, $mode, true, true, 'email')) {
|
||||
while ($record = $result->iterate()) {
|
||||
// Contact can have more than one e-mail address
|
||||
$email_arr = (array) $abook->get_col_values('email', $record, true);
|
||||
$email_cnt = count($email_arr);
|
||||
$idx = 0;
|
||||
|
||||
foreach ($email_arr as $email) {
|
||||
if (empty($email)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = rcube_addressbook::compose_list_name($record);
|
||||
$contact = format_email_recipient($email, $name);
|
||||
|
||||
// skip entries that don't match
|
||||
if ($email_cnt > 1 && strpos(mb_strtolower($contact), $search_lc) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$index = $contact;
|
||||
|
||||
// skip duplicates
|
||||
if (empty($contacts[$index])) {
|
||||
$contact = [
|
||||
'name' => $contact,
|
||||
'type' => $record['_type'] ?? null,
|
||||
'id' => $record['ID'],
|
||||
'source' => $abook_id,
|
||||
];
|
||||
|
||||
$display = rcube_addressbook::compose_search_name($record, $email, $name);
|
||||
|
||||
if ($display && $display != $contact['name']) {
|
||||
$contact['display'] = $display;
|
||||
}
|
||||
|
||||
// groups with defined email address will not be expanded to its members' addresses
|
||||
if ($contact['type'] == 'group') {
|
||||
$contact['email'] = $email;
|
||||
}
|
||||
|
||||
$name = !empty($contact['display']) ? $contact['display'] : $name;
|
||||
$contacts[$index] = $contact;
|
||||
$sort_keys[$index] = sprintf('%s %03d', $name, $idx++);
|
||||
|
||||
if (count($contacts) >= $MAXNUM) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
|
||||
// skip redundant entries (show only first email address)
|
||||
if ($single) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// also list matching contact groups
|
||||
if ($abook->groups && count($contacts) < $MAXNUM) {
|
||||
foreach ($abook->list_groups($search, $mode) as $group) {
|
||||
$abook->reset();
|
||||
$abook->set_group($group['ID']);
|
||||
|
||||
$group_prop = $abook->get_group($group['ID']);
|
||||
|
||||
// group (distribution list) with email address(es)
|
||||
if (!empty($group_prop['email'])) {
|
||||
$idx = 0;
|
||||
foreach ((array) $group_prop['email'] as $email) {
|
||||
$index = format_email_recipient($email, $group['name']);
|
||||
|
||||
if (empty($contacts[$index])) {
|
||||
$sort_keys[$index] = sprintf('%s %03d', $group['name'] , $idx++);
|
||||
$contacts[$index] = [
|
||||
'name' => $index,
|
||||
'email' => $email,
|
||||
'type' => 'group',
|
||||
'id' => $group['ID'],
|
||||
'source' => $abook_id,
|
||||
];
|
||||
|
||||
if (count($contacts) >= $MAXNUM) {
|
||||
break 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// show group with count
|
||||
else if (($result = $abook->count()) && $result->count) {
|
||||
if (empty($contacts[$group['name']])) {
|
||||
$sort_keys[$group['name']] = $group['name'];
|
||||
$contacts[$group['name']] = [
|
||||
'name' => $group['name'] . ' (' . intval($result->count) . ')',
|
||||
'type' => 'group',
|
||||
'id' => $group['ID'],
|
||||
'source' => $abook_id,
|
||||
];
|
||||
|
||||
if (count($contacts) >= $MAXNUM) {
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($contacts)) {
|
||||
// sort contacts index
|
||||
asort($sort_keys, SORT_LOCALE_STRING);
|
||||
// re-sort contacts according to index
|
||||
foreach ($sort_keys as $idx => $val) {
|
||||
$sort_keys[$idx] = $contacts[$idx];
|
||||
}
|
||||
$contacts = array_values($sort_keys);
|
||||
}
|
||||
}
|
||||
|
||||
// Allow autocomplete result optimization via plugin
|
||||
$plugin = $rcmail->plugins->exec_hook('contacts_autocomplete_after', [
|
||||
'search' => $search,
|
||||
// Provide already-found contacts to plugin if they are required
|
||||
'contacts' => $contacts,
|
||||
]);
|
||||
|
||||
$contacts = $plugin['contacts'];
|
||||
|
||||
$rcmail->output->command('ksearch_query_results', $contacts, $search, $reqid);
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect addressbook sources used for autocompletion
|
||||
*/
|
||||
public static function autocomplete_addressbooks()
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
|
||||
if (strlen($source)) {
|
||||
$book_types = [$source];
|
||||
}
|
||||
else {
|
||||
$book_types = (array) $rcmail->config->get('autocomplete_addressbooks', 'sql');
|
||||
}
|
||||
|
||||
$collected_recipients = $rcmail->config->get('collected_recipients');
|
||||
$collected_senders = $rcmail->config->get('collected_senders');
|
||||
|
||||
if (strlen($collected_recipients) && !in_array($collected_recipients, $book_types)) {
|
||||
$book_types[] = $collected_recipients;
|
||||
}
|
||||
|
||||
if (strlen($collected_senders) && !in_array($collected_senders, $book_types)) {
|
||||
$book_types[] = $collected_senders;
|
||||
}
|
||||
|
||||
return !empty($book_types) ? $book_types : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Bounce/resend an email message |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_bounce extends rcmail_action
|
||||
{
|
||||
protected static $MESSAGE;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$msg_uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GP);
|
||||
$msg_folder = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_GP, true);
|
||||
$MESSAGE = new rcube_message($msg_uid, $msg_folder);
|
||||
|
||||
self::$MESSAGE = $MESSAGE;
|
||||
|
||||
if (!$MESSAGE->headers) {
|
||||
$rcmail->output->show_message('messageopenerror', 'error');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
// Display Bounce form
|
||||
if (empty($_POST)) {
|
||||
if (!empty($MESSAGE->headers->charset)) {
|
||||
$rcmail->storage->set_charset($MESSAGE->headers->charset);
|
||||
}
|
||||
|
||||
// Initialize helper class to build the UI
|
||||
$SENDMAIL = new rcmail_sendmail(
|
||||
['mode' => rcmail_sendmail::MODE_FORWARD],
|
||||
['message' => $MESSAGE]
|
||||
);
|
||||
|
||||
$rcmail->output->set_env('mailbox', $msg_folder);
|
||||
$rcmail->output->set_env('uid', $msg_uid);
|
||||
$rcmail->output->add_handler('bounceobjects', [$this, 'bounce_objects']);
|
||||
$rcmail->output->send('bounce');
|
||||
}
|
||||
|
||||
// Initialize helper class to send the message
|
||||
$SENDMAIL = new rcmail_sendmail(
|
||||
['mode' => rcmail_sendmail::MODE_FORWARD],
|
||||
[
|
||||
'sendmail' => true,
|
||||
'error_handler' => function(...$args) use ($rcmail) {
|
||||
call_user_func_array([$rcmail->output, 'show_message'], $args);
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
]
|
||||
);
|
||||
|
||||
// Handle the form input
|
||||
$input_headers = $SENDMAIL->headers_input();
|
||||
|
||||
// Set Resent-* headers, these will be added on top of the bounced message
|
||||
$headers = [];
|
||||
foreach (['From', 'To', 'Cc', 'Bcc', 'Date', 'Message-ID'] as $name) {
|
||||
if (!empty($input_headers[$name])) {
|
||||
$headers['Resent-' . $name] = $input_headers[$name];
|
||||
}
|
||||
}
|
||||
|
||||
// Create the bounce message
|
||||
$BOUNCE = new rcmail_resend_mail([
|
||||
'bounce_message' => $MESSAGE,
|
||||
'bounce_headers' => $headers,
|
||||
]);
|
||||
|
||||
// Send the bounce message
|
||||
$SENDMAIL->deliver_message($BOUNCE);
|
||||
|
||||
// Save in Sent (if requested)
|
||||
$saved = $SENDMAIL->save_message($BOUNCE);
|
||||
|
||||
if (!$saved && strlen($SENDMAIL->options['store_target'])) {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
|
||||
$rcmail->output->show_message('messagesent', 'confirmation', null, false);
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for template object 'bounceObjects'
|
||||
*
|
||||
* @param array $attrib HTML attributes
|
||||
*
|
||||
* @return string HTML content
|
||||
*/
|
||||
public static function bounce_objects($attrib)
|
||||
{
|
||||
if (empty($attrib['id'])) {
|
||||
$attrib['id'] = 'bounce-objects';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$content = [];
|
||||
|
||||
// Always display a hint about the bounce feature behavior
|
||||
$msg = html::span(null, rcube::Q($rcmail->gettext('bouncehint')));
|
||||
$msg_attrib = ['id' => 'bounce-hint', 'class' => 'boxinformation'];
|
||||
$content[] = html::div($msg_attrib, $msg);
|
||||
|
||||
// Add a warning about Bcc recipients
|
||||
if (self::$MESSAGE->headers->get('bcc', false) || self::$MESSAGE->headers->get('resent-bcc', false)) {
|
||||
$msg = html::span(null, rcube::Q($rcmail->gettext('bccemail')));
|
||||
$msg_attrib = ['id' => 'bcc-warning', 'class' => 'boxwarning'];
|
||||
$content[] = html::div($msg_attrib, $msg);
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('bounce_objects',
|
||||
['content' => $content, 'message' => self::$MESSAGE]);
|
||||
|
||||
$content = implode("\n", $plugin['content']);
|
||||
|
||||
return $content ? html::div($attrib, $content) : '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Check for recent messages, in all mailboxes |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_check_recent extends rcmail_action_mail_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// If there's no folder or messages list, there's nothing to update
|
||||
// This can happen on 'refresh' request
|
||||
if (empty($_POST['_folderlist']) && empty($_POST['_list'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trash = $rcmail->config->get('trash_mbox');
|
||||
$current = $rcmail->storage->get_folder();
|
||||
$check_all = $rcmail->action != 'refresh' || (bool) $rcmail->config->get('check_all_folders');
|
||||
$page = $rcmail->storage->get_page();
|
||||
$page_size = $rcmail->storage->get_pagesize();
|
||||
|
||||
$search_request = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC);
|
||||
if ($search_request && $_SESSION['search_request'] != $search_request) {
|
||||
$search_request = null;
|
||||
}
|
||||
|
||||
// list of folders to check
|
||||
if ($check_all) {
|
||||
$a_mailboxes = $rcmail->storage->list_folders_subscribed('', '*', 'mail');
|
||||
}
|
||||
else if ($search_request && isset($_SESSION['search'][1]) && is_object($_SESSION['search'][1])) {
|
||||
$a_mailboxes = (array) $_SESSION['search'][1]->get_parameters('MAILBOX');
|
||||
}
|
||||
else {
|
||||
$a_mailboxes = (array) $current;
|
||||
if ($current != 'INBOX') {
|
||||
$a_mailboxes[] = 'INBOX';
|
||||
}
|
||||
}
|
||||
|
||||
// Control folders list from a plugin
|
||||
$plugin = $rcmail->plugins->exec_hook('check_recent', ['folders' => $a_mailboxes, 'all' => $check_all]);
|
||||
$a_mailboxes = $plugin['folders'];
|
||||
$list_cleared = false;
|
||||
|
||||
self::storage_fatal_error();
|
||||
|
||||
// check recent/unseen counts
|
||||
foreach ($a_mailboxes as $mbox_name) {
|
||||
$is_current = $mbox_name == $current
|
||||
|| (
|
||||
!empty($search_request)
|
||||
&& isset($_SESSION['search'][1])
|
||||
&& is_object($_SESSION['search'][1])
|
||||
&& in_array($mbox_name, (array)$_SESSION['search'][1]->get_parameters('MAILBOX'))
|
||||
);
|
||||
|
||||
if ($is_current) {
|
||||
// Synchronize mailbox cache, handle flag changes
|
||||
$rcmail->storage->folder_sync($mbox_name);
|
||||
}
|
||||
|
||||
// Get mailbox status
|
||||
$status = $rcmail->storage->folder_status($mbox_name, $diff);
|
||||
|
||||
if ($is_current) {
|
||||
self::storage_fatal_error();
|
||||
}
|
||||
|
||||
if ($status & 1) {
|
||||
// trigger plugin hook
|
||||
$rcmail->plugins->exec_hook('new_messages', [
|
||||
'mailbox' => $mbox_name,
|
||||
'is_current' => $is_current,
|
||||
'diff' => $diff
|
||||
]);
|
||||
}
|
||||
|
||||
self::send_unread_count($mbox_name, true, null, (!$is_current && ($status & 1)) ? 'recent' : '');
|
||||
|
||||
if ($status && $is_current) {
|
||||
// refresh saved search set
|
||||
if (!empty($search_request) && isset($_SESSION['search'])) {
|
||||
unset($search_request); // only do this once
|
||||
$_SESSION['search'] = $rcmail->storage->refresh_search();
|
||||
if (!empty($_SESSION['search'][1]->multi)) {
|
||||
$mbox_name = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_POST['_quota'])) {
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $mbox_name));
|
||||
}
|
||||
|
||||
$rcmail->output->set_env('exists', $rcmail->storage->count($mbox_name, 'EXISTS', true));
|
||||
|
||||
// "No-list" mode, don't get messages
|
||||
if (empty($_POST['_list'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get overall message count; allow caching because rcube_storage::folder_status()
|
||||
// did a refresh but only in list mode
|
||||
$list_mode = $rcmail->storage->get_threading() ? 'THREADS' : 'ALL';
|
||||
$all_count = $rcmail->storage->count($mbox_name, $list_mode, $list_mode == 'THREADS', false);
|
||||
|
||||
// check current page if we're not on the first page
|
||||
if ($all_count && $page > 1) {
|
||||
$remaining = $all_count - $page_size * ($page - 1);
|
||||
if ($remaining <= 0) {
|
||||
$page -= 1;
|
||||
$rcmail->storage->set_page($page);
|
||||
$_SESSION['page'] = $page;
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->set_env('messagecount', $all_count);
|
||||
$rcmail->output->set_env('pagecount', ceil($all_count/$page_size));
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($all_count), $mbox_name);
|
||||
$rcmail->output->set_env('current_page', $all_count ? $page : 1);
|
||||
|
||||
// remove old rows (and clear selection if new list is empty)
|
||||
$rcmail->output->command('message_list.clear', $all_count ? false : true);
|
||||
|
||||
if ($all_count) {
|
||||
$a_headers = $rcmail->storage->list_messages($mbox_name, null, self::sort_column(), self::sort_order());
|
||||
// add message rows
|
||||
self::js_message_list($a_headers, false);
|
||||
// remove messages that don't exists from list selection array
|
||||
$rcmail->output->command('update_selection');
|
||||
}
|
||||
|
||||
$list_cleared = true;
|
||||
}
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox_name === $trash) {
|
||||
$rcmail->output->command('set_trash_count', $rcmail->storage->count($mbox_name, 'EXISTS', true));
|
||||
}
|
||||
}
|
||||
|
||||
// handle flag updates
|
||||
if (!$list_cleared) {
|
||||
$uids = rcube_utils::get_input_value('_uids', rcube_utils::INPUT_POST);
|
||||
$uids = self::get_uids($uids, null, $multifolder);
|
||||
|
||||
$recent_flags = [];
|
||||
|
||||
foreach ($uids as $mbox_name => $set) {
|
||||
$get_flags = true;
|
||||
$modseq = null;
|
||||
|
||||
if ($mbox_name == $current) {
|
||||
$data = $rcmail->storage->folder_data($mbox_name);
|
||||
$modseq = !empty($_SESSION['list_mod_seq']) ? $_SESSION['list_mod_seq'] : null;
|
||||
$get_flags = empty($modseq) || empty($data['HIGHESTMODSEQ']) || $modseq != $data['HIGHESTMODSEQ'];
|
||||
|
||||
// remember last HIGHESTMODSEQ value (if supported)
|
||||
if (!empty($data['HIGHESTMODSEQ'])) {
|
||||
$_SESSION['list_mod_seq'] = $data['HIGHESTMODSEQ'];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider HIGHESTMODSEQ for all folders in multifolder search, otherwise
|
||||
// flags for all messages in a set are requested on every refresh
|
||||
|
||||
if ($get_flags) {
|
||||
$flags = $rcmail->storage->list_flags($mbox_name, $set, $modseq);
|
||||
|
||||
foreach ($flags as $idx => $row) {
|
||||
if ($multifolder) {
|
||||
$idx .= '-' . $mbox_name;
|
||||
}
|
||||
$recent_flags[$idx] = array_change_key_case(array_map('intval', $row));
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->set_env('recent_flags', $recent_flags);
|
||||
}
|
||||
}
|
||||
|
||||
// trigger refresh hook
|
||||
$rcmail->plugins->exec_hook('refresh', []);
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Copy the submitted messages to a specific mailbox |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_copy extends rcmail_action_mail_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// copy messages
|
||||
if (empty($_POST['_uid']) || !isset($_POST['_target_mbox']) || !strlen($_POST['_target_mbox'])) {
|
||||
$rcmail->output->show_message('internalerror', 'error');
|
||||
}
|
||||
|
||||
$uids = self::get_uids(null, null, $multifolder, rcube_utils::INPUT_POST);
|
||||
$target = rcube_utils::get_input_string('_target_mbox', rcube_utils::INPUT_POST, true);
|
||||
$sources = [];
|
||||
$copied = false;
|
||||
|
||||
foreach ($uids as $mbox => $uids) {
|
||||
if ($mbox === $target) {
|
||||
$copied++;
|
||||
}
|
||||
else {
|
||||
$copied += (int) $rcmail->storage->copy_message($uids, $target, $mbox);
|
||||
$sources[] = $mbox;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$copied) {
|
||||
self::display_server_error('errorcopying');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('messagecopied', 'confirmation');
|
||||
|
||||
self::send_unread_count($target, true);
|
||||
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX'));
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handler for mail delete operation |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_delete extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// count messages before changing anything
|
||||
$threading = (bool) $rcmail->storage->get_threading();
|
||||
$trash = $rcmail->config->get('trash_mbox');
|
||||
$sources = [];
|
||||
$old_count = 0;
|
||||
$deleted = 0;
|
||||
$count = 0;
|
||||
|
||||
if (empty($_POST['_from']) || $_POST['_from'] != 'show') {
|
||||
$old_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
|
||||
}
|
||||
|
||||
if (empty($_POST['_uid'])) {
|
||||
$rcmail->output->show_message('internalerror', 'error');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
foreach (rcmail::get_uids(null, null, $multifolder, rcube_utils::INPUT_POST) as $mbox => $uids) {
|
||||
$deleted += (int) $rcmail->storage->delete_message($uids, $mbox);
|
||||
$count += is_array($uids) ? count($uids) : 1;
|
||||
$sources[] = $mbox;
|
||||
}
|
||||
|
||||
if (empty($deleted)) {
|
||||
// send error message
|
||||
if ($_POST['_from'] != 'show') {
|
||||
$rcmail->output->command('list_mailbox');
|
||||
}
|
||||
|
||||
self::display_server_error('errordeleting');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('messagedeleted', 'confirmation');
|
||||
}
|
||||
|
||||
$search_request = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC);
|
||||
|
||||
// refresh saved search set after moving some messages
|
||||
if ($search_request && $rcmail->storage->get_search_set()) {
|
||||
$_SESSION['search'] = $rcmail->storage->refresh_search();
|
||||
}
|
||||
|
||||
if (!empty($_POST['_from']) && $_POST['_from'] == 'show') {
|
||||
if ($next = rcube_utils::get_input_string('_next_uid', rcube_utils::INPUT_GPC)) {
|
||||
$rcmail->output->command('show_message', $next);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->command('command', 'list');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$mbox = $rcmail->storage->get_folder();
|
||||
$msg_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
|
||||
$exists = $rcmail->storage->count($mbox, 'EXISTS', true);
|
||||
$page_size = $rcmail->storage->get_pagesize();
|
||||
$page = $rcmail->storage->get_page();
|
||||
$pages = ceil($msg_count / $page_size);
|
||||
$nextpage_count = $old_count - $page_size * $page;
|
||||
$remaining = $msg_count - $page_size * ($page - 1);
|
||||
$jump_back = false;
|
||||
|
||||
// jump back one page (user removed the whole last page)
|
||||
if ($page > 1 && $remaining == 0) {
|
||||
$page -= 1;
|
||||
$rcmail->storage->set_page($page);
|
||||
$_SESSION['page'] = $page;
|
||||
$jump_back = true;
|
||||
}
|
||||
|
||||
// update unseen messages counts for all involved folders
|
||||
foreach ($sources as $source) {
|
||||
self::send_unread_count($source, true);
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('messagecount', $msg_count);
|
||||
$rcmail->output->set_env('current_page', $page);
|
||||
$rcmail->output->set_env('pagecount', $pages);
|
||||
$rcmail->output->set_env('exists', $exists);
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX'));
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox);
|
||||
|
||||
if ($threading) {
|
||||
$count = rcube_utils::get_input_string('_count', rcube_utils::INPUT_POST);
|
||||
}
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if (!empty($count) && $_POST['_uid'] != '*' && ($jump_back || $nextpage_count > 0)) {
|
||||
// #5862: Don't add more rows than it was on the next page
|
||||
$count = $jump_back ? null : min($nextpage_count, $count);
|
||||
|
||||
$a_headers = $rcmail->storage->list_messages($mbox, null, self::sort_column(), self::sort_order(), $count);
|
||||
|
||||
self::js_message_list($a_headers, false);
|
||||
}
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox === $trash) {
|
||||
$rcmail->output->command('set_trash_count', $exists);
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Implement folder EXPUNGE request |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_folder_expunge extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
$success = $rcmail->storage->expunge_folder($mbox);
|
||||
|
||||
// reload message list if current mailbox
|
||||
if ($success) {
|
||||
$rcmail->output->show_message('folderexpunged', 'confirmation');
|
||||
|
||||
if (!empty($_REQUEST['_reload'])) {
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $mbox));
|
||||
$rcmail->output->command('message_list.clear');
|
||||
$rcmail->action = 'list';
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::display_server_error();
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Implement folder PURGE request |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_folder_purge extends rcmail_action_mail_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$delimiter = $storage->get_hierarchy_delimiter();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
$trash_mbox = $rcmail->config->get('trash_mbox');
|
||||
$trash_regexp = '/^' . preg_quote($trash_mbox . $delimiter, '/') . '/';
|
||||
|
||||
// we should only be purging trash (or their subfolders)
|
||||
if (!strlen($trash_mbox) || $mbox === $trash_mbox || preg_match($trash_regexp, $mbox)) {
|
||||
$success = $storage->delete_message('*', $mbox);
|
||||
$delete = true;
|
||||
}
|
||||
// move to Trash
|
||||
else {
|
||||
$success = $storage->move_message('1:*', $trash_mbox, $mbox);
|
||||
$delete = false;
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
$rcmail->output->show_message('folderpurged', 'confirmation');
|
||||
$rcmail->output->command('set_unread_count', $mbox, 0);
|
||||
self::set_unseen_count($mbox, 0);
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox === $trash_mbox) {
|
||||
$rcmail->output->command('set_trash_count', 0);
|
||||
}
|
||||
else if (strlen($trash_mbox)) {
|
||||
$rcmail->output->command('set_trash_count', $rcmail->storage->count($trash_mbox, 'EXISTS'));
|
||||
}
|
||||
|
||||
if (!$delete && strlen($trash_mbox)) {
|
||||
self::send_unread_count($trash_mbox, true);
|
||||
}
|
||||
|
||||
if (!empty($_REQUEST['_reload'])) {
|
||||
$rcmail->output->set_env('messagecount', 0);
|
||||
$rcmail->output->set_env('pagecount', 0);
|
||||
$rcmail->output->set_env('exists', 0);
|
||||
$rcmail->output->command('message_list.clear');
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text(), $mbox);
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $mbox));
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::display_server_error();
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Delivering a specific uploaded file or mail message attachment |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_get extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $attachment;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// This resets X-Frame-Options for framed output (#6688)
|
||||
$rcmail->output->page_headers();
|
||||
|
||||
// show loading page
|
||||
if (!empty($_GET['_preload'])) {
|
||||
unset($_GET['_preload']);
|
||||
unset($_GET['_safe']);
|
||||
|
||||
$url = $rcmail->url($_GET + ['_mimewarning' => 1, '_embed' => 1]);
|
||||
$message = $rcmail->gettext('loadingdata');
|
||||
|
||||
header('Content-Type: text/html; charset=' . RCUBE_CHARSET);
|
||||
print "<html>\n<head>\n"
|
||||
. '<meta http-equiv="refresh" content="0; url='.rcube::Q($url).'">' . "\n"
|
||||
. '<meta http-equiv="content-type" content="text/html; charset=' . RCUBE_CHARSET . '">' . "\n"
|
||||
. "</head>\n<body>\n$message\n</body>\n</html>";
|
||||
exit;
|
||||
}
|
||||
|
||||
$attachment = new rcmail_attachment_handler;
|
||||
$mimetype = $attachment->mimetype;
|
||||
$filename = $attachment->filename;
|
||||
|
||||
self::$attachment = $attachment;
|
||||
|
||||
// show part page
|
||||
if (!empty($_GET['_frame'])) {
|
||||
$rcmail->output->set_pagetitle($filename);
|
||||
|
||||
// register UI objects
|
||||
$rcmail->output->add_handlers([
|
||||
'messagepartframe' => [$this, 'message_part_frame'],
|
||||
'messagepartcontrols' => [$this, 'message_part_controls'],
|
||||
]);
|
||||
|
||||
$part_id = rcube_utils::get_input_string('_part', rcube_utils::INPUT_GET);
|
||||
$uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET);
|
||||
|
||||
// message/rfc822 preview (Note: handle also multipart/ parts, they can
|
||||
// come from Enigma, which replaces message/rfc822 with real mimetype)
|
||||
if ($part_id && ($mimetype == 'message/rfc822' || strpos($mimetype, 'multipart/') === 0)) {
|
||||
$uid = preg_replace('/\.[0-9.]+/', '', $uid);
|
||||
$uid .= '.' . $part_id;
|
||||
|
||||
$rcmail->output->set_env('is_message', true);
|
||||
}
|
||||
|
||||
$rcmail->output->set_env('mailbox', $rcmail->storage->get_folder());
|
||||
$rcmail->output->set_env('uid', $uid);
|
||||
$rcmail->output->set_env('part', $part_id);
|
||||
$rcmail->output->set_env('filename', $filename);
|
||||
$rcmail->output->set_env('mimetype', $mimetype);
|
||||
|
||||
$rcmail->output->send('messagepart');
|
||||
}
|
||||
|
||||
// render thumbnail of an image attachment
|
||||
if (!empty($_GET['_thumb']) && $attachment->is_valid()) {
|
||||
$thumbnail_size = $rcmail->config->get('image_thumbnail_size', 240);
|
||||
$file_ident = $attachment->ident;
|
||||
$thumb_name = 'thumb' . md5($file_ident . ':' . $rcmail->user->ID . ':' . $thumbnail_size);
|
||||
$cache_file = rcube_utils::temp_filename($thumb_name, false, false);
|
||||
|
||||
// render thumbnail image if not done yet
|
||||
if (!is_file($cache_file) && $attachment->body_to_file($orig_name = rcube_utils::temp_filename('attmnt'))) {
|
||||
$image = new rcube_image($orig_name);
|
||||
|
||||
if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
|
||||
$mimetype = 'image/' . $imgtype;
|
||||
}
|
||||
else {
|
||||
// Resize failed, we need to check the file mimetype
|
||||
// So, we do not exit here, but goto generic file body handler below
|
||||
$_GET['_thumb'] = 0;
|
||||
$_REQUEST['_embed'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($_GET['_thumb'])) {
|
||||
if (is_file($cache_file)) {
|
||||
$rcmail->output->future_expire_header(3600);
|
||||
header('Content-Type: ' . $mimetype);
|
||||
header('Content-Length: ' . filesize($cache_file));
|
||||
readfile($cache_file);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle attachment body (display or download)
|
||||
if (empty($_GET['_thumb']) && $attachment->is_valid()) {
|
||||
// require CSRF protected url for downloads
|
||||
if (!empty($_GET['_download'])) {
|
||||
$rcmail->request_security_check(rcube_utils::INPUT_GET);
|
||||
}
|
||||
|
||||
$extensions = rcube_mime::get_mime_extensions($mimetype);
|
||||
|
||||
// compare file mimetype with the stated content-type headers and file extension to avoid malicious operations
|
||||
if (!empty($_REQUEST['_embed']) && empty($_REQUEST['_nocheck'])) {
|
||||
$file_extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
|
||||
// 1. compare filename suffix with expected suffix derived from mimetype
|
||||
$valid = $file_extension && in_array($file_extension, (array)$extensions)
|
||||
|| empty($extensions)
|
||||
|| !empty($_REQUEST['_mimeclass']);
|
||||
|
||||
// 2. detect the real mimetype of the attachment part and compare it with the stated mimetype and filename extension
|
||||
if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || stripos($mimetype, 'text/') === 0) {
|
||||
$tmp_body = $attachment->body(2048);
|
||||
|
||||
// detect message part mimetype
|
||||
$real_mimetype = rcube_mime::file_content_type($tmp_body, $filename, $mimetype, true, true);
|
||||
list($real_ctype_primary, $real_ctype_secondary) = explode('/', $real_mimetype);
|
||||
|
||||
// accept text/plain with any extension
|
||||
if ($real_mimetype == 'text/plain' && self::mimetype_compare($real_mimetype, $mimetype)) {
|
||||
$valid_extension = true;
|
||||
}
|
||||
// ignore differences in text/* mimetypes. Filetype detection isn't very reliable here
|
||||
else if ($real_ctype_primary == 'text' && strpos($mimetype, $real_ctype_primary) === 0) {
|
||||
$real_mimetype = $mimetype;
|
||||
$valid_extension = true;
|
||||
}
|
||||
// ignore filename extension if mimeclass matches (#1489029)
|
||||
else if (!empty($_REQUEST['_mimeclass']) && $real_ctype_primary == $_REQUEST['_mimeclass']) {
|
||||
$valid_extension = true;
|
||||
}
|
||||
else {
|
||||
// get valid file extensions
|
||||
$extensions = rcube_mime::get_mime_extensions($real_mimetype);
|
||||
$valid_extension = !$file_extension || empty($extensions) || in_array($file_extension, (array)$extensions);
|
||||
}
|
||||
|
||||
if (
|
||||
// fix mimetype for files wrongly declared as octet-stream
|
||||
($mimetype == 'application/octet-stream' && $valid_extension)
|
||||
// force detected mimetype for images (#8158)
|
||||
|| (strpos($real_mimetype, 'image/') === 0)
|
||||
) {
|
||||
$mimetype = $real_mimetype;
|
||||
}
|
||||
|
||||
// "fix" real mimetype the same way the original is before comparison
|
||||
$real_mimetype = rcube_mime::fix_mimetype($real_mimetype);
|
||||
|
||||
$valid = $valid_extension && self::mimetype_compare($real_mimetype, $mimetype);
|
||||
}
|
||||
else {
|
||||
$real_mimetype = $mimetype;
|
||||
}
|
||||
|
||||
// show warning if validity checks failed
|
||||
if (!$valid) {
|
||||
// send blocked.gif for expected images
|
||||
if (empty($_REQUEST['_mimewarning']) && strpos($mimetype, 'image/') === 0) {
|
||||
// Do not cache. Failure might be the result of a misconfiguration,
|
||||
// thus real content should be returned once fixed.
|
||||
$content = self::get_resource_content('blocked.gif');
|
||||
$rcmail->output->nocacheing_headers();
|
||||
header("Content-Type: image/gif");
|
||||
header("Content-Transfer-Encoding: binary");
|
||||
header("Content-Length: " . strlen($content));
|
||||
echo $content;
|
||||
}
|
||||
// html warning with a button to load the file anyway
|
||||
else {
|
||||
$rcmail->output = new rcmail_html_page();
|
||||
$rcmail->output->register_inline_warning(
|
||||
$rcmail->gettext([
|
||||
'name' => 'attachmentvalidationerror',
|
||||
'vars' => [
|
||||
'expected' => $mimetype . (!empty($file_extension) ? rcube::Q(" (.{$file_extension})") : ''),
|
||||
'detected' => $real_mimetype . (!empty($extensions[0]) ? " (.{$extensions[0]})" : ''),
|
||||
]
|
||||
]),
|
||||
$rcmail->gettext('showanyway'),
|
||||
$rcmail->url(array_merge($_GET, ['_nocheck' => 1]))
|
||||
);
|
||||
|
||||
$rcmail->output->write();
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// TIFF/WEBP to JPEG conversion, if needed
|
||||
foreach (['tiff', 'webp'] as $type) {
|
||||
$img_support = !empty($_SESSION['browser_caps']) && !empty($_SESSION['browser_caps'][$type]);
|
||||
if (
|
||||
!empty($_REQUEST['_embed'])
|
||||
&& !$img_support
|
||||
&& $attachment->image_type() == 'image/' . $type
|
||||
&& rcube_image::is_convertable('image/' . $type)
|
||||
) {
|
||||
$convert2jpeg = true;
|
||||
$mimetype = 'image/jpeg';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// deliver part content
|
||||
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
|
||||
$rcmail->output = new rcmail_html_page();
|
||||
$out = '';
|
||||
|
||||
// Check if we have enough memory to handle the message in it
|
||||
// #1487424: we need up to 10x more memory than the body
|
||||
if (!rcube_utils::mem_check($attachment->size * 10)) {
|
||||
$rcmail->output->register_inline_warning(
|
||||
$rcmail->gettext('messagetoobig'),
|
||||
$rcmail->gettext('download'),
|
||||
$rcmail->url(array_merge($_GET, ['_download' => 1]))
|
||||
);
|
||||
}
|
||||
else {
|
||||
// render HTML body
|
||||
$out = $attachment->html();
|
||||
|
||||
// insert remote objects warning into HTML body
|
||||
if (self::$REMOTE_OBJECTS) {
|
||||
$rcmail->output->register_inline_warning(
|
||||
$rcmail->gettext('blockedresources'),
|
||||
$rcmail->gettext('allow'),
|
||||
$rcmail->url(array_merge($_GET, ['_safe' => 1]))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->write($out);
|
||||
exit;
|
||||
}
|
||||
|
||||
// add filename extension if missing
|
||||
if (!pathinfo($filename, PATHINFO_EXTENSION) && ($extensions = rcube_mime::get_mime_extensions($mimetype))) {
|
||||
$filename .= '.' . $extensions[0];
|
||||
}
|
||||
|
||||
$rcmail->output->download_headers($filename, [
|
||||
'type' => $mimetype,
|
||||
'type_charset' => $attachment->charset,
|
||||
'disposition' => !empty($_GET['_download']) ? 'attachment' : 'inline',
|
||||
]);
|
||||
|
||||
// handle tiff to jpeg conversion
|
||||
if (!empty($convert2jpeg)) {
|
||||
$file_path = rcube_utils::temp_filename('attmnt');
|
||||
|
||||
// convert image to jpeg and send it to the browser
|
||||
if ($attachment->body_to_file($file_path)) {
|
||||
$image = new rcube_image($file_path);
|
||||
if ($image->convert(rcube_image::TYPE_JPG, $file_path)) {
|
||||
header("Content-Length: " . filesize($file_path));
|
||||
readfile($file_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$attachment->output($mimetype);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
// if we arrive here, the requested part was not found
|
||||
header('HTTP/1.1 404 Not Found');
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two mimetype strings with making sure that
|
||||
* e.g. image/bmp and image/x-ms-bmp are treated as equal.
|
||||
*/
|
||||
public static function mimetype_compare($type1, $type2)
|
||||
{
|
||||
$regexp = '~/(x-ms-|x-)~';
|
||||
$type1 = preg_replace($regexp, '/', $type1);
|
||||
$type2 = preg_replace($regexp, '/', $type2);
|
||||
|
||||
return $type1 === $type2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachment properties table
|
||||
*/
|
||||
public static function message_part_controls($attrib)
|
||||
{
|
||||
if (!self::$attachment->is_valid()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$table = new html_table(['cols' => 2]);
|
||||
|
||||
$table->add('title', rcube::Q($rcmail->gettext('namex')).':');
|
||||
$table->add('header', rcube::Q(self::$attachment->filename));
|
||||
|
||||
$table->add('title', rcube::Q($rcmail->gettext('type')).':');
|
||||
$table->add('header', rcube::Q(self::$attachment->mimetype));
|
||||
|
||||
$table->add('title', rcube::Q($rcmail->gettext('size')).':');
|
||||
$table->add('header', rcube::Q(self::$attachment->size()));
|
||||
|
||||
return $table->show($attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachment preview frame
|
||||
*/
|
||||
public static function message_part_frame($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($rcmail->output->get_env('is_message')) {
|
||||
$url = [
|
||||
'task' => 'mail',
|
||||
'action' => 'preview',
|
||||
'uid' => $rcmail->output->get_env('uid'),
|
||||
'mbox' => $rcmail->output->get_env('mailbox'),
|
||||
];
|
||||
}
|
||||
else {
|
||||
$mimetype = $rcmail->output->get_env('mimetype');
|
||||
$url = $_GET;
|
||||
$url[strpos($mimetype, 'text/') === 0 ? '_embed' : '_preload'] = 1;
|
||||
unset($url['_frame']);
|
||||
}
|
||||
|
||||
$url['_framed'] = 1; // For proper X-Frame-Options:deny handling
|
||||
|
||||
$attrib['src'] = $rcmail->url($url);
|
||||
|
||||
$rcmail->output->add_gui_object('messagepartframe', $attrib['id']);
|
||||
|
||||
return html::iframe($attrib);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Check all mailboxes for unread messages and update GUI |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_getunread extends rcmail_action_mail_index
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$a_folders = $rcmail->storage->list_folders_subscribed('', '*', 'mail');
|
||||
|
||||
if (!empty($a_folders)) {
|
||||
$current = $rcmail->storage->get_folder();
|
||||
$inbox = $current == 'INBOX';
|
||||
$trash = $rcmail->config->get('trash_mbox');
|
||||
$check_all = (bool) $rcmail->config->get('check_all_folders');
|
||||
|
||||
foreach ($a_folders as $mbox) {
|
||||
$unseen_old = self::get_unseen_count($mbox);
|
||||
|
||||
if (!$check_all && $unseen_old !== null && $mbox != $current) {
|
||||
$unseen = $unseen_old;
|
||||
}
|
||||
else {
|
||||
$unseen = $rcmail->storage->count($mbox, 'UNSEEN', $unseen_old === null);
|
||||
}
|
||||
|
||||
// call it always for current folder, so it can update counter
|
||||
// after possible message status change when opening a message
|
||||
// not in preview frame
|
||||
if ($unseen || $unseen_old === null || $mbox == $current) {
|
||||
$rcmail->output->command('set_unread_count', $mbox, $unseen, $inbox && $mbox == 'INBOX');
|
||||
}
|
||||
|
||||
self::set_unseen_count($mbox, $unseen);
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox === $trash) {
|
||||
$rcmail->output->command('set_trash_count', $rcmail->storage->count($mbox, 'EXISTS'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Expand addressbook group into list of email addresses |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_group_expand extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$gid = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GET);
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$abook = $rcmail->get_address_book($source);
|
||||
|
||||
if ($gid && $abook) {
|
||||
$abook->set_group($gid);
|
||||
$abook->set_pagesize(9999); // TODO: limit number of group members by config?
|
||||
|
||||
$result = $abook->list_records($rcmail->config->get('contactlist_fields'));
|
||||
$members = [];
|
||||
|
||||
while ($result && ($record = $result->iterate())) {
|
||||
$email = array_first((array) $abook->get_col_values('email', $record, true));
|
||||
if (!empty($email)) {
|
||||
$members[] = format_email_recipient($email, rcube_addressbook::compose_list_name($record));
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->command('replace_group_recipients', $gid, join(', ', array_unique($members)));
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Fetch message headers in raw format for display |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_headers extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $source;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GP);
|
||||
$inline = $rcmail->output instanceof rcmail_output_html;
|
||||
|
||||
if (!$uid) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($pos = strpos($uid, '.')) {
|
||||
$message = new rcube_message($uid);
|
||||
$source = $message->get_part_body(substr($uid, $pos + 1));
|
||||
$source = substr($source, 0, strpos($source, "\r\n\r\n"));
|
||||
}
|
||||
else {
|
||||
$source = $rcmail->storage->get_raw_headers($uid);
|
||||
}
|
||||
|
||||
if ($source !== false) {
|
||||
$source = trim(rcube_charset::clean($source));
|
||||
$source = htmlspecialchars($source, ENT_COMPAT | ENT_HTML401, RCUBE_CHARSET);
|
||||
$source = preg_replace(
|
||||
[
|
||||
'/\n[\t\s]+/',
|
||||
'/^([a-z0-9_:-]+)/im',
|
||||
'/\r?\n/'
|
||||
],
|
||||
[
|
||||
"\n ",
|
||||
'<font class="bold">\1</font>',
|
||||
'<br />'
|
||||
],
|
||||
$source
|
||||
);
|
||||
|
||||
self::$source = $source;
|
||||
|
||||
$rcmail->output->add_handlers(['dialogcontent' => [$this, 'headers_output']]);
|
||||
|
||||
if ($inline) {
|
||||
$rcmail->output->set_env('dialog_class', 'text-nowrap');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->command('set_headers', $source);
|
||||
}
|
||||
}
|
||||
else if (!$inline) {
|
||||
$rcmail->output->show_message('messageopenerror', 'error');
|
||||
}
|
||||
|
||||
$rcmail->output->send($inline ? 'dialog' : null);
|
||||
}
|
||||
|
||||
public static function headers_output()
|
||||
{
|
||||
return self::$source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Save the uploaded file(s) as messages to the current IMAP folder |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_import extends rcmail_action
|
||||
{
|
||||
// only process ajax requests
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// clear all stored output properties (like scripts and env vars)
|
||||
$rcmail->output->reset();
|
||||
|
||||
if (!empty($_FILES['_file']) && is_array($_FILES['_file'])) {
|
||||
$imported = 0;
|
||||
$folder = $rcmail->storage->get_folder();
|
||||
|
||||
foreach ((array) $_FILES['_file']['tmp_name'] as $i => $filepath) {
|
||||
// Process uploaded file if there is no error
|
||||
$err = $_FILES['_file']['error'][$i];
|
||||
|
||||
if (!$err) {
|
||||
// check file content type first
|
||||
$ctype = rcube_mime::file_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i]);
|
||||
list($mtype_primary, $mtype_secondary) = explode('/', $ctype);
|
||||
|
||||
if (in_array($ctype, ['application/zip', 'application/x-zip'])) {
|
||||
$filepath = self::zip_extract($filepath);
|
||||
if (empty($filepath)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!in_array($mtype_primary, ['text', 'message'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ((array) $filepath as $file) {
|
||||
// read the first few lines to detect header-like structure
|
||||
$fp = fopen($file, 'r');
|
||||
do {
|
||||
$line = fgets($fp);
|
||||
}
|
||||
while ($line !== false && trim($line) == '');
|
||||
|
||||
if (!preg_match('/^From .+/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = $lastline = '';
|
||||
fseek($fp, 0);
|
||||
|
||||
while (($line = fgets($fp)) !== false) {
|
||||
// importing mbox file, split by From - lines
|
||||
if ($lastline === '' && strncmp($line, 'From ', 5) === 0 && strlen($line) > 5) {
|
||||
if (!empty($message)) {
|
||||
$imported += (int) self::save_message($folder, $message);
|
||||
}
|
||||
|
||||
$message = $line;
|
||||
$lastline = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
$message .= $line;
|
||||
$lastline = rtrim($line);
|
||||
}
|
||||
|
||||
if (!empty($message)) {
|
||||
$imported += (int) self::save_message($folder, $message);
|
||||
}
|
||||
|
||||
// remove temp files extracted from zip
|
||||
if (is_array($filepath)) {
|
||||
unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::upload_error($err);
|
||||
}
|
||||
}
|
||||
|
||||
if ($imported) {
|
||||
$rcmail->output->show_message($rcmail->gettext(['name' => 'importmessagesuccess', 'nr' => $imported, 'vars' => ['nr' => $imported]]), 'confirmation');
|
||||
$rcmail->output->command('command', 'list');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('importmessageerror', 'error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
self::upload_failure();
|
||||
}
|
||||
|
||||
// send html page with JS calls as response
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
public static function zip_extract($path)
|
||||
{
|
||||
if (!class_exists('ZipArchive', false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$zip = new ZipArchive;
|
||||
$files = [];
|
||||
|
||||
if ($zip->open($path)) {
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$entry = $zip->getNameIndex($i);
|
||||
$tmpfname = rcube_utils::temp_filename('zipimport');
|
||||
|
||||
if (copy("zip://$path#$entry", $tmpfname)) {
|
||||
$ctype = rcube_mime::file_content_type($tmpfname, $entry);
|
||||
list($mtype_primary, ) = explode('/', $ctype);
|
||||
|
||||
if (in_array($mtype_primary, ['text', 'message'])) {
|
||||
$files[] = $tmpfname;
|
||||
}
|
||||
else {
|
||||
unlink($tmpfname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
public static function save_message($folder, &$message)
|
||||
{
|
||||
$date = null;
|
||||
|
||||
if (strncmp($message, 'From ', 5) === 0) {
|
||||
// Extract the mbox from_line
|
||||
$pos = strpos($message, "\n");
|
||||
$from = substr($message, 0, $pos);
|
||||
$message = substr($message, $pos + 1);
|
||||
|
||||
// Read the received date, support only known date formats
|
||||
|
||||
// RFC4155: "Sat Jan 3 01:05:34 1996"
|
||||
$mboxdate_rx = '/^([a-z]{3} [a-z]{3} [0-9 ][0-9] [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9]{4})/i';
|
||||
// Roundcube/Zipdownload: "12-Dec-2016 10:56:33 +0100"
|
||||
$imapdate_rx = '/^([0-9]{1,2}-[a-z]{3}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [0-9+-]{5})/i';
|
||||
|
||||
if (
|
||||
($pos = strpos($from, ' ', 6))
|
||||
&& ($dt_str = substr($from, $pos + 1))
|
||||
&& (preg_match($mboxdate_rx, $dt_str, $m) || preg_match($imapdate_rx, $dt_str, $m))
|
||||
) {
|
||||
try {
|
||||
$date = new DateTime($m[0], new DateTimeZone('UTC'));
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unquote ">From " lines in message body
|
||||
$message = preg_replace('/\n>([>]*)From /', "\n\\1From ", $message);
|
||||
$message = rtrim($message);
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($rcmail->storage->save_message($folder, $message, '', false, [], $date)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
rcube::raise_error("Failed to import message to $folder", true, false);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Send message list to client (as remote response) |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_list extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$save_arr = [];
|
||||
$dont_override = (array) $rcmail->config->get('dont_override');
|
||||
$cols = null;
|
||||
|
||||
// is there a sort type for this request?
|
||||
$sort = rcube_utils::get_input_string('_sort', rcube_utils::INPUT_GET);
|
||||
if ($sort && preg_match('/^[a-zA-Z_-]+$/', $sort)) {
|
||||
// yes, so set the sort vars
|
||||
list($sort_col, $sort_order) = explode('_', $sort);
|
||||
|
||||
// set session vars for sort (so next page and task switch know how to sort)
|
||||
if (!in_array('message_sort_col', $dont_override)) {
|
||||
$_SESSION['sort_col'] = $save_arr['message_sort_col'] = $sort_col;
|
||||
}
|
||||
if (!in_array('message_sort_order', $dont_override)) {
|
||||
$_SESSION['sort_order'] = $save_arr['message_sort_order'] = $sort_order;
|
||||
}
|
||||
}
|
||||
|
||||
// is there a set of columns for this request?
|
||||
if ($cols = rcube_utils::get_input_string('_cols', rcube_utils::INPUT_GET)) {
|
||||
$_SESSION['list_attrib']['columns'] = explode(',', $cols);
|
||||
if (!in_array('list_cols', $dont_override)) {
|
||||
$save_arr['list_cols'] = explode(',', $cols);
|
||||
}
|
||||
}
|
||||
|
||||
// register layout change
|
||||
if ($layout = rcube_utils::get_input_string('_layout', rcube_utils::INPUT_GET)) {
|
||||
$rcmail->output->set_env('layout', $layout);
|
||||
$save_arr['layout'] = $layout;
|
||||
// force header replace on layout change
|
||||
if (!empty($_SESSION['list_attrib']['columns'])) {
|
||||
$cols = $_SESSION['list_attrib']['columns'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($save_arr)) {
|
||||
$rcmail->user->save_prefs($save_arr);
|
||||
}
|
||||
|
||||
$mbox_name = $rcmail->storage->get_folder();
|
||||
$threading = (bool) $rcmail->storage->get_threading();
|
||||
|
||||
// Synchronize mailbox cache, handle flag changes
|
||||
$rcmail->storage->folder_sync($mbox_name);
|
||||
|
||||
// fetch message headers
|
||||
$a_headers = [];
|
||||
if ($count = $rcmail->storage->count($mbox_name, $threading ? 'THREADS' : 'ALL', !empty($_REQUEST['_refresh']))) {
|
||||
$a_headers = $rcmail->storage->list_messages($mbox_name, null, self::sort_column(), self::sort_order());
|
||||
}
|
||||
|
||||
// update search set (possible change of threading mode)
|
||||
if (!empty($_REQUEST['_search']) && isset($_SESSION['search'])
|
||||
&& $_SESSION['search_request'] == $_REQUEST['_search']
|
||||
) {
|
||||
$search_request = $_REQUEST['_search'];
|
||||
$_SESSION['search'] = $rcmail->storage->get_search_set();
|
||||
$multifolder = !empty($_SESSION['search']) && !empty($_SESSION['search'][1]->multi);
|
||||
}
|
||||
// remove old search data
|
||||
else if (empty($_REQUEST['_search']) && isset($_SESSION['search'])) {
|
||||
$rcmail->session->remove('search');
|
||||
}
|
||||
|
||||
self::list_pagetitle();
|
||||
|
||||
// update mailboxlist
|
||||
if (empty($search_request)) {
|
||||
self::send_unread_count($mbox_name, !empty($_REQUEST['_refresh']), empty($a_headers) ? 0 : null);
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$pages = ceil($count / $rcmail->storage->get_pagesize());
|
||||
$page = $count ? $rcmail->storage->get_page() : 1;
|
||||
$exists = $rcmail->storage->count($mbox_name, 'EXISTS', true);
|
||||
|
||||
$rcmail->output->set_env('messagecount', $count);
|
||||
$rcmail->output->set_env('pagecount', $pages);
|
||||
$rcmail->output->set_env('threading', $threading);
|
||||
$rcmail->output->set_env('current_page', $page);
|
||||
$rcmail->output->set_env('exists', $exists);
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($count), $mbox_name);
|
||||
|
||||
// remove old message rows if commanded by the client
|
||||
if (!empty($_REQUEST['_clear'])) {
|
||||
$rcmail->output->command('clear_message_list');
|
||||
}
|
||||
|
||||
// add message rows
|
||||
self::js_message_list($a_headers, false, $cols);
|
||||
|
||||
if (!empty($a_headers)) {
|
||||
if (!empty($search_request)) {
|
||||
$rcmail->output->show_message('searchsuccessful', 'confirmation', ['nr' => $count]);
|
||||
}
|
||||
|
||||
// remember last HIGHESTMODSEQ value (if supported)
|
||||
// we need it for flag updates in check-recent
|
||||
$data = $rcmail->storage->folder_data($mbox_name);
|
||||
if (!empty($data['HIGHESTMODSEQ'])) {
|
||||
$_SESSION['list_mod_seq'] = $data['HIGHESTMODSEQ'];
|
||||
}
|
||||
}
|
||||
else {
|
||||
// handle IMAP errors (e.g. #1486905)
|
||||
if ($err_code = $rcmail->storage->get_error_code()) {
|
||||
self::display_server_error();
|
||||
}
|
||||
else if (!empty($search_request)) {
|
||||
$rcmail->output->show_message('searchnomatch', 'notice');
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('nomessagesfound', 'notice');
|
||||
}
|
||||
}
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox_name === $rcmail->config->get('trash_mbox')) {
|
||||
$rcmail->output->command('set_trash_count', $exists);
|
||||
}
|
||||
|
||||
if ($page == 1) {
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, !empty($multifolder) ? 'INBOX' : $mbox_name));
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Send contacts list to client (as remote response) |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_list_contacts extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$source = rcube_utils::get_input_string('_source', rcube_utils::INPUT_GPC);
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$addr_sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$list_page = max(1, $_GET['_page'] ?? 0);
|
||||
$jsresult = [];
|
||||
|
||||
// Use search result
|
||||
if (!empty($_REQUEST['_search']) && isset($_SESSION['contact_search'][$_REQUEST['_search']])) {
|
||||
$search = (array) $_SESSION['contact_search'][$_REQUEST['_search']];
|
||||
$sparam = $_SESSION['contact_search_params']['id'] == $_REQUEST['_search'] ? $_SESSION['contact_search_params']['data'] : [];
|
||||
$mode = (int) $rcmail->config->get('addressbook_search_mode');
|
||||
$records = [];
|
||||
|
||||
// get records from all sources
|
||||
foreach ($search as $s => $set) {
|
||||
$CONTACTS = $rcmail->get_address_book($s);
|
||||
|
||||
// list matching groups of this source (on page one)
|
||||
if ($sparam[1] && $CONTACTS->groups && $list_page == 1) {
|
||||
$jsresult += self::compose_contact_groups($CONTACTS, $s, $sparam[1], $mode);
|
||||
}
|
||||
|
||||
// reset page
|
||||
$CONTACTS->set_page(1);
|
||||
$CONTACTS->set_pagesize(9999);
|
||||
$CONTACTS->set_search_set($set);
|
||||
|
||||
// get records
|
||||
$result = $CONTACTS->list_records($afields);
|
||||
|
||||
while ($row = $result->next()) {
|
||||
$row['sourceid'] = $s;
|
||||
$key = rcube_addressbook::compose_contact_key($row, $addr_sort_col);
|
||||
$records[$key] = $row;
|
||||
}
|
||||
unset($result);
|
||||
}
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$first = ($list_page-1) * $page_size;
|
||||
$result = new rcube_result_set($count, $first);
|
||||
|
||||
// we need only records for current page
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $first, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
}
|
||||
// list contacts from selected source
|
||||
else {
|
||||
$CONTACTS = $rcmail->get_address_book($source);
|
||||
|
||||
if ($CONTACTS && $CONTACTS->ready) {
|
||||
// set list properties
|
||||
$CONTACTS->set_pagesize($page_size);
|
||||
$CONTACTS->set_page($list_page);
|
||||
|
||||
if ($group_id = rcube_utils::get_input_string('_gid', rcube_utils::INPUT_GET)) {
|
||||
$CONTACTS->set_group($group_id);
|
||||
}
|
||||
// list groups of this source (on page one)
|
||||
else if ($CONTACTS->groups && $CONTACTS->list_page == 1) {
|
||||
$jsresult = self::compose_contact_groups($CONTACTS, $source);
|
||||
}
|
||||
|
||||
// get contacts for this user
|
||||
$result = $CONTACTS->list_records($afields);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result) && !$result->count && $result->searchonly) {
|
||||
$rcmail->output->show_message('contactsearchonly', 'notice');
|
||||
}
|
||||
else if (!empty($result) && $result->count > 0) {
|
||||
// create javascript list
|
||||
while ($row = $result->next()) {
|
||||
$name = rcube_addressbook::compose_list_name($row);
|
||||
|
||||
// add record for every email address of the contact
|
||||
$emails = rcube_addressbook::get_col_values('email', $row, true);
|
||||
foreach ($emails as $i => $email) {
|
||||
$source = !empty($row['sourceid']) ? $row['sourceid'] : $source;
|
||||
$row_id = $source.'-'.$row['ID'].'-'.$i;
|
||||
$is_group = isset($row['_type']) && $row['_type'] == 'group';
|
||||
$classname = $is_group ? 'group' : 'person';
|
||||
$keyname = $is_group ? 'contactgroup' : 'contact';
|
||||
|
||||
$jsresult[$row_id] = format_email_recipient($email, $name);
|
||||
|
||||
$rcmail->output->command('add_contact_row', $row_id, [
|
||||
$keyname => html::a(
|
||||
['title' => $email],
|
||||
rcube::Q($name ?: $email)
|
||||
. ($name && count($emails) > 1 ? ' ' . html::span('email', rcube::Q($email)) : '')
|
||||
)
|
||||
],
|
||||
$classname
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update env
|
||||
$rcmail->output->set_env('contactdata', $jsresult);
|
||||
$rcmail->output->set_env('pagecount', isset($result) ? ceil($result->count / $page_size) : 1);
|
||||
$rcmail->output->command('set_page_buttons');
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add groups from the given address source to the address book widget
|
||||
*/
|
||||
public static function compose_contact_groups($abook, $source_id, $search = null, $search_mode = 0)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$jsresult = [];
|
||||
|
||||
foreach ($abook->list_groups($search, $search_mode) as $group) {
|
||||
$abook->reset();
|
||||
$abook->set_group($group['ID']);
|
||||
|
||||
// group (distribution list) with email address(es)
|
||||
if (!empty($group['email'])) {
|
||||
foreach ((array) $group['email'] as $email) {
|
||||
$row_id = 'G'.$group['ID'];
|
||||
$jsresult[$row_id] = format_email_recipient($email, $group['name']);
|
||||
$rcmail->output->command('add_contact_row', $row_id, [
|
||||
'contactgroup' => html::span(['title' => $email], rcube::Q($group['name']))
|
||||
], 'group');
|
||||
}
|
||||
}
|
||||
// make virtual groups clickable to list their members
|
||||
else if (!empty($group['virtual'])) {
|
||||
$row_id = 'G'.$group['ID'];
|
||||
$rcmail->output->command('add_contact_row', $row_id, [
|
||||
'contactgroup' => html::a([
|
||||
'href' => '#list',
|
||||
'rel' => $group['ID'],
|
||||
'title' => $rcmail->gettext('listgroup'),
|
||||
'onclick' => sprintf("return %s.command('pushgroup',{'source':'%s','id':'%s'},this,event)",
|
||||
rcmail_output::JS_OBJECT_NAME, $source_id, $group['ID']),
|
||||
],
|
||||
rcube::Q($group['name']) . ' ' . html::span('action', '»')
|
||||
)],
|
||||
'group',
|
||||
['ID' => $group['ID'], 'name' => $group['name'], 'virtual' => true]
|
||||
);
|
||||
}
|
||||
// show group with count
|
||||
else if (($result = $abook->count()) && $result->count) {
|
||||
$row_id = 'E'.$group['ID'];
|
||||
$jsresult[$row_id] = ['name' => $group['name'], 'source' => $source_id];
|
||||
$rcmail->output->command('add_contact_row', $row_id, [
|
||||
'contactgroup' => rcube::Q($group['name'] . ' (' . intval($result->count) . ')')
|
||||
], 'group');
|
||||
}
|
||||
}
|
||||
|
||||
$abook->reset();
|
||||
$abook->set_group(0);
|
||||
|
||||
return $jsresult;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Mark the submitted messages with the specified flag |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_mark extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$_uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST);
|
||||
$flag = rcube_utils::get_input_string('_flag', rcube_utils::INPUT_POST);
|
||||
$folders = rcube_utils::get_input_string('_folders', rcube_utils::INPUT_POST);
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST);
|
||||
|
||||
if (empty($_uids) || empty($flag)) {
|
||||
$rcmail->output->show_message('internalerror', 'error');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$threading = (bool) $rcmail->storage->get_threading();
|
||||
$skip_deleted = (bool) $rcmail->config->get('skip_deleted');
|
||||
$read_deleted = (bool) $rcmail->config->get('read_when_deleted');
|
||||
$flag = self::imap_flag($flag);
|
||||
$old_count = 0;
|
||||
$from = $_POST['_from'] ?? null;
|
||||
|
||||
if ($flag == 'DELETED' && $skip_deleted && $from != 'show') {
|
||||
// count messages before changing anything
|
||||
$old_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
|
||||
}
|
||||
|
||||
if ($folders == 'all') {
|
||||
$mboxes = $rcmail->storage->list_folders_subscribed('', '*', 'mail');
|
||||
$input = array_combine($mboxes, array_fill(0, count($mboxes), '*'));
|
||||
}
|
||||
else if ($folders == 'sub') {
|
||||
$delim = $rcmail->storage->get_hierarchy_delimiter();
|
||||
$mboxes = $rcmail->storage->list_folders_subscribed($mbox . $delim, '*', 'mail');
|
||||
array_unshift($mboxes, $mbox);
|
||||
$input = array_combine($mboxes, array_fill(0, count($mboxes), '*'));
|
||||
}
|
||||
else if ($folders == 'cur') {
|
||||
$input = [$mbox => '*'];
|
||||
}
|
||||
else {
|
||||
$input = self::get_uids(null, null, $dummy, rcube_utils::INPUT_POST);
|
||||
}
|
||||
|
||||
$marked = 0;
|
||||
$count = 0;
|
||||
$read = 0;
|
||||
|
||||
foreach ($input as $mbox => $uids) {
|
||||
$marked += (int) $rcmail->storage->set_flag($uids, $flag, $mbox);
|
||||
$count += is_array($uids) ? count($uids) : 1;
|
||||
}
|
||||
|
||||
if (!$marked) {
|
||||
// send error message
|
||||
if ($from != 'show') {
|
||||
$rcmail->output->command('list_mailbox');
|
||||
}
|
||||
|
||||
self::display_server_error('errormarking');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else if (empty($_POST['_quiet'])) {
|
||||
$rcmail->output->show_message('messagemarked', 'confirmation');
|
||||
}
|
||||
|
||||
if ($flag == 'DELETED' && $read_deleted && !empty($_POST['_ruid'])) {
|
||||
if ($ruids = rcube_utils::get_input_value('_ruid', rcube_utils::INPUT_POST)) {
|
||||
foreach (self::get_uids($ruids) as $mbox => $uids) {
|
||||
$read += (int) $rcmail->storage->set_flag($uids, 'SEEN', $mbox);
|
||||
}
|
||||
}
|
||||
|
||||
if ($read && !$skip_deleted) {
|
||||
$rcmail->output->command('flag_deleted_as_read', $ruids);
|
||||
}
|
||||
}
|
||||
|
||||
if ($flag == 'SEEN' || $flag == 'UNSEEN' || ($flag == 'DELETED' && !$skip_deleted)) {
|
||||
foreach ($input as $mbox => $uids) {
|
||||
self::send_unread_count($mbox);
|
||||
}
|
||||
|
||||
$rcmail->output->set_env('last_flag', $flag);
|
||||
}
|
||||
else if ($flag == 'DELETED' && $skip_deleted) {
|
||||
if ($from == 'show') {
|
||||
if ($next = rcube_utils::get_input_value('_next_uid', rcube_utils::INPUT_GPC)) {
|
||||
$rcmail->output->command('show_message', $next);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->command('command', 'list');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC);
|
||||
|
||||
// refresh saved search set after moving some messages
|
||||
if ($search_request && $rcmail->storage->get_search_set()) {
|
||||
$_SESSION['search'] = $rcmail->storage->refresh_search();
|
||||
}
|
||||
|
||||
$msg_count = $rcmail->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
|
||||
$page_size = $rcmail->storage->get_pagesize();
|
||||
$page = $rcmail->storage->get_page();
|
||||
$pages = ceil($msg_count / $page_size);
|
||||
$nextpage_count = $old_count - $page_size * $page;
|
||||
$remaining = $msg_count - $page_size * ($page - 1);
|
||||
$jump_back = false;
|
||||
|
||||
// jump back one page (user removed the whole last page)
|
||||
if ($page > 1 && $remaining == 0) {
|
||||
$page -= 1;
|
||||
$rcmail->storage->set_page($page);
|
||||
$_SESSION['page'] = $page;
|
||||
$jump_back = true;
|
||||
}
|
||||
|
||||
foreach ($input as $mbox => $uids) {
|
||||
self::send_unread_count($mbox, true);
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('messagecount', $msg_count);
|
||||
$rcmail->output->set_env('current_page', $page);
|
||||
$rcmail->output->set_env('pagecount', $pages);
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox);
|
||||
|
||||
if ($threading) {
|
||||
$count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST);
|
||||
}
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if ($old_count && $_uids != '*' && ($jump_back || $nextpage_count > 0)) {
|
||||
// #5862: Don't add more rows than it was on the next page
|
||||
$count = $jump_back ? null : min($nextpage_count, $count);
|
||||
|
||||
$a_headers = $rcmail->storage->list_messages($mbox, null,
|
||||
self::sort_column(), self::sort_order(), $count);
|
||||
|
||||
self::js_message_list($a_headers, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map Roundcube UI's flag label into IMAP flag
|
||||
*
|
||||
* @param string $flag Flag label
|
||||
*
|
||||
* @return string Uppercase IMAP flag
|
||||
*/
|
||||
public static function imap_flag($flag)
|
||||
{
|
||||
$flags_map = [
|
||||
'undelete' => 'UNDELETED',
|
||||
'delete' => 'DELETED',
|
||||
'read' => 'SEEN',
|
||||
'unread' => 'UNSEEN',
|
||||
'flagged' => 'FLAGGED',
|
||||
'unflagged' => 'UNFLAGGED',
|
||||
];
|
||||
|
||||
return !empty($flags_map[$flag]) ? $flags_map[$flag] : strtoupper($flag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handler for mail move operation |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_move extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// count messages before changing anything
|
||||
$threading = (bool) $rcmail->storage->get_threading();
|
||||
$trash = $rcmail->config->get('trash_mbox');
|
||||
$old_count = 0;
|
||||
|
||||
if (empty($_POST['_from']) || $_POST['_from'] != 'show') {
|
||||
$old_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
|
||||
}
|
||||
|
||||
$target = rcube_utils::get_input_string('_target_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (empty($_POST['_uid']) || !strlen($target)) {
|
||||
$rcmail->output->show_message('internalerror', 'error');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$success = true;
|
||||
$addrows = false;
|
||||
$count = 0;
|
||||
$sources = [];
|
||||
|
||||
foreach (rcmail::get_uids(null, null, $multifolder, rcube_utils::INPUT_POST) as $mbox => $uids) {
|
||||
if ($mbox === $target) {
|
||||
$count += is_array($uids) ? count($uids) : 1;
|
||||
}
|
||||
else if ($rcmail->storage->move_message($uids, $target, $mbox)) {
|
||||
$count += is_array($uids) ? count($uids) : 1;
|
||||
$sources[] = $mbox;
|
||||
}
|
||||
else {
|
||||
$success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
// send error message
|
||||
if (empty($_POST['_from']) || $_POST['_from'] != 'show') {
|
||||
$rcmail->output->command('list_mailbox');
|
||||
}
|
||||
|
||||
self::display_server_error('errormoving', null, $target == $trash ? 'delete' : '');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message($target == $trash ? 'messagemovedtotrash' : 'messagemoved', 'confirmation');
|
||||
}
|
||||
|
||||
if (!empty($_POST['_refresh'])) {
|
||||
// FIXME: send updated message rows instead of reloading the entire list
|
||||
$rcmail->output->command('refresh_list');
|
||||
}
|
||||
else {
|
||||
$addrows = true;
|
||||
}
|
||||
|
||||
$search_request = rcube_utils::get_input_string('_search', rcube_utils::INPUT_GPC);
|
||||
|
||||
// refresh saved search set after moving some messages
|
||||
if ($search_request && $rcmail->storage->get_search_set()) {
|
||||
$_SESSION['search'] = $rcmail->storage->refresh_search();
|
||||
}
|
||||
|
||||
if (!empty($_POST['_from']) && $_POST['_from'] == 'show') {
|
||||
if ($next = rcube_utils::get_input_string('_next_uid', rcube_utils::INPUT_GPC)) {
|
||||
$rcmail->output->command('show_message', $next);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->command('command', 'list');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
$mbox = $rcmail->storage->get_folder();
|
||||
$msg_count = $rcmail->storage->count(null, $threading ? 'THREADS' : 'ALL');
|
||||
$exists = $rcmail->storage->count($mbox, 'EXISTS', true);
|
||||
$page_size = $rcmail->storage->get_pagesize();
|
||||
$page = $rcmail->storage->get_page();
|
||||
$pages = ceil($msg_count / $page_size);
|
||||
$nextpage_count = $old_count - $page_size * $page;
|
||||
$remaining = $msg_count - $page_size * ($page - 1);
|
||||
|
||||
// jump back one page (user removed the whole last page)
|
||||
if ($page > 1 && $remaining == 0) {
|
||||
$page -= 1;
|
||||
$rcmail->storage->set_page($page);
|
||||
$_SESSION['page'] = $page;
|
||||
$jump_back = true;
|
||||
}
|
||||
|
||||
// update unseen messages counts for all involved folders
|
||||
foreach ($sources as $source) {
|
||||
self::send_unread_count($source, true);
|
||||
}
|
||||
|
||||
self::send_unread_count($target, true);
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('messagecount', $msg_count);
|
||||
$rcmail->output->set_env('current_page', $page);
|
||||
$rcmail->output->set_env('pagecount', $pages);
|
||||
$rcmail->output->set_env('exists', $exists);
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $multifolder ? $sources[0] : 'INBOX'));
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($msg_count), $mbox);
|
||||
|
||||
if ($threading) {
|
||||
$count = rcube_utils::get_input_string('_count', rcube_utils::INPUT_POST);
|
||||
}
|
||||
|
||||
// add new rows from next page (if any)
|
||||
if ($addrows && $count && $_POST['_uid'] != '*' && (!empty($jump_back) || $nextpage_count > 0)) {
|
||||
// #5862: Don't add more rows than it was on the next page
|
||||
$count = !empty($jump_back) ? null : min($nextpage_count, $count);
|
||||
|
||||
$a_headers = $rcmail->storage->list_messages($mbox, NULL,
|
||||
self::sort_column(), self::sort_order(), $count);
|
||||
|
||||
self::js_message_list($a_headers, false);
|
||||
}
|
||||
|
||||
// set trash folder state
|
||||
if ($mbox === $trash) {
|
||||
$rcmail->output->command('set_trash_count', $exists);
|
||||
}
|
||||
else if ($target === $trash) {
|
||||
$rcmail->output->command('set_trash_count', $rcmail->storage->count($trash, 'EXISTS', true));
|
||||
}
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Updates message page navigation controls |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_pagenav extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET);
|
||||
$index = $rcmail->storage->index(null, self::sort_column(), self::sort_order());
|
||||
$cnt = $index->count_messages();
|
||||
|
||||
if ($cnt && ($pos = $index->exists($uid, true)) !== false) {
|
||||
$prev = $pos ? $index->get_element($pos-1) : 0;
|
||||
$first = $pos ? $index->get_element('FIRST') : 0;
|
||||
$next = $pos < $cnt-1 ? $index->get_element($pos+1) : 0;
|
||||
$last = $pos < $cnt-1 ? $index->get_element('LAST') : 0;
|
||||
}
|
||||
else {
|
||||
// error, this will at least disable page navigation
|
||||
$rcmail->output->command('set_rowcount', '');
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
// Set UIDs and activate navigation buttons
|
||||
if (!empty($prev)) {
|
||||
$rcmail->output->set_env('prev_uid', $prev);
|
||||
$rcmail->output->command('enable_command', 'previousmessage', 'firstmessage', true);
|
||||
}
|
||||
|
||||
if (!empty($next)) {
|
||||
$rcmail->output->set_env('next_uid', $next);
|
||||
$rcmail->output->command('enable_command', 'nextmessage', 'lastmessage', true);
|
||||
}
|
||||
|
||||
if (!empty($first)) {
|
||||
$rcmail->output->set_env('first_uid', $first);
|
||||
}
|
||||
|
||||
if (!empty($last)) {
|
||||
$rcmail->output->set_env('last_uid', $last);
|
||||
}
|
||||
|
||||
// Don't need a real messages count value
|
||||
$rcmail->output->set_env('messagecount', 1);
|
||||
|
||||
// Set rowcount text
|
||||
$rcmail->output->command('set_rowcount', $rcmail->gettext([
|
||||
'name' => 'messagenrof',
|
||||
'vars' => ['nr' => ($pos ?? 0) + 1, 'count' => $cnt]
|
||||
]));
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Mail messages search action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Benjamin Smith <defitro@gmail.com> |
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_search extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
@set_time_limit(170); // extend default max_execution_time to ~3 minutes
|
||||
|
||||
// reset list_page and old search results
|
||||
$rcmail->storage->set_page(1);
|
||||
$rcmail->storage->set_search_set(null);
|
||||
$_SESSION['page'] = 1;
|
||||
|
||||
// get search string
|
||||
$str = rcube_utils::get_input_string('_q', rcube_utils::INPUT_GET, true);
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_GET, true);
|
||||
$filter = rcube_utils::get_input_string('_filter', rcube_utils::INPUT_GET);
|
||||
$headers = rcube_utils::get_input_string('_headers', rcube_utils::INPUT_GET);
|
||||
$scope = rcube_utils::get_input_string('_scope', rcube_utils::INPUT_GET);
|
||||
$interval = rcube_utils::get_input_string('_interval', rcube_utils::INPUT_GET);
|
||||
$continue = rcube_utils::get_input_string('_continue', rcube_utils::INPUT_GET);
|
||||
|
||||
$filter = trim((string) $filter);
|
||||
$search_request = md5($mbox . $scope . $interval . $filter . $str);
|
||||
|
||||
// Parse input
|
||||
list($subject, $search) = self::search_input($str, $headers, $scope, $mbox);
|
||||
|
||||
// add list filter string
|
||||
$search_str = $filter && $filter != 'ALL' ? $filter : '';
|
||||
|
||||
if ($search_interval = self::search_interval_criteria($interval)) {
|
||||
$search_str .= ' ' . $search_interval;
|
||||
}
|
||||
|
||||
if (!empty($subject)) {
|
||||
$search_str .= str_repeat(' OR', count($subject)-1);
|
||||
foreach ($subject as $sub) {
|
||||
$search_str .= ' ' . $sub . ' ' . rcube_imap_generic::escape($search);
|
||||
}
|
||||
}
|
||||
|
||||
$search_str = trim($search_str);
|
||||
$sort_column = self::sort_column();
|
||||
$sort_order = self::sort_order();
|
||||
|
||||
// set message set for already stored (but incomplete) search request
|
||||
if (!empty($continue) && isset($_SESSION['search']) && $_SESSION['search_request'] == $continue) {
|
||||
$rcmail->storage->set_search_set($_SESSION['search']);
|
||||
$search_str = $_SESSION['search'][0];
|
||||
}
|
||||
|
||||
// execute IMAP search
|
||||
if ($search_str) {
|
||||
$mboxes = [];
|
||||
|
||||
// search all, current or subfolders folders
|
||||
if ($scope == 'all') {
|
||||
$mboxes = $rcmail->storage->list_folders_subscribed('', '*', 'mail', null, true);
|
||||
// we want natural alphabetic sorting of folders in the result set
|
||||
natcasesort($mboxes);
|
||||
}
|
||||
else if ($scope == 'sub') {
|
||||
$delim = $rcmail->storage->get_hierarchy_delimiter();
|
||||
$mboxes = $rcmail->storage->list_folders_subscribed($mbox . $delim, '*', 'mail');
|
||||
array_unshift($mboxes, $mbox);
|
||||
}
|
||||
|
||||
if ($scope != 'all') {
|
||||
// Remember current folder, it can change in meantime (plugins)
|
||||
// but we need it to e.g. recognize Sent folder to handle From/To column later
|
||||
$rcmail->output->set_env('mailbox', $mbox);
|
||||
}
|
||||
|
||||
$result = $rcmail->storage->search($mboxes, $search_str, RCUBE_CHARSET, $sort_column);
|
||||
}
|
||||
|
||||
// save search results in session
|
||||
if (!isset($_SESSION['search']) || !is_array($_SESSION['search'])) {
|
||||
$_SESSION['search'] = [];
|
||||
}
|
||||
|
||||
if ($search_str) {
|
||||
$_SESSION['search'] = $rcmail->storage->get_search_set();
|
||||
$_SESSION['last_text_search'] = $str;
|
||||
}
|
||||
|
||||
$_SESSION['search_request'] = $search_request;
|
||||
$_SESSION['search_scope'] = $scope;
|
||||
$_SESSION['search_interval'] = $interval;
|
||||
$_SESSION['search_filter'] = $filter;
|
||||
|
||||
// Get the headers
|
||||
if (!isset($result) || empty($result->incomplete)) {
|
||||
$result_h = $rcmail->storage->list_messages($mbox, 1, $sort_column, $sort_order);
|
||||
}
|
||||
|
||||
// Make sure we got the headers
|
||||
if (!empty($result_h)) {
|
||||
$count = $rcmail->storage->count($mbox, $rcmail->storage->get_threading() ? 'THREADS' : 'ALL');
|
||||
|
||||
self::js_message_list($result_h, false);
|
||||
|
||||
if ($search_str) {
|
||||
$all_count = $rcmail->storage->count(null, 'ALL');
|
||||
$rcmail->output->show_message('searchsuccessful', 'confirmation', ['nr' => $all_count]);
|
||||
}
|
||||
|
||||
// remember last HIGHESTMODSEQ value (if supported)
|
||||
// we need it for flag updates in check-recent
|
||||
if ($mbox !== null) {
|
||||
$data = $rcmail->storage->folder_data($mbox);
|
||||
if (!empty($data['HIGHESTMODSEQ'])) {
|
||||
$_SESSION['list_mod_seq'] = $data['HIGHESTMODSEQ'];
|
||||
}
|
||||
}
|
||||
}
|
||||
// handle IMAP errors (e.g. #1486905)
|
||||
else if ($err_code = $rcmail->storage->get_error_code()) {
|
||||
$count = 0;
|
||||
self::display_server_error();
|
||||
}
|
||||
// advice the client to re-send the (cross-folder) search request
|
||||
else if (!empty($result) && !empty($result->incomplete)) {
|
||||
$count = 0; // keep UI locked
|
||||
$rcmail->output->command('continue_search', $search_request);
|
||||
}
|
||||
else {
|
||||
$count = 0;
|
||||
|
||||
$rcmail->output->show_message('searchnomatch', 'notice');
|
||||
$rcmail->output->set_env('multifolder_listing', isset($result) ? !empty($result->multi) : false);
|
||||
|
||||
if (isset($result) && !empty($result->multi) && $scope == 'all') {
|
||||
$rcmail->output->command('select_folder', '');
|
||||
}
|
||||
}
|
||||
|
||||
// update message count display
|
||||
$rcmail->output->set_env('search_request', $search_str ? $search_request : '');
|
||||
$rcmail->output->set_env('search_filter', $_SESSION['search_filter']);
|
||||
$rcmail->output->set_env('messagecount', $count);
|
||||
$rcmail->output->set_env('pagecount', ceil($count / $rcmail->storage->get_pagesize()));
|
||||
$rcmail->output->set_env('exists', $mbox === null ? 0 : $rcmail->storage->count($mbox, 'EXISTS'));
|
||||
$rcmail->output->command('set_rowcount', self::get_messagecount_text($count, 1), $mbox);
|
||||
|
||||
self::list_pagetitle();
|
||||
|
||||
// update unseen messages count
|
||||
if ($search_str === '') {
|
||||
self::send_unread_count($mbox, false, empty($result_h) ? 0 : null);
|
||||
}
|
||||
|
||||
if (isset($result) && empty($result->incomplete)) {
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, !empty($result->multi) ? 'INBOX' : $mbox));
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates BEFORE/SINCE search criteria from the specified interval
|
||||
* Interval can be: 1W, 1M, 1Y, -1W, -1M, -1Y
|
||||
*/
|
||||
public static function search_interval_criteria($interval)
|
||||
{
|
||||
if (empty($interval)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($interval[0] == '-') {
|
||||
$search = 'BEFORE';
|
||||
$interval = substr($interval, 1);
|
||||
}
|
||||
else {
|
||||
$search = 'SINCE';
|
||||
}
|
||||
|
||||
$date = new DateTime('now');
|
||||
$interval = new DateInterval('P' . $interval);
|
||||
|
||||
$date->sub($interval);
|
||||
|
||||
return $search . ' ' . $date->format('j-M-Y');
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse search input.
|
||||
*
|
||||
* @param string $str Search string
|
||||
* @param string $headers Comma-separated list of headers/fields to search in
|
||||
* @param string $scope Search scope (all | base | sub)
|
||||
* @param string $mbox Folder name
|
||||
*
|
||||
* @return array Search criteria (1st element) and search value (2nd element)
|
||||
*/
|
||||
public static function search_input($str, $headers, $scope, $mbox)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$subject = [];
|
||||
$srch = null;
|
||||
$supported = ['subject', 'from', 'to', 'cc', 'bcc'];
|
||||
|
||||
// Check the search string for type of search
|
||||
if (preg_match("/^(from|to|reply-to|cc|bcc|subject):.*/i", $str, $m)) {
|
||||
list(, $srch) = explode(":", $str);
|
||||
$subject[$m[1]] = 'HEADER ' . strtoupper($m[1]);
|
||||
}
|
||||
else if (preg_match("/^body:.*/i", $str)) {
|
||||
list(, $srch) = explode(":", $str);
|
||||
$subject['body'] = 'BODY';
|
||||
}
|
||||
else if (strlen(trim($str))) {
|
||||
if ($headers) {
|
||||
foreach (explode(',', $headers) as $header) {
|
||||
switch ($header) {
|
||||
case 'text':
|
||||
// #1488208: get rid of other headers when searching by "TEXT"
|
||||
$subject = ['text' => 'TEXT'];
|
||||
break 2;
|
||||
case 'body':
|
||||
$subject['body'] = 'BODY';
|
||||
break;
|
||||
case 'replyto':
|
||||
case 'reply-to':
|
||||
$subject['reply-to'] = 'HEADER REPLY-TO';
|
||||
$subject['mail-reply-to'] = 'HEADER MAIL-REPLY-TO';
|
||||
break;
|
||||
case 'followupto':
|
||||
case 'followup-to':
|
||||
$subject['followup-to'] = 'HEADER FOLLOWUP-TO';
|
||||
$subject['mail-followup-to'] = 'HEADER MAIL-FOLLOWUP-TO';
|
||||
break;
|
||||
default:
|
||||
if (in_array_nocase($header, $supported)) {
|
||||
$subject[$header] = 'HEADER ' . strtoupper($header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save search modifiers for the current folder to user prefs
|
||||
if ($scope != 'all') {
|
||||
$search_mods = self::search_mods();
|
||||
$search_mods_value = array_fill_keys(array_keys($subject), 1);
|
||||
|
||||
if (!isset($search_mods[$mbox]) || $search_mods[$mbox] != $search_mods_value) {
|
||||
$search_mods[$mbox] = $search_mods_value;
|
||||
$rcmail->user->save_prefs(['search_mods' => $search_mods]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// search in subject by default
|
||||
$subject['subject'] = 'HEADER SUBJECT';
|
||||
}
|
||||
}
|
||||
|
||||
return [$subject, isset($srch) ? trim($srch) : trim($str)];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Search contacts from the address book widget |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_search_contacts extends rcmail_action_mail_list_contacts
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$search = rcube_utils::get_input_string('_q', rcube_utils::INPUT_GPC, true);
|
||||
$sources = $rcmail->get_address_sources();
|
||||
$search_mode = (int) $rcmail->config->get('addressbook_search_mode');
|
||||
$addr_sort_col = $rcmail->config->get('addressbook_sort_col', 'name');
|
||||
$afields = $rcmail->config->get('contactlist_fields');
|
||||
$page_size = $rcmail->config->get('addressbook_pagesize', $rcmail->config->get('pagesize', 50));
|
||||
$records = [];
|
||||
$search_set = [];
|
||||
$jsresult = [];
|
||||
$search_mode |= rcube_addressbook::SEARCH_GROUPS;
|
||||
|
||||
foreach ($sources as $s) {
|
||||
$source = $rcmail->get_address_book($s['id']);
|
||||
$source->set_page(1);
|
||||
$source->set_pagesize(9999);
|
||||
|
||||
// list matching groups of this source
|
||||
if ($source->groups) {
|
||||
$jsresult += self::compose_contact_groups($source, $s['id'], $search, $search_mode);
|
||||
}
|
||||
|
||||
// get contacts count
|
||||
$result = $source->search($afields, $search, $search_mode, true, true, 'email');
|
||||
|
||||
if (!$result->count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
while ($row = $result->next()) {
|
||||
$row['sourceid'] = $s['id'];
|
||||
$key = rcube_addressbook::compose_contact_key($row, $addr_sort_col);
|
||||
$records[$key] = $row;
|
||||
}
|
||||
|
||||
$search_set[$s['id']] = $source->get_search_set();
|
||||
unset($result);
|
||||
}
|
||||
|
||||
$group_count = count($jsresult);
|
||||
|
||||
// sort the records
|
||||
ksort($records, SORT_LOCALE_STRING);
|
||||
|
||||
// create resultset object
|
||||
$count = count($records);
|
||||
$result = new rcube_result_set($count);
|
||||
|
||||
// select the requested page
|
||||
if ($page_size < $count) {
|
||||
$records = array_slice($records, $result->first, $page_size);
|
||||
}
|
||||
|
||||
$result->records = array_values($records);
|
||||
|
||||
if (!empty($result) && $result->count > 0) {
|
||||
// create javascript list
|
||||
while ($row = $result->next()) {
|
||||
$name = rcube_addressbook::compose_list_name($row);
|
||||
$is_group = isset($row['_type']) && $row['_type'] == 'group';
|
||||
$classname = $is_group ? 'group' : 'person';
|
||||
$keyname = $is_group ? 'contactgroup' : 'contact';
|
||||
|
||||
// add record for every email address of the contact
|
||||
// (same as in list_contacts.inc)
|
||||
$emails = rcube_addressbook::get_col_values('email', $row, true);
|
||||
|
||||
foreach ($emails as $i => $email) {
|
||||
$row_id = $row['sourceid'].'-'.$row['ID'].'-'.$i;
|
||||
|
||||
$jsresult[$row_id] = format_email_recipient($email, $name);
|
||||
|
||||
$title = rcube_addressbook::compose_search_name($row, $email, $name);
|
||||
$link_content = rcube::Q($name ?: $email);
|
||||
if ($name && count($emails) > 1) {
|
||||
$link_content .= ' ' . html::span('email', rcube::Q($email));
|
||||
}
|
||||
$link = html::a(['title' => $title], $link_content);
|
||||
|
||||
$rcmail->output->command('add_contact_row', $row_id, [$keyname => $link], $classname);
|
||||
}
|
||||
}
|
||||
|
||||
// search request ID
|
||||
$search_request = md5('composeaddr' . $search);
|
||||
|
||||
// save search settings in session
|
||||
$_SESSION['contact_search'][$search_request] = $search_set;
|
||||
$_SESSION['contact_search_params'] = ['id' => $search_request, 'data' => [$afields, $search]];
|
||||
|
||||
$rcmail->output->show_message('contactsearchsuccessful', 'confirmation', ['nr' => $result->count]);
|
||||
|
||||
$rcmail->output->set_env('search_request', $search_request);
|
||||
$rcmail->output->set_env('source', '');
|
||||
$rcmail->output->command('unselect_directory');
|
||||
}
|
||||
else if (!$group_count) {
|
||||
$rcmail->output->show_message('nocontactsfound', 'notice');
|
||||
}
|
||||
|
||||
// update env
|
||||
$rcmail->output->set_env('contactdata', $jsresult);
|
||||
$rcmail->output->set_env('pagecount', ceil($result->count / $page_size));
|
||||
$rcmail->output->command('set_page_buttons');
|
||||
|
||||
// send response
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Compose a new mail message and send it or store as draft |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_send extends rcmail_action
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// remove all scripts and act as called in frame
|
||||
$rcmail->output->reset();
|
||||
$rcmail->output->framed = true;
|
||||
|
||||
$COMPOSE_ID = rcube_utils::get_input_string('_id', rcube_utils::INPUT_GPC);
|
||||
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
|
||||
|
||||
// Sanity checks
|
||||
if (!isset($COMPOSE['id'])) {
|
||||
rcube::raise_error([
|
||||
'code' => 500,
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Invalid compose ID"
|
||||
], true, false
|
||||
);
|
||||
|
||||
$rcmail->output->show_message('internalerror', 'error');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
$saveonly = !empty($_GET['_saveonly']);
|
||||
$savedraft = !empty($_POST['_draft']) && !$saveonly;
|
||||
$SENDMAIL = new rcmail_sendmail($COMPOSE, [
|
||||
'sendmail' => true,
|
||||
'saveonly' => $saveonly,
|
||||
'savedraft' => $savedraft,
|
||||
'error_handler' => function(...$args) use ($rcmail) {
|
||||
call_user_func_array([$rcmail->output, 'show_message'], $args);
|
||||
$rcmail->output->send('iframe');
|
||||
},
|
||||
'keepformatting' => !empty($_POST['_keepformatting']),
|
||||
]);
|
||||
|
||||
if (!isset($COMPOSE['attachments'])) {
|
||||
$COMPOSE['attachments'] = [];
|
||||
}
|
||||
|
||||
// Collect input for message headers
|
||||
$headers = $SENDMAIL->headers_input();
|
||||
|
||||
$COMPOSE['param']['message-id'] = $headers['Message-ID'];
|
||||
|
||||
$message_id = $headers['Message-ID'];
|
||||
$message_charset = $SENDMAIL->options['charset'];
|
||||
$message_body = rcube_utils::get_input_string('_message', rcube_utils::INPUT_POST, true, $message_charset);
|
||||
$isHtml = (bool) rcube_utils::get_input_string('_is_html', rcube_utils::INPUT_POST);
|
||||
|
||||
// Reset message body and attachments in Mailvelope mode
|
||||
if (isset($_POST['_pgpmime'])) {
|
||||
$pgp_mime = rcube_utils::get_input_string('_pgpmime', rcube_utils::INPUT_POST);
|
||||
$isHtml = false;
|
||||
$message_body = '';
|
||||
|
||||
// clear unencrypted attachments
|
||||
if (!empty($COMPOSE['attachments'])) {
|
||||
foreach ((array) $COMPOSE['attachments'] as $attach) {
|
||||
$rcmail->plugins->exec_hook('attachment_delete', $attach);
|
||||
}
|
||||
}
|
||||
|
||||
$COMPOSE['attachments'] = [];
|
||||
}
|
||||
|
||||
if ($isHtml) {
|
||||
$bstyle = [];
|
||||
|
||||
if ($font_size = $rcmail->config->get('default_font_size')) {
|
||||
$bstyle[] = 'font-size: ' . $font_size;
|
||||
}
|
||||
if ($font_family = $rcmail->config->get('default_font')) {
|
||||
$bstyle[] = 'font-family: ' . self::font_defs($font_family);
|
||||
}
|
||||
|
||||
// append doctype and html/body wrappers
|
||||
$bstyle = !empty($bstyle) ? (" style='" . implode('; ', $bstyle) . "'") : '';
|
||||
$message_body = '<html><head>'
|
||||
. '<meta http-equiv="Content-Type" content="text/html; charset='
|
||||
. ($message_charset ?: RCUBE_CHARSET) . '" /></head>'
|
||||
. "<body" . $bstyle . ">\r\n" . $message_body;
|
||||
}
|
||||
|
||||
if (!$savedraft) {
|
||||
if ($isHtml) {
|
||||
$b_style = 'padding: 0 0.4em; border-left: #1010ff 2px solid; margin: 0';
|
||||
$pre_style = 'margin: 0; padding: 0; font-family: monospace';
|
||||
|
||||
$message_body = preg_replace(
|
||||
[
|
||||
// remove empty signature div
|
||||
'/<div id="_rc_sig">( )?<\/div>[\s\r\n]*$/',
|
||||
// replace signature's div ID (#6073)
|
||||
'/ id="_rc_sig"/',
|
||||
// add inline css for blockquotes and container
|
||||
'/<blockquote>/',
|
||||
'/<div class="pre">/',
|
||||
// convert TinyMCE's new-line sequences (#1490463)
|
||||
'/<p> <\/p>/',
|
||||
],
|
||||
[
|
||||
'',
|
||||
' id="signature"',
|
||||
'<blockquote type="cite" style="'.$b_style.'">',
|
||||
'<div class="pre" style="'.$pre_style.'">',
|
||||
'<p><br /></p>',
|
||||
],
|
||||
$message_body
|
||||
);
|
||||
|
||||
rcube_utils::preg_error([
|
||||
'line' => __LINE__,
|
||||
'file' => __FILE__,
|
||||
'message' => "Could not format HTML!"
|
||||
], true);
|
||||
}
|
||||
|
||||
// Check spelling before send
|
||||
if (
|
||||
$rcmail->config->get('spellcheck_before_send')
|
||||
&& $rcmail->config->get('enable_spellcheck')
|
||||
&& empty($COMPOSE['spell_checked'])
|
||||
&& !empty($message_body)
|
||||
) {
|
||||
$language = rcube_utils::get_input_string('_lang', rcube_utils::INPUT_GPC);
|
||||
$message_body = str_replace("\r\n", "\n", $message_body);
|
||||
$spellchecker = new rcube_spellchecker($language);
|
||||
$spell_result = $spellchecker->check($message_body, $isHtml);
|
||||
|
||||
if ($error = $spellchecker->error()) {
|
||||
rcube::raise_error([
|
||||
'code' => 500, 'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Spellcheck error: " . $error
|
||||
],
|
||||
true, false
|
||||
);
|
||||
}
|
||||
else {
|
||||
$COMPOSE['spell_checked'] = true;
|
||||
|
||||
if (!$spell_result) {
|
||||
if ($isHtml) {
|
||||
$result['words'] = $spellchecker->get();
|
||||
$result['dictionary'] = (bool) $rcmail->config->get('spellcheck_dictionary');
|
||||
}
|
||||
else {
|
||||
$result = $spellchecker->get_xml();
|
||||
}
|
||||
|
||||
$rcmail->output->show_message('mispellingsfound', 'error');
|
||||
$rcmail->output->command('spellcheck_resume', $result);
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generic footer for all messages
|
||||
if ($footer = $SENDMAIL->generic_message_footer($isHtml)) {
|
||||
$message_body .= "\r\n" . $footer;
|
||||
}
|
||||
}
|
||||
|
||||
if ($isHtml) {
|
||||
$message_body .= "\r\n</body></html>\r\n";
|
||||
}
|
||||
|
||||
// sort attachments to make sure the order is the same as in the UI (#1488423)
|
||||
if ($files = rcube_utils::get_input_string('_attachments', rcube_utils::INPUT_POST)) {
|
||||
$files = explode(',', $files);
|
||||
$files = array_flip($files);
|
||||
foreach ($files as $idx => $val) {
|
||||
if (!empty($COMPOSE['attachments'][$idx])) {
|
||||
$files[$idx] = $COMPOSE['attachments'][$idx];
|
||||
unset($COMPOSE['attachments'][$idx]);
|
||||
}
|
||||
}
|
||||
|
||||
$COMPOSE['attachments'] = array_merge(array_filter($files), (array) $COMPOSE['attachments']);
|
||||
}
|
||||
|
||||
// Since we can handle big messages with disk usage, we need more time to work
|
||||
@set_time_limit(360);
|
||||
|
||||
// create PEAR::Mail_mime instance, set headers, body and params
|
||||
$MAIL_MIME = $SENDMAIL->create_message($headers, $message_body, $isHtml, $COMPOSE['attachments']);
|
||||
|
||||
// add stored attachments, if any
|
||||
if (is_array($COMPOSE['attachments'])) {
|
||||
self::add_attachments($SENDMAIL, $MAIL_MIME, $COMPOSE['attachments'], $isHtml);
|
||||
}
|
||||
|
||||
// compose PGP/Mime message
|
||||
if (!empty($pgp_mime)) {
|
||||
$MAIL_MIME->addAttachment(new Mail_mimePart('Version: 1', [
|
||||
'content_type' => 'application/pgp-encrypted',
|
||||
'description' => 'PGP/MIME version identification',
|
||||
]));
|
||||
|
||||
$MAIL_MIME->addAttachment(new Mail_mimePart($pgp_mime, [
|
||||
'content_type' => 'application/octet-stream',
|
||||
'filename' => 'encrypted.asc',
|
||||
'disposition' => 'inline',
|
||||
]));
|
||||
|
||||
$MAIL_MIME->setContentType('multipart/encrypted', ['protocol' => 'application/pgp-encrypted']);
|
||||
$MAIL_MIME->setParam('preamble', 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)');
|
||||
}
|
||||
|
||||
// This hook allows to modify the message before send or save action
|
||||
$plugin = $rcmail->plugins->exec_hook('message_ready', ['message' => $MAIL_MIME]);
|
||||
$MAIL_MIME = $plugin['message'];
|
||||
|
||||
// Deliver the message over SMTP
|
||||
if (!$savedraft && !$saveonly) {
|
||||
$sent = $SENDMAIL->deliver_message($MAIL_MIME);
|
||||
}
|
||||
|
||||
// Save the message in Drafts/Sent
|
||||
$saved = $SENDMAIL->save_message($MAIL_MIME);
|
||||
|
||||
// raise error if saving failed
|
||||
if (!$saved && $savedraft) {
|
||||
self::display_server_error('errorsaving');
|
||||
// start the auto-save timer again
|
||||
$rcmail->output->command('auto_save_start');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
$store_target = $SENDMAIL->options['store_target'];
|
||||
$store_folder = $SENDMAIL->options['store_folder'];
|
||||
|
||||
// delete previous saved draft
|
||||
$drafts_mbox = $rcmail->config->get('drafts_mbox');
|
||||
$old_id = rcube_utils::get_input_string('_draft_saveid', rcube_utils::INPUT_POST);
|
||||
|
||||
if ($old_id && (!empty($sent) || $saved)) {
|
||||
$deleted = $rcmail->storage->delete_message($old_id, $drafts_mbox);
|
||||
|
||||
// raise error if deletion of old draft failed
|
||||
if (!$deleted) {
|
||||
rcube::raise_error([
|
||||
'code' => 800,
|
||||
'type' => 'imap',
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Could not delete message from $drafts_mbox"
|
||||
],
|
||||
true, false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($savedraft) {
|
||||
// remember new draft-uid ($saved could be an UID or true/false here)
|
||||
if ($saved && is_bool($saved)) {
|
||||
$index = $rcmail->storage->search_once($drafts_mbox, 'HEADER Message-ID ' . $message_id);
|
||||
$saved = $index->max();
|
||||
}
|
||||
|
||||
if ($saved) {
|
||||
$plugin = $rcmail->plugins->exec_hook('message_draftsaved', [
|
||||
'msgid' => $message_id,
|
||||
'uid' => $saved,
|
||||
'folder' => $store_target
|
||||
]);
|
||||
|
||||
// display success
|
||||
$rcmail->output->show_message(!empty($plugin['message']) ? $plugin['message'] : 'messagesaved', 'confirmation');
|
||||
|
||||
// update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning
|
||||
$COMPOSE['param']['draft_uid'] = $plugin['uid'];
|
||||
$rcmail->output->command('set_draft_id', $plugin['uid']);
|
||||
$rcmail->output->command('compose_field_hash', true);
|
||||
}
|
||||
|
||||
// start the auto-save timer again
|
||||
$rcmail->output->command('auto_save_start');
|
||||
}
|
||||
else {
|
||||
// Collect folders which could contain the composed message,
|
||||
// we'll refresh the list if currently opened folder is one of them (#1490238)
|
||||
$folders = [];
|
||||
$save_error = false;
|
||||
|
||||
if (!$saveonly) {
|
||||
if (in_array($COMPOSE['mode'], ['reply', 'forward', 'draft'])) {
|
||||
$folders[] = $COMPOSE['mailbox'];
|
||||
}
|
||||
if (!empty($COMPOSE['param']['draft_uid']) && $drafts_mbox) {
|
||||
$folders[] = $drafts_mbox;
|
||||
}
|
||||
}
|
||||
|
||||
if ($store_folder && !$saved) {
|
||||
$params = $saveonly ? null : ['prefix' => true];
|
||||
self::display_server_error('errorsavingsent', null, null, $params);
|
||||
|
||||
if ($saveonly) {
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
$save_error = true;
|
||||
}
|
||||
else {
|
||||
$rcmail->plugins->exec_hook('attachments_cleanup', ['group' => $COMPOSE_ID]);
|
||||
$rcmail->session->remove('compose_data_' . $COMPOSE_ID);
|
||||
$_SESSION['last_compose_session'] = $COMPOSE_ID;
|
||||
|
||||
$rcmail->output->command('remove_compose_data', $COMPOSE_ID);
|
||||
|
||||
if ($store_folder) {
|
||||
$folders[] = $store_target;
|
||||
}
|
||||
}
|
||||
|
||||
$msg = $rcmail->gettext($saveonly ? 'successfullysaved' : 'messagesent');
|
||||
|
||||
$rcmail->output->command('sent_successfully', 'confirmation', $msg, $folders, $save_error);
|
||||
}
|
||||
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
|
||||
public static function add_attachments($SENDMAIL, $message, $attachments, $isHtml)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
foreach ($attachments as $id => $attachment) {
|
||||
// This hook retrieves the attachment contents from the file storage backend
|
||||
$attachment = $rcmail->plugins->exec_hook('attachment_get', $attachment);
|
||||
$is_inline = false;
|
||||
$dispurl = null;
|
||||
|
||||
if ($isHtml) {
|
||||
$dispurl = '/[\'"]\S+display-attachment\S+file=rcmfile' . preg_quote($attachment['id']) . '[\'"]/';
|
||||
$message_body = $message->getHTMLBody();
|
||||
$is_inline = preg_match($dispurl, $message_body);
|
||||
}
|
||||
|
||||
$ctype = isset($attachment['mimetype']) ? $attachment['mimetype'] : '';
|
||||
$ctype = str_replace('image/pjpeg', 'image/jpeg', $ctype); // #1484914
|
||||
|
||||
// inline image
|
||||
if ($is_inline) {
|
||||
// Mail_Mime does not support many inline attachments with the same name (#1489406)
|
||||
// we'll generate cid: urls here to workaround this
|
||||
$cid = preg_replace('/[^0-9a-zA-Z]/', '', uniqid(time(), true));
|
||||
if (preg_match('#(@[0-9a-zA-Z\-\.]+)#', $SENDMAIL->options['from'], $matches)) {
|
||||
$cid .= $matches[1];
|
||||
}
|
||||
else {
|
||||
$cid .= '@localhost';
|
||||
}
|
||||
|
||||
if ($dispurl && !empty($message_body)) {
|
||||
$message_body = preg_replace($dispurl, '"cid:' . $cid . '"', $message_body);
|
||||
|
||||
rcube_utils::preg_error([
|
||||
'line' => __LINE__,
|
||||
'file' => __FILE__,
|
||||
'message' => "Could not replace an image reference!"
|
||||
], true
|
||||
);
|
||||
|
||||
$message->setHTMLBody($message_body);
|
||||
}
|
||||
|
||||
if (!empty($attachment['data'])) {
|
||||
$message->addHTMLImage($attachment['data'], $ctype, $attachment['name'], false, $cid);
|
||||
}
|
||||
else {
|
||||
$message->addHTMLImage($attachment['path'], $ctype, $attachment['name'], true, $cid);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$file = !empty($attachment['data']) ? $attachment['data'] : $attachment['path'];
|
||||
$folding = (int) $rcmail->config->get('mime_param_folding');
|
||||
|
||||
$message->addAttachment($file,
|
||||
$ctype,
|
||||
$attachment['name'],
|
||||
empty($attachment['data']),
|
||||
$ctype == 'message/rfc822' ? '8bit' : 'base64',
|
||||
'attachment',
|
||||
$attachment['charset'] ?? null,
|
||||
'', '',
|
||||
$folding ? 'quoted-printable' : null,
|
||||
$folding == 2 ? 'quoted-printable' : null,
|
||||
'', RCUBE_CHARSET
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Send a message disposition notification for a specific mail |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_sendmdn extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_POST)) {
|
||||
$sent = self::send_mdn($uid, $smtp_error);
|
||||
}
|
||||
|
||||
// show either confirm or error message
|
||||
if (!empty($sent)) {
|
||||
$rcmail->output->set_env('mdn_request', false);
|
||||
$rcmail->output->show_message('receiptsent', 'confirmation');
|
||||
}
|
||||
else if (!empty($smtp_error) && is_string($smtp_error)) {
|
||||
$rcmail->output->show_message($smtp_error, 'error');
|
||||
}
|
||||
else if (!empty($smtp_error) && !empty($smtp_error['label'])) {
|
||||
$rcmail->output->show_message($smtp_error['label'], 'error', $smtp_error['vars']);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('errorsendingreceipt', 'error');
|
||||
}
|
||||
|
||||
// Redirect to 'addcontact' action to save the sender address
|
||||
if (!empty($_POST['_save'])) {
|
||||
if ($_POST['_save'] == 5) {
|
||||
$_POST['_source'] = rcube_addressbook::TYPE_TRUSTED_SENDER;
|
||||
}
|
||||
|
||||
$rcmail->action = 'addcontact';
|
||||
return;
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the MDN response
|
||||
*
|
||||
* @param mixed $message Original message object (rcube_message) or UID
|
||||
* @param array|string $smtp_error SMTP error array or (deprecated) string
|
||||
*
|
||||
* @return boolean Send status
|
||||
*/
|
||||
public static function send_mdn($message, &$smtp_error)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!is_object($message) || !is_a($message, 'rcube_message')) {
|
||||
$message = new rcube_message($message);
|
||||
}
|
||||
|
||||
if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
|
||||
($rcmail->storage->check_permflag('MDNSENT') || $rcmail->storage->check_permflag('*'))
|
||||
) {
|
||||
$charset = $message->headers->charset;
|
||||
$identity = rcmail_sendmail::identity_select($message);
|
||||
$sender = format_email_recipient($identity['email'], $identity['name']);
|
||||
$recipient = array_first(rcube_mime::decode_address_list($message->headers->mdn_to, 1, true, $charset));
|
||||
$mailto = $recipient['mailto'];
|
||||
|
||||
$compose = new Mail_mime("\r\n");
|
||||
|
||||
$compose->setParam('text_encoding', 'quoted-printable');
|
||||
$compose->setParam('html_encoding', 'quoted-printable');
|
||||
$compose->setParam('head_encoding', 'quoted-printable');
|
||||
$compose->setParam('head_charset', RCUBE_CHARSET);
|
||||
$compose->setParam('html_charset', RCUBE_CHARSET);
|
||||
$compose->setParam('text_charset', RCUBE_CHARSET);
|
||||
|
||||
// compose headers array
|
||||
$headers = [
|
||||
'Date' => $rcmail->user_date(),
|
||||
'From' => $sender,
|
||||
'To' => $message->headers->mdn_to,
|
||||
'Subject' => $rcmail->gettext('receiptread') . ': ' . $message->subject,
|
||||
'Message-ID' => $rcmail->gen_message_id($identity['email']),
|
||||
'X-Sender' => $identity['email'],
|
||||
'References' => trim($message->headers->references . ' ' . $message->headers->messageID),
|
||||
'In-Reply-To' => $message->headers->messageID,
|
||||
];
|
||||
|
||||
$report = "Final-Recipient: rfc822; {$identity['email']}\r\n"
|
||||
. "Original-Message-ID: {$message->headers->messageID}\r\n"
|
||||
. "Disposition: manual-action/MDN-sent-manually; displayed\r\n";
|
||||
|
||||
if ($message->headers->to) {
|
||||
$report .= "Original-Recipient: {$message->headers->to}\r\n";
|
||||
}
|
||||
|
||||
if ($agent = $rcmail->config->get('useragent')) {
|
||||
$headers['User-Agent'] = $agent;
|
||||
$report .= "Reporting-UA: $agent\r\n";
|
||||
}
|
||||
|
||||
$to = rcube_mime::decode_mime_string($message->headers->to, $charset);
|
||||
$date = $rcmail->format_date($message->headers->date, $rcmail->config->get('date_long'));
|
||||
$body = $rcmail->gettext("yourmessage") . "\r\n\r\n" .
|
||||
"\t" . $rcmail->gettext("to") . ": {$to}\r\n" .
|
||||
"\t" . $rcmail->gettext("subject") . ": {$message->subject}\r\n" .
|
||||
"\t" . $rcmail->gettext("date") . ": {$date}\r\n" .
|
||||
"\r\n" . $rcmail->gettext("receiptnote");
|
||||
|
||||
$compose->headers(array_filter($headers));
|
||||
$compose->setContentType('multipart/report', ['report-type'=> 'disposition-notification']);
|
||||
$compose->setTXTBody(rcube_mime::wordwrap($body, 75, "\r\n"));
|
||||
$compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
|
||||
|
||||
// SMTP options
|
||||
$options = ['mdn_use_from' => (bool) $rcmail->config->get('mdn_use_from')];
|
||||
|
||||
$sent = $rcmail->deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options, true);
|
||||
|
||||
if ($sent) {
|
||||
$rcmail->storage->set_flag($message->uid, 'MDNSENT');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,953 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Display a mail message similar as a usual mail application does |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_show extends rcmail_action_mail_index
|
||||
{
|
||||
protected static $MESSAGE;
|
||||
protected static $CLIENT_MIMETYPES = [];
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
self::$PRINT_MODE = $rcmail->action == 'print';
|
||||
|
||||
// Read browser capabilities and store them in session
|
||||
if ($caps = rcube_utils::get_input_string('_caps', rcube_utils::INPUT_GET)) {
|
||||
$browser_caps = [];
|
||||
foreach (explode(',', $caps) as $cap) {
|
||||
$cap = explode('=', $cap);
|
||||
$browser_caps[$cap[0]] = $cap[1];
|
||||
}
|
||||
|
||||
$_SESSION['browser_caps'] = $browser_caps;
|
||||
}
|
||||
|
||||
$msg_id = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET);
|
||||
$uid = preg_replace('/\.[0-9.]+$/', '', $msg_id);
|
||||
$mbox_name = $rcmail->storage->get_folder();
|
||||
|
||||
// similar code as in program/steps/mail/get.inc
|
||||
if ($uid) {
|
||||
// set message format (need to be done before rcube_message construction)
|
||||
if (!empty($_GET['_format'])) {
|
||||
$prefer_html = $_GET['_format'] == 'html';
|
||||
$rcmail->config->set('prefer_html', $prefer_html);
|
||||
$_SESSION['msg_formats'][$mbox_name.':'.$uid] = $prefer_html;
|
||||
}
|
||||
else if (isset($_SESSION['msg_formats'][$mbox_name.':'.$uid])) {
|
||||
$rcmail->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$uid]);
|
||||
}
|
||||
|
||||
$MESSAGE = new rcube_message($msg_id, $mbox_name, !empty($_GET['_safe']));
|
||||
|
||||
self::$MESSAGE = $MESSAGE;
|
||||
|
||||
// if message not found (wrong UID)...
|
||||
if (empty($MESSAGE->headers)) {
|
||||
self::message_error();
|
||||
}
|
||||
|
||||
self::$CLIENT_MIMETYPES = self::supported_mimetypes();
|
||||
|
||||
// show images?
|
||||
self::check_safe($MESSAGE);
|
||||
|
||||
// set message charset as default
|
||||
if (!empty($MESSAGE->headers->charset)) {
|
||||
$rcmail->storage->set_charset($MESSAGE->headers->charset);
|
||||
}
|
||||
|
||||
if (!isset($_SESSION['writeable_abook'])) {
|
||||
$_SESSION['writeable_abook'] = $rcmail->get_address_sources(true) ? true : false;
|
||||
}
|
||||
|
||||
$rcmail->output->set_pagetitle(abbreviate_string($MESSAGE->subject, 128, '...', true));
|
||||
|
||||
// set environment
|
||||
$rcmail->output->set_env('uid', $msg_id);
|
||||
$rcmail->output->set_env('safemode', $MESSAGE->is_safe);
|
||||
$rcmail->output->set_env('message_context', $MESSAGE->context);
|
||||
$rcmail->output->set_env('message_flags', array_keys(array_change_key_case((array) $MESSAGE->headers->flags)));
|
||||
$rcmail->output->set_env('sender', !empty($MESSAGE->sender) ? $MESSAGE->sender['string'] : '');
|
||||
$rcmail->output->set_env('mailbox', $mbox_name);
|
||||
$rcmail->output->set_env('username', $rcmail->get_user_name());
|
||||
$rcmail->output->set_env('permaurl', $rcmail->url(['_action' => 'show', '_uid' => $msg_id, '_mbox' => $mbox_name]));
|
||||
$rcmail->output->set_env('has_writeable_addressbook', $_SESSION['writeable_abook']);
|
||||
$rcmail->output->set_env('delimiter', $rcmail->storage->get_hierarchy_delimiter());
|
||||
$rcmail->output->set_env('mimetypes', self::$CLIENT_MIMETYPES);
|
||||
|
||||
if ($MESSAGE->headers->get('list-post', false)) {
|
||||
$rcmail->output->set_env('list_post', true);
|
||||
}
|
||||
|
||||
// set configuration
|
||||
self::set_env_config(['delete_junk', 'flag_for_deletion', 'read_when_deleted',
|
||||
'skip_deleted', 'display_next', 'forward_attachment', 'mailvelope_main_keyring']);
|
||||
|
||||
// set special folders
|
||||
foreach (['drafts', 'trash', 'junk'] as $mbox) {
|
||||
if ($folder = $rcmail->config->get($mbox . '_mbox')) {
|
||||
$rcmail->output->set_env($mbox . '_mailbox', $folder);
|
||||
}
|
||||
}
|
||||
|
||||
if ($MESSAGE->has_html_part()) {
|
||||
$prefer_html = $rcmail->config->get('prefer_html');
|
||||
$rcmail->output->set_env('optional_format', $prefer_html ? 'text' : 'html');
|
||||
}
|
||||
|
||||
$rcmail->output->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
|
||||
'movingmessage', 'deletingmessage', 'markingmessage', 'replyall', 'replylist',
|
||||
'bounce', 'bouncemsg', 'sendingmessage');
|
||||
|
||||
// check for unset disposition notification
|
||||
self::mdn_request_handler($MESSAGE);
|
||||
|
||||
if (empty($MESSAGE->headers->flags['SEEN']) && $MESSAGE->context === null) {
|
||||
$v = intval($rcmail->config->get('mail_read_time'));
|
||||
if ($v > 0) {
|
||||
$rcmail->output->set_env('mail_read_time', $v);
|
||||
}
|
||||
else if ($v == 0) {
|
||||
$rcmail->output->command('set_unread_message', $MESSAGE->uid, $mbox_name);
|
||||
$rcmail->plugins->exec_hook('message_read', [
|
||||
'uid' => $MESSAGE->uid,
|
||||
'mailbox' => $mbox_name,
|
||||
'message' => $MESSAGE,
|
||||
]);
|
||||
|
||||
$set_seen_flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'mailboxname' => [$this, 'mailbox_name_display'],
|
||||
'messageattachments' => [$this, 'message_attachments'],
|
||||
'messageobjects' => [$this, 'message_objects'],
|
||||
'messagesummary' => [$this, 'message_summary'],
|
||||
'messageheaders' => [$this, 'message_headers'],
|
||||
'messagefullheaders' => [$this, 'message_full_headers'],
|
||||
'messagebody' => [$this, 'message_body'],
|
||||
'contactphoto' => [$this, 'message_contactphoto'],
|
||||
]);
|
||||
|
||||
if ($rcmail->action == 'print' && $rcmail->output->template_exists('messageprint')) {
|
||||
$rcmail->output->send('messageprint', false);
|
||||
}
|
||||
else if ($rcmail->action == 'preview' && $rcmail->output->template_exists('messagepreview')) {
|
||||
$rcmail->output->send('messagepreview', false);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->send('message', false);
|
||||
}
|
||||
|
||||
// mark message as read
|
||||
if (!empty($set_seen_flag)) {
|
||||
if ($rcmail->storage->set_flag(self::$MESSAGE->uid, 'SEEN', $mbox_name)) {
|
||||
if ($count = self::get_unseen_count($mbox_name)) {
|
||||
self::set_unseen_count($mbox_name, $count - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the template object 'messageattachments'.
|
||||
*
|
||||
* @param array $attrib Named parameters
|
||||
*
|
||||
* @return string HTML content showing the message attachments list
|
||||
*/
|
||||
public static function message_attachments($attrib)
|
||||
{
|
||||
if (empty(self::$MESSAGE->attachments)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$out =
|
||||
$ol = '';
|
||||
$attachments = [];
|
||||
|
||||
foreach (self::$MESSAGE->attachments as $attach_prop) {
|
||||
$filename = self::attachment_name($attach_prop, true);
|
||||
$filesize = self::message_part_size($attach_prop);
|
||||
$mimetype = rcube_mime::fix_mimetype($attach_prop->mimetype);
|
||||
$class = rcube_utils::file2class($mimetype, $filename);
|
||||
$id = 'attach' . $attach_prop->mime_id;
|
||||
|
||||
if ($mimetype == 'application/octet-stream' && ($type = rcube_mime::file_ext_type($filename))) {
|
||||
$mimetype = $type;
|
||||
}
|
||||
|
||||
// Skip inline images
|
||||
if (strpos($mimetype, 'image/') === 0 && !self::is_attachment(self::$MESSAGE, $attach_prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($attrib['maxlength']) && mb_strlen($filename) > $attrib['maxlength']) {
|
||||
$title = $filename;
|
||||
$filename = abbreviate_string($filename, $attrib['maxlength']);
|
||||
}
|
||||
else {
|
||||
$title = '';
|
||||
}
|
||||
|
||||
$item = html::span('attachment-name', rcube::Q($filename))
|
||||
. html::span('attachment-size', '(' . rcube::Q($filesize) . ')');
|
||||
|
||||
$li_class = $class;
|
||||
|
||||
if (!self::$PRINT_MODE) {
|
||||
$link_attrs = [
|
||||
'href' => self::$MESSAGE->get_part_url($attach_prop->mime_id, false),
|
||||
'onclick' => sprintf('%s.command(\'load-attachment\',\'%s\',this); return false',
|
||||
rcmail_output::JS_OBJECT_NAME, $attach_prop->mime_id),
|
||||
'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)',
|
||||
'title' => $title,
|
||||
'class' => 'filename',
|
||||
];
|
||||
|
||||
if ($mimetype != 'message/rfc822' && empty($attach_prop->size)) {
|
||||
$li_class .= ' no-menu';
|
||||
$link_attrs['onclick'] = sprintf('%s.alert_dialog(%s.get_label(\'emptyattachment\')); return false',
|
||||
rcmail_output::JS_OBJECT_NAME, rcmail_output::JS_OBJECT_NAME);
|
||||
$rcmail->output->add_label('emptyattachment');
|
||||
}
|
||||
|
||||
$item = html::a($link_attrs, $item);
|
||||
$attachments[$attach_prop->mime_id] = $mimetype;
|
||||
}
|
||||
|
||||
$ol .= html::tag('li', ['class' => $li_class, 'id' => $id], $item);
|
||||
}
|
||||
|
||||
$out = html::tag('ul', $attrib, $ol, html::$common_attrib);
|
||||
|
||||
$rcmail->output->set_env('attachments', $attachments);
|
||||
$rcmail->output->add_gui_object('attachments', $attrib['id']);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function remote_objects_msg()
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$attrib['id'] = 'remote-objects-message';
|
||||
$attrib['class'] = 'notice';
|
||||
$attrib['style'] = 'display: none';
|
||||
|
||||
$msg = html::span(null, rcube::Q($rcmail->gettext('blockedresources')));
|
||||
|
||||
$buttons = html::a([
|
||||
'href' => "#loadremote",
|
||||
'onclick' => rcmail_output::JS_OBJECT_NAME . ".command('load-remote')"
|
||||
],
|
||||
rcube::Q($rcmail->gettext('allow'))
|
||||
);
|
||||
|
||||
// add link to save sender in addressbook and reload message
|
||||
$show_images = $rcmail->config->get('show_images');
|
||||
if (!empty(self::$MESSAGE->sender['mailto']) && ($show_images == 1 || $show_images == 3)) {
|
||||
$arg = $show_images == 3 ? rcube_addressbook::TYPE_TRUSTED_SENDER : 'true';
|
||||
$buttons .= ' ' . html::a([
|
||||
'href' => "#loadremotealways",
|
||||
'onclick' => rcmail_output::JS_OBJECT_NAME . ".command('load-remote', $arg)",
|
||||
'style' => "white-space:nowrap"
|
||||
],
|
||||
rcube::Q($rcmail->gettext(['name' => 'alwaysallow', 'vars' => ['sender' => self::$MESSAGE->sender['mailto']]]))
|
||||
);
|
||||
}
|
||||
|
||||
$rcmail->output->add_gui_object('remoteobjectsmsg', $attrib['id']);
|
||||
|
||||
return html::div($attrib, $msg . ' ' . html::span('boxbuttons', $buttons));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a warning whenever a suspicious email address has been found in the message.
|
||||
*
|
||||
* @return string HTML content of the warning element
|
||||
*/
|
||||
public static function suspicious_content_warning()
|
||||
{
|
||||
if (empty(self::$SUSPICIOUS_EMAIL)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$attrib = [
|
||||
'id' => 'suspicious-content-message',
|
||||
'class' => 'notice',
|
||||
];
|
||||
|
||||
$msg = html::span(null, rcube::Q($rcmail->gettext('suspiciousemail')));
|
||||
|
||||
return html::div($attrib, $msg);
|
||||
}
|
||||
|
||||
public static function message_buttons()
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$delim = $rcmail->storage->get_hierarchy_delimiter();
|
||||
$dbox = $rcmail->config->get('drafts_mbox');
|
||||
|
||||
// the message is not a draft
|
||||
if (!empty(self::$MESSAGE->context)
|
||||
|| (
|
||||
!empty(self::$MESSAGE->folder)
|
||||
&& (self::$MESSAGE->folder != $dbox && strpos(self::$MESSAGE->folder, $dbox.$delim) !== 0)
|
||||
)
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$attrib['id'] = 'message-buttons';
|
||||
$attrib['class'] = 'information notice';
|
||||
|
||||
$msg = html::span(null, rcube::Q($rcmail->gettext('isdraft')))
|
||||
. ' '
|
||||
. html::a([
|
||||
'href' => "#edit",
|
||||
'onclick' => rcmail_output::JS_OBJECT_NAME.".command('edit')"
|
||||
],
|
||||
rcube::Q($rcmail->gettext('edit'))
|
||||
);
|
||||
|
||||
return html::div($attrib, $msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the template object 'messageobjects' that contains
|
||||
* warning/info boxes, buttons, etc. related to the displayed message.
|
||||
*
|
||||
* @param array $attrib Named parameters
|
||||
*
|
||||
* @return string HTML content showing the message objects
|
||||
*/
|
||||
public static function message_objects($attrib)
|
||||
{
|
||||
if (empty($attrib['id'])) {
|
||||
$attrib['id'] = 'message-objects';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$content = [
|
||||
self::message_buttons(),
|
||||
self::remote_objects_msg(),
|
||||
self::suspicious_content_warning(),
|
||||
];
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('message_objects',
|
||||
['content' => $content, 'message' => self::$MESSAGE]);
|
||||
|
||||
$content = implode("\n", $plugin['content']);
|
||||
|
||||
return html::div($attrib, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the template object 'contactphoto'.
|
||||
*
|
||||
* @param array $attrib Named parameters
|
||||
*
|
||||
* @return string HTML content for the IMG tag
|
||||
*/
|
||||
public static function message_contactphoto($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$error_handler = false;
|
||||
$placeholder = 'data:image/gif;base64,' . rcmail_output::BLANK_GIF;
|
||||
|
||||
if (!empty($attrib['placeholder'])) {
|
||||
$placeholder = $rcmail->output->abs_url($attrib['placeholder'], true);
|
||||
$placeholder = $rcmail->output->asset_url($placeholder);
|
||||
|
||||
// set error handler on <img>
|
||||
$error_handler = true;
|
||||
$attrib['onerror'] = "this.onerror = null; this.src = '$placeholder';";
|
||||
}
|
||||
|
||||
if (!empty(self::$MESSAGE->sender)) {
|
||||
$photo_img = $rcmail->url([
|
||||
'_task' => 'addressbook',
|
||||
'_action' => 'photo',
|
||||
'_email' => self::$MESSAGE->sender['mailto'],
|
||||
'_error' => $error_handler ? 1 : null,
|
||||
'_bgcolor' => $attrib['bg-color'] ?? null
|
||||
]);
|
||||
}
|
||||
else {
|
||||
$photo_img = $placeholder;
|
||||
}
|
||||
|
||||
return html::img(['src' => $photo_img, 'alt' => $rcmail->gettext('contactphoto')] + $attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns table with message headers
|
||||
*/
|
||||
public static function message_headers($attrib, $headers = null)
|
||||
{
|
||||
static $sa_attrib;
|
||||
|
||||
// keep header table attrib
|
||||
if (is_array($attrib) && !$sa_attrib && empty($attrib['valueof'])) {
|
||||
$sa_attrib = $attrib;
|
||||
}
|
||||
else if (!is_array($attrib) && is_array($sa_attrib)) {
|
||||
$attrib = $sa_attrib;
|
||||
}
|
||||
|
||||
if (!isset(self::$MESSAGE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
// get associative array of headers object
|
||||
if (!$headers) {
|
||||
$headers_obj = self::$MESSAGE->headers;
|
||||
$headers = get_object_vars(self::$MESSAGE->headers);
|
||||
}
|
||||
else if (is_object($headers)) {
|
||||
$headers_obj = $headers;
|
||||
$headers = get_object_vars($headers_obj);
|
||||
}
|
||||
else {
|
||||
$headers_obj = rcube_message_header::from_array($headers);
|
||||
}
|
||||
|
||||
// show these headers
|
||||
$standard_headers = ['subject', 'from', 'sender', 'to', 'cc', 'bcc', 'replyto',
|
||||
'mail-reply-to', 'mail-followup-to', 'date', 'priority'];
|
||||
$exclude_headers = !empty($attrib['exclude']) ? explode(',', $attrib['exclude']) : [];
|
||||
$output_headers = [];
|
||||
|
||||
$attr_max = $attrib['max'] ?? null;
|
||||
$attr_addicon = $attrib['addicon'] ?? null;
|
||||
$charset = !empty($headers['charset']) ? $headers['charset'] : null;
|
||||
|
||||
foreach ($standard_headers as $hkey) {
|
||||
$value = null;
|
||||
if (!empty($headers[$hkey])) {
|
||||
$value = $headers[$hkey];
|
||||
}
|
||||
else if (!empty($headers['others'][$hkey])) {
|
||||
$value = $headers['others'][$hkey];
|
||||
}
|
||||
else if (empty($attrib['valueof'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($hkey, $exclude_headers)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ishtml = false;
|
||||
$header_title = $rcmail->gettext(preg_replace('/(^mail-|-)/', '', $hkey));
|
||||
$header_value = null;
|
||||
|
||||
if ($hkey == 'date') {
|
||||
$header_value = $rcmail->format_date($value,
|
||||
self::$PRINT_MODE ? $rcmail->config->get('date_long', 'x') : null);
|
||||
}
|
||||
else if ($hkey == 'priority') {
|
||||
$header_value = html::span('prio' . $value, rcube::Q(self::localized_priority($value)));
|
||||
$ishtml = true;
|
||||
}
|
||||
else if ($hkey == 'replyto') {
|
||||
if ($value != $headers['from']) {
|
||||
$header_value = self::address_string($value, $attr_max, true, $attr_addicon, $charset, $header_title);
|
||||
$ishtml = true;
|
||||
}
|
||||
}
|
||||
else if ($hkey == 'mail-reply-to') {
|
||||
if ((!isset($headers['replyto']) || $value != $headers['replyto']) && $value != $headers['from']) {
|
||||
$header_value = self::address_string($value, $attr_max, true, $attr_addicon, $charset, $header_title);
|
||||
$ishtml = true;
|
||||
}
|
||||
}
|
||||
else if ($hkey == 'sender') {
|
||||
if ($value != $headers['from']) {
|
||||
$header_value = self::address_string($value, $attr_max, true, $attr_addicon, $charset, $header_title);
|
||||
$ishtml = true;
|
||||
}
|
||||
}
|
||||
else if ($hkey == 'mail-followup-to') {
|
||||
$header_value = self::address_string($value, $attr_max, true, $attr_addicon, $charset, $header_title);
|
||||
$ishtml = true;
|
||||
}
|
||||
else if (in_array($hkey, ['from', 'to', 'cc', 'bcc'])) {
|
||||
$header_value = self::address_string($value, $attr_max, true, $attr_addicon, $charset, $header_title);
|
||||
$ishtml = true;
|
||||
}
|
||||
else if ($hkey == 'subject' && empty($value)) {
|
||||
$header_value = $rcmail->gettext('nosubject');
|
||||
}
|
||||
else {
|
||||
$value = is_array($value) ? implode(' ', $value) : $value;
|
||||
$header_value = trim(rcube_mime::decode_header($value, $charset));
|
||||
}
|
||||
|
||||
if (empty($header_value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output_headers[$hkey] = [
|
||||
'title' => $header_title,
|
||||
'value' => $header_value,
|
||||
'raw' => $value,
|
||||
'html' => $ishtml,
|
||||
];
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('message_headers_output', [
|
||||
'output' => $output_headers,
|
||||
'headers' => $headers_obj,
|
||||
'exclude' => $exclude_headers, // readonly
|
||||
'folder' => self::$MESSAGE->folder, // readonly
|
||||
'uid' => self::$MESSAGE->uid, // readonly
|
||||
]);
|
||||
|
||||
// single header value is requested
|
||||
if (!empty($attrib['valueof'])) {
|
||||
if (empty($plugin['output'][$attrib['valueof']])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$row = $plugin['output'][$attrib['valueof']];
|
||||
return !empty($row['html']) ? $row['value'] : rcube::SQ($row['value']);
|
||||
}
|
||||
|
||||
// compose html table
|
||||
$table = new html_table(['cols' => 2]);
|
||||
|
||||
foreach ($plugin['output'] as $hkey => $row) {
|
||||
$val = !empty($row['html']) ? $row['value'] : rcube::SQ($row['value']);
|
||||
|
||||
$table->add(['class' => 'header-title'], rcube::SQ($row['title']));
|
||||
$table->add(['class' => 'header ' . $hkey], $val);
|
||||
}
|
||||
|
||||
return $table->show($attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns element with "From|To <sender|recipient> on <date>"
|
||||
*/
|
||||
public static function message_summary($attrib)
|
||||
{
|
||||
if (!isset(self::$MESSAGE) || empty(self::$MESSAGE->headers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$header = self::$MESSAGE->context ? 'from' : self::message_list_smart_column_name();
|
||||
$label = 'shortheader' . $header;
|
||||
$date = $rcmail->format_date(self::$MESSAGE->headers->date, $rcmail->config->get('date_long', 'x'));
|
||||
$user = self::$MESSAGE->headers->$header;
|
||||
|
||||
if (!$user && $header == 'to' && !empty(self::$MESSAGE->headers->cc)) {
|
||||
$user = self::$MESSAGE->headers->cc;
|
||||
}
|
||||
if (!$user && $header == 'to' && !empty(self::$MESSAGE->headers->bcc)) {
|
||||
$user = self::$MESSAGE->headers->bcc;
|
||||
}
|
||||
|
||||
$vars[$header] = self::address_string($user, 1, true, $attrib['addicon'], self::$MESSAGE->headers->charset);
|
||||
$vars['date'] = html::span('text-nowrap', $date);
|
||||
|
||||
if (empty($user)) {
|
||||
$label = 'shortheaderdate';
|
||||
}
|
||||
|
||||
$out = html::span(null, $rcmail->gettext(['name' => $label, 'vars' => $vars]));
|
||||
|
||||
return html::div($attrib, $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Priority header value into a localized string
|
||||
*/
|
||||
public static function localized_priority($value)
|
||||
{
|
||||
$labels_map = [
|
||||
'1' => 'highest',
|
||||
'2' => 'high',
|
||||
'3' => 'normal',
|
||||
'4' => 'low',
|
||||
'5' => 'lowest',
|
||||
];
|
||||
|
||||
if ($value && !empty($labels_map[$value])) {
|
||||
return rcmail::get_instance()->gettext($labels_map[$value]);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns block to show full message headers
|
||||
*/
|
||||
public static function message_full_headers($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$html = html::div(['id' => "all-headers", 'class' => "all", 'style' => 'display:none'],
|
||||
html::div(['id' => 'headers-source'], ''));
|
||||
|
||||
$html .= html::div([
|
||||
'class' => "more-headers show-headers",
|
||||
'onclick' => "return ".rcmail_output::JS_OBJECT_NAME.".command('show-headers','',this)",
|
||||
'title' => $rcmail->gettext('togglefullheaders')
|
||||
], '');
|
||||
|
||||
$rcmail->output->add_gui_object('all_headers_row', 'all-headers');
|
||||
$rcmail->output->add_gui_object('all_headers_box', 'headers-source');
|
||||
|
||||
return html::div($attrib, $html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the 'messagebody' GUI object
|
||||
*
|
||||
* @param array $attrib Named parameters
|
||||
*
|
||||
* @return string HTML content showing the message body
|
||||
*/
|
||||
public static function message_body($attrib)
|
||||
{
|
||||
if (
|
||||
empty(self::$MESSAGE)
|
||||
|| (!is_array(self::$MESSAGE->parts) && empty(self::$MESSAGE->body))
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (empty($attrib['id'])) {
|
||||
$attrib['id'] = 'rcmailMsgBody';
|
||||
}
|
||||
|
||||
$rcmail = rcmail::get_instance();
|
||||
$safe_mode = self::$MESSAGE->is_safe || !empty($_GET['_safe']);
|
||||
$out = '';
|
||||
$part_no = 0;
|
||||
|
||||
$header_attrib = [];
|
||||
foreach ($attrib as $attr => $value) {
|
||||
if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs)) {
|
||||
$header_attrib[$regs[1]] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty(self::$MESSAGE->parts)) {
|
||||
foreach (self::$MESSAGE->parts as $part) {
|
||||
if ($part->type == 'headers') {
|
||||
$out .= html::div('message-partheaders', self::message_headers(count($header_attrib) ? $header_attrib : null, $part->headers));
|
||||
}
|
||||
else if ($part->type == 'content') {
|
||||
// unsupported (e.g. encrypted)
|
||||
if (!empty($part->realtype)) {
|
||||
if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') {
|
||||
if (
|
||||
!empty($_SESSION['browser_caps']['pgpmime'])
|
||||
&& ($pgp_mime_part = self::$MESSAGE->get_multipart_encrypted_part())
|
||||
) {
|
||||
$out .= html::span('part-notice', $rcmail->gettext('externalmessagedecryption'));
|
||||
$rcmail->output->set_env('pgp_mime_part', $pgp_mime_part->mime_id);
|
||||
$rcmail->output->set_env('pgp_mime_container', '#' . $attrib['id']);
|
||||
$rcmail->output->add_label('loadingdata');
|
||||
}
|
||||
|
||||
if (!self::$MESSAGE->encrypted_part) {
|
||||
$out .= html::span('part-notice', $rcmail->gettext('encryptedmessage'));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (!$part->size) {
|
||||
continue;
|
||||
}
|
||||
// Check if we have enough memory to handle the message in it
|
||||
// #1487424: we need up to 10x more memory than the body
|
||||
else if (!rcube_utils::mem_check($part->size * 10)) {
|
||||
$out .= self::part_too_big_message(self::$MESSAGE, $part->mime_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// fetch part body
|
||||
$body = self::$MESSAGE->get_part_body($part->mime_id, true);
|
||||
|
||||
// message is cached but not exists (#1485443), or other error
|
||||
if ($body === false) {
|
||||
// Don't bail out if it is only one-of-many part of the message (#6854)
|
||||
if (strlen($out)) {
|
||||
$out .= html::span('part-notice', $rcmail->gettext('messageopenerror'));
|
||||
continue;
|
||||
}
|
||||
|
||||
self::message_error();
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('message_body_prefix',
|
||||
['part' => $part, 'prefix' => '', 'message' => self::$MESSAGE]);
|
||||
|
||||
// Set attributes of the part container
|
||||
$container_class = $part->ctype_secondary == 'html' ? 'message-htmlpart' : 'message-part';
|
||||
$container_id = $container_class . (++$part_no);
|
||||
$container_attrib = ['class' => $container_class, 'id' => $container_id];
|
||||
|
||||
$body_args = [
|
||||
'safe' => $safe_mode,
|
||||
'plain' => !$rcmail->config->get('prefer_html'),
|
||||
'css_prefix' => 'v' . $part_no,
|
||||
'body_class' => 'rcmBody',
|
||||
'container_id' => $container_id,
|
||||
'container_attrib' => $container_attrib,
|
||||
];
|
||||
|
||||
// Parse the part content for display
|
||||
$body = self::print_body($body, $part, $body_args);
|
||||
|
||||
// check if the message body is PGP encrypted
|
||||
if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) {
|
||||
$rcmail->output->set_env('is_pgp_content', '#' . $container_id);
|
||||
}
|
||||
|
||||
if ($part->ctype_secondary == 'html') {
|
||||
$body = self::html4inline($body, $body_args);
|
||||
}
|
||||
|
||||
$out .= html::div($body_args['container_attrib'], $plugin['prefix'] . $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Check if we have enough memory to handle the message in it
|
||||
// #1487424: we need up to 10x more memory than the body
|
||||
if (isset(self::$MESSAGE->body) && !rcube_utils::mem_check(strlen(self::$MESSAGE->body) * 10)) {
|
||||
$out .= self::part_too_big_message(self::$MESSAGE, 0);
|
||||
}
|
||||
else {
|
||||
$plugin = $rcmail->plugins->exec_hook('message_body_prefix',
|
||||
['part' => self::$MESSAGE, 'prefix' => '']);
|
||||
|
||||
$out .= html::div('message-part',
|
||||
$plugin['prefix'] . self::plain_body(self::$MESSAGE->body));
|
||||
}
|
||||
}
|
||||
|
||||
// list images after mail body
|
||||
if ($rcmail->config->get('inline_images', true) && !empty(self::$MESSAGE->attachments)) {
|
||||
$thumbnail_size = $rcmail->config->get('image_thumbnail_size', 240);
|
||||
$show_label = rcube::Q($rcmail->gettext('showattachment'));
|
||||
$download_label = rcube::Q($rcmail->gettext('download'));
|
||||
|
||||
foreach (self::$MESSAGE->attachments as $attach_prop) {
|
||||
// Content-Type: image/*...
|
||||
if ($mimetype = self::part_image_type($attach_prop)) {
|
||||
// Skip inline images
|
||||
if (!self::is_attachment(self::$MESSAGE, $attach_prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// display thumbnails
|
||||
if ($thumbnail_size) {
|
||||
$supported = in_array($mimetype, self::$CLIENT_MIMETYPES);
|
||||
$show_link_attr = [
|
||||
'href' => self::$MESSAGE->get_part_url($attach_prop->mime_id, false),
|
||||
'onclick' => sprintf(
|
||||
'%s.command(\'load-attachment\',\'%s\',this); return false',
|
||||
rcmail_output::JS_OBJECT_NAME,
|
||||
$attach_prop->mime_id
|
||||
)
|
||||
];
|
||||
$download_link_attr = [
|
||||
'href' => $show_link_attr['href'] . '&_download=1',
|
||||
];
|
||||
$show_link = html::a($show_link_attr + ['class' => 'open'], $show_label);
|
||||
$download_link = html::a($download_link_attr + ['class' => 'download'], $download_label);
|
||||
|
||||
$out .= html::p(['class' => 'image-attachment', 'style' => $supported ? '' : 'display:none'],
|
||||
html::a($show_link_attr + ['class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)],
|
||||
html::img([
|
||||
'class' => 'image-thumbnail',
|
||||
'src' => self::$MESSAGE->get_part_url($attach_prop->mime_id, 'image') . '&_thumb=1',
|
||||
'title' => $attach_prop->filename,
|
||||
'alt' => $attach_prop->filename,
|
||||
'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size),
|
||||
'onload' => $supported ? '' : '$(this).parents(\'p.image-attachment\').show()',
|
||||
])
|
||||
) .
|
||||
html::span('image-filename', rcube::Q($attach_prop->filename)) .
|
||||
html::span('image-filesize', rcube::Q(self::message_part_size($attach_prop))) .
|
||||
html::span('attachment-links', ($supported ? $show_link . ' ' : '') . $download_link) .
|
||||
html::br(['style' => 'clear:both'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
$out .= html::tag('fieldset', 'image-attachment',
|
||||
html::tag('legend', 'image-filename', rcube::Q($attach_prop->filename)) .
|
||||
html::p(['align' => 'center'],
|
||||
html::img([
|
||||
'src' => self::$MESSAGE->get_part_url($attach_prop->mime_id, 'image'),
|
||||
'title' => $attach_prop->filename,
|
||||
'alt' => $attach_prop->filename,
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tell client that there are blocked remote objects
|
||||
if (self::$REMOTE_OBJECTS && !$safe_mode) {
|
||||
$rcmail->output->set_env('blockedobjects', true);
|
||||
}
|
||||
|
||||
$rcmail->output->add_gui_object('messagebody', $attrib['id']);
|
||||
|
||||
return html::div($attrib, $out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a HTML notice element for too big message parts
|
||||
*
|
||||
* @param rcube_message $message Email message object
|
||||
* @param string $part_id Message part identifier
|
||||
*
|
||||
* @return string HTML content
|
||||
*/
|
||||
public static function part_too_big_message($message, $part_id)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$token = $rcmail->get_request_token();
|
||||
$url = $rcmail->url([
|
||||
'task' => 'mail',
|
||||
'action' => 'get',
|
||||
'download' => 1,
|
||||
'uid' => $message->uid,
|
||||
'part' => $part_id,
|
||||
'mbox' => $message->folder,
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
return html::span('part-notice', $rcmail->gettext('messagetoobig')
|
||||
. ' ' . html::a($url, $rcmail->gettext('download')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle disposition notification requests
|
||||
*
|
||||
* @param rcube_message $message Email message object
|
||||
*/
|
||||
public static function mdn_request_handler($message)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($message->headers->mdn_to
|
||||
&& $message->context === null
|
||||
&& !empty($message->sender['mailto'])
|
||||
&& empty($message->headers->flags['MDNSENT'])
|
||||
&& empty($message->headers->flags['SEEN'])
|
||||
&& ($rcmail->storage->check_permflag('MDNSENT') || $rcmail->storage->check_permflag('*'))
|
||||
&& $message->folder != $rcmail->config->get('drafts_mbox')
|
||||
&& $message->folder != $rcmail->config->get('sent_mbox')
|
||||
) {
|
||||
$mdn_cfg = intval($rcmail->config->get('mdn_requests'));
|
||||
$exists = $mdn_cfg == 1;
|
||||
|
||||
// Check sender existence in contacts
|
||||
// 3 and 4 = my contacts, 5 and 6 = trusted senders
|
||||
if ($mdn_cfg == 3 || $mdn_cfg == 4 || $mdn_cfg == 5 || $mdn_cfg == 6) {
|
||||
$type = rcube_addressbook::TYPE_TRUSTED_SENDER;
|
||||
|
||||
if ($mdn_cfg == 3 || $mdn_cfg == 4) {
|
||||
$type |= rcube_addressbook::TYPE_WRITEABLE | rcube_addressbook::TYPE_RECIPIENT;
|
||||
}
|
||||
|
||||
if ($rcmail->contact_exists($message->sender['mailto'], $type)) {
|
||||
$exists = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($exists) {
|
||||
// Send MDN
|
||||
if (rcmail_action_mail_sendmdn::send_mdn($message, $smtp_error)) {
|
||||
$rcmail->output->show_message('receiptsent', 'confirmation');
|
||||
}
|
||||
else if ($smtp_error && is_string($smtp_error)) {
|
||||
$rcmail->output->show_message($smtp_error, 'error');
|
||||
}
|
||||
else if ($smtp_error && !empty($smtp_error['label'])) {
|
||||
$rcmail->output->show_message($smtp_error['label'], 'error', $smtp_error['vars']);
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('errorsendingreceipt', 'error');
|
||||
}
|
||||
}
|
||||
else if ($mdn_cfg != 2 && $mdn_cfg != 4 && $mdn_cfg != 6) {
|
||||
// Ask the user
|
||||
$rcmail->output->add_label('sendreceipt', 'mdnrequest', 'send', 'sendalwaysto', 'ignore');
|
||||
$rcmail->output->set_env('mdn_request_save', $mdn_cfg == 3 || $mdn_cfg == 5 ? $mdn_cfg : 0);
|
||||
$rcmail->output->set_env('mdn_request_sender', $message->sender);
|
||||
$rcmail->output->set_env('mdn_request', true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the message part is a normal attachment
|
||||
*
|
||||
* @param rcube_message $message Message object
|
||||
* @param rcube_message_part $part Message part
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function is_attachment($message, $part)
|
||||
{
|
||||
// Inline attachment with Content-Id specified
|
||||
if (!empty($part->content_id) && $part->disposition == 'inline') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Any image attached to multipart/related message (#7184)
|
||||
$parent_id = preg_replace('/\.[0-9]+$/', '', $part->mime_id);
|
||||
$parent = $message->mime_parts[$parent_id] ?? null;
|
||||
|
||||
if ($parent && $parent->mimetype == 'multipart/related') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Display a mail message similar as a usual mail application does |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_mail_viewsource extends rcmail_action
|
||||
{
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (!empty($_GET['_save'])) {
|
||||
$rcmail->request_security_check(rcube_utils::INPUT_GET);
|
||||
}
|
||||
|
||||
ob_end_clean();
|
||||
|
||||
// similar code as in program/steps/mail/get.inc
|
||||
if ($uid = rcube_utils::get_input_string('_uid', rcube_utils::INPUT_GET)) {
|
||||
if ($pos = strpos($uid, '.')) {
|
||||
$message = new rcube_message($uid);
|
||||
$headers = $message->headers;
|
||||
$part_id = substr($uid, $pos + 1);
|
||||
}
|
||||
else {
|
||||
$headers = $rcmail->storage->get_message_headers($uid);
|
||||
}
|
||||
|
||||
$charset = $headers->charset ?: $rcmail->config->get('default_charset', RCUBE_CHARSET);
|
||||
|
||||
if (!empty($_GET['_save'])) {
|
||||
$subject = rcube_mime::decode_header($headers->subject, $headers->charset);
|
||||
$filename = self::filename_from_subject(mb_substr($subject, 0, 128));
|
||||
$filename = ($filename ?: $uid) . '.eml';
|
||||
|
||||
$rcmail->output->download_headers($filename, [
|
||||
'length' => $headers->size,
|
||||
'type' => 'text/plain',
|
||||
'type_charset' => $charset,
|
||||
]);
|
||||
}
|
||||
else {
|
||||
// Make sure it works in an iframe (#9084)
|
||||
$rcmail->output->page_headers();
|
||||
|
||||
header("Content-Type: text/plain; charset={$charset}");
|
||||
}
|
||||
|
||||
if (isset($part_id) && isset($message)) {
|
||||
$message->get_part_body($part_id, empty($_GET['_save']), 0, -1);
|
||||
}
|
||||
else {
|
||||
$rcmail->storage->print_raw_body($uid, empty($_GET['_save']));
|
||||
}
|
||||
}
|
||||
else {
|
||||
rcube::raise_error([
|
||||
'code' => 500,
|
||||
'file' => __FILE__,
|
||||
'line' => __LINE__,
|
||||
'message' => "Message UID $uid not found"
|
||||
],
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert message subject into filename
|
||||
*/
|
||||
public static function filename_from_subject($str)
|
||||
{
|
||||
$str = preg_replace('/[:\t\n\r\0\x0B\/]+\s*/', ' ', $str);
|
||||
|
||||
return trim($str, " \t\n\r\0\x0B./_");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| Copyright (C) Kolab Systems AG |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Display license information about program and enabled plugins |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_about extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$rcmail->output->set_pagetitle($rcmail->gettext('about'));
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'supportlink' => [$this, 'supportlink'],
|
||||
'pluginlist' => [$this, 'plugins_list'],
|
||||
'copyright' => function() {
|
||||
return 'Copyright © 2005-2022, The Roundcube Dev Team';
|
||||
},
|
||||
'license' => function() {
|
||||
return 'This program is free software; you can redistribute it and/or modify it under the terms '
|
||||
. 'of the <a href="http://www.gnu.org/licenses/gpl.html" target="_blank">GNU General Public License</a> '
|
||||
. 'as published by the Free Software Foundation, either version 3 of the License, '
|
||||
. 'or (at your option) any later version.<br/>'
|
||||
. 'Some <a href="https://roundcube.net/license" target="_blank">exceptions</a> '
|
||||
. 'for skins & plugins apply.';
|
||||
},
|
||||
]);
|
||||
|
||||
$rcmail->output->send('about');
|
||||
}
|
||||
|
||||
public static function supportlink($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if ($url = $rcmail->config->get('support_url')) {
|
||||
$label = !empty($attrib['label']) ? $attrib['label'] : 'support';
|
||||
$attrib['href'] = $url;
|
||||
|
||||
return html::a($attrib, $rcmail->gettext($label));
|
||||
}
|
||||
}
|
||||
|
||||
public static function plugins_list($attrib)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
if (empty($attrib['id'])) {
|
||||
$attrib['id'] = 'rcmpluginlist';
|
||||
}
|
||||
|
||||
$plugins = array_filter($rcmail->plugins->active_plugins);
|
||||
$plugin_info = [];
|
||||
|
||||
foreach ($plugins as $name) {
|
||||
if ($info = $rcmail->plugins->get_info($name)) {
|
||||
$plugin_info[$name] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
// load info from required plugins, too
|
||||
foreach ($plugin_info as $name => $info) {
|
||||
if (!empty($info['require']) && is_array($info['require'])) {
|
||||
foreach ($info['require'] as $req_name) {
|
||||
if (!isset($plugin_info[$req_name]) && ($req_info = $rcmail->plugins->get_info($req_name))) {
|
||||
$plugin_info[$req_name] = $req_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($plugin_info)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
ksort($plugin_info, SORT_LOCALE_STRING);
|
||||
|
||||
$table = new html_table($attrib);
|
||||
|
||||
// add table header
|
||||
$table->add_header('name', $rcmail->gettext('plugin'));
|
||||
$table->add_header('version', $rcmail->gettext('version'));
|
||||
$table->add_header('license', $rcmail->gettext('license'));
|
||||
$table->add_header('source', $rcmail->gettext('source'));
|
||||
|
||||
foreach ($plugin_info as $name => $data) {
|
||||
$uri = !empty($data['src_uri']) ? $data['src_uri'] : ($data['uri'] ?? '');
|
||||
if ($uri && stripos($uri, 'http') !== 0) {
|
||||
$uri = 'http://' . $uri;
|
||||
}
|
||||
|
||||
if ($uri) {
|
||||
$uri = html::a([
|
||||
'target' => '_blank',
|
||||
'href' => rcube::Q($uri)
|
||||
],
|
||||
rcube::Q($rcmail->gettext('download'))
|
||||
);
|
||||
}
|
||||
|
||||
$license = isset($data['license']) ? $data['license'] : '';
|
||||
|
||||
if (!empty($data['license_uri'])) {
|
||||
$license = html::a([
|
||||
'target' => '_blank',
|
||||
'href' => rcube::Q($data['license_uri'])
|
||||
],
|
||||
rcube::Q($data['license'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
$license = rcube::Q($license);
|
||||
}
|
||||
|
||||
$table->add_row();
|
||||
$table->add('name', rcube::Q(!empty($data['name']) ? $data['name'] : $name));
|
||||
$table->add('version', !empty($data['version']) ? rcube::Q($data['version']) : '');
|
||||
$table->add('license', $license);
|
||||
$table->add('source', $uri);
|
||||
}
|
||||
|
||||
return $table->show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality to create a folder |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_create extends rcmail_action_settings_folder_edit
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality to delete a folder |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_delete extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (strlen($mbox)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('folder_delete', ['name' => $mbox]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$deleted = $storage->delete_folder($plugin['name']);
|
||||
}
|
||||
else {
|
||||
$deleted = $plugin['result'];
|
||||
}
|
||||
|
||||
// #1488692: update session
|
||||
if ($deleted && isset($_SESSION['mbox']) && $_SESSION['mbox'] === $mbox) {
|
||||
$rcmail->session->remove('mbox');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($deleted)) {
|
||||
// Remove folder and subfolders rows
|
||||
$rcmail->output->command('remove_folder_row', $mbox);
|
||||
$rcmail->output->show_message('folderdeleted', 'confirmation');
|
||||
// Clear content frame
|
||||
$rcmail->output->command('subscription_select');
|
||||
$rcmail->output->command('set_quota', self::quota_content());
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality to edit a folder |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_edit extends rcmail_action_settings_folders
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
|
||||
$rcmail->output->add_handlers([
|
||||
'folderdetails' => [$this, 'folder_form'],
|
||||
]);
|
||||
|
||||
$rcmail->output->add_label('nonamewarning');
|
||||
|
||||
$rcmail->output->send('folderedit');
|
||||
}
|
||||
|
||||
public static function folder_form($attrib)
|
||||
{
|
||||
// WARNING: folder names in UI are encoded with RCUBE_CHARSET
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
|
||||
// edited folder name (empty in create-folder mode)
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_GPC, true);
|
||||
|
||||
// predefined path for new folder
|
||||
$parent = rcube_utils::get_input_string('_path', rcube_utils::INPUT_GPC, true);
|
||||
|
||||
$threading_supported = $storage->get_capability('THREAD');
|
||||
$dual_use_supported = $storage->get_capability(rcube_storage::DUAL_USE_FOLDERS);
|
||||
$delimiter = $storage->get_hierarchy_delimiter();
|
||||
|
||||
// Get mailbox parameters
|
||||
if (strlen($mbox)) {
|
||||
$options = self::folder_options($mbox);
|
||||
$namespace = $storage->get_namespace();
|
||||
|
||||
$path = explode($delimiter, $mbox);
|
||||
$folder = array_pop($path);
|
||||
$path = implode($delimiter, $path);
|
||||
$folder = rcube_charset::convert($folder, 'UTF7-IMAP');
|
||||
|
||||
$hidden_fields = ['name' => '_mbox', 'value' => $mbox];
|
||||
}
|
||||
else {
|
||||
$options = [];
|
||||
$path = $parent;
|
||||
$folder = '';
|
||||
$hidden_fields = [];
|
||||
|
||||
// allow creating subfolders of INBOX folder
|
||||
if ($path == 'INBOX') {
|
||||
$path = $storage->mod_folder($path, 'in');
|
||||
}
|
||||
}
|
||||
|
||||
// remove personal namespace prefix
|
||||
$path_id = null;
|
||||
if (strlen($path)) {
|
||||
$path_id = $path;
|
||||
$path = $storage->mod_folder($path . $delimiter);
|
||||
if (($path[strlen($path)-1] ?? '') == $delimiter) {
|
||||
$path = substr($path, 0, -1);
|
||||
}
|
||||
}
|
||||
|
||||
$form = [];
|
||||
|
||||
// General tab
|
||||
$form['props'] = [
|
||||
'name' => $rcmail->gettext('properties'),
|
||||
];
|
||||
|
||||
// Location (name)
|
||||
if (!empty($options['protected'])) {
|
||||
$foldername = str_replace($delimiter, ' » ', rcube::Q(self::localize_foldername($mbox, false, true)));
|
||||
}
|
||||
else if (!empty($options['norename'])) {
|
||||
$foldername = rcube::Q($folder);
|
||||
}
|
||||
else {
|
||||
if (isset($_POST['_name'])) {
|
||||
$folder = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true));
|
||||
}
|
||||
|
||||
$foldername = new html_inputfield(['name' => '_name', 'id' => '_name', 'size' => 30, 'class' => 'form-control']);
|
||||
$foldername = '<span class="input-group">' . $foldername->show($folder);
|
||||
|
||||
if (!empty($options['special']) && ($sname = self::localize_foldername($mbox, false, true)) != $folder) {
|
||||
$foldername .= ' <span class="input-group-append"><span class="input-group-text">(' . rcube::Q($sname) .')</span></span>';
|
||||
}
|
||||
|
||||
$foldername .= '</span>';
|
||||
}
|
||||
|
||||
$form['props']['fieldsets']['location'] = [
|
||||
'name' => $rcmail->gettext('location'),
|
||||
'content' => [
|
||||
'name' => [
|
||||
'label' => $rcmail->gettext('foldername'),
|
||||
'value' => $foldername,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
if (!empty($options) && (!empty($options['norename']) || !empty($options['protected']))) {
|
||||
// prevent user from moving folder
|
||||
$hidden_path = new html_hiddenfield(['name' => '_parent', 'value' => $path]);
|
||||
$form['props']['fieldsets']['location']['content']['name']['value'] .= $hidden_path->show();
|
||||
}
|
||||
else {
|
||||
$selected = $_POST['_parent'] ?? $path_id;
|
||||
$exceptions = [$mbox];
|
||||
|
||||
// Exclude 'prefix' namespace from parent folders list (#1488349)
|
||||
// If INBOX. namespace exists, folders created as INBOX subfolders
|
||||
// will be listed at the same level - selecting INBOX as a parent does nothing
|
||||
if ($prefix = $storage->get_namespace('prefix')) {
|
||||
$exceptions[] = substr($prefix, 0, -1);
|
||||
}
|
||||
|
||||
$select = self::folder_selector([
|
||||
'id' => '_parent',
|
||||
'name' => '_parent',
|
||||
'noselection' => '---',
|
||||
'maxlength' => 150,
|
||||
'unsubscribed' => true,
|
||||
'skip_noinferiors' => true,
|
||||
'exceptions' => $exceptions,
|
||||
'additional' => is_string($selected) && strlen($selected) ? [$selected] : null,
|
||||
]);
|
||||
|
||||
$form['props']['fieldsets']['location']['content']['parent'] = [
|
||||
'label' => $rcmail->gettext('parentfolder'),
|
||||
'value' => $select->show($selected),
|
||||
];
|
||||
}
|
||||
|
||||
// Settings
|
||||
$form['props']['fieldsets']['settings'] = [
|
||||
'name' => $rcmail->gettext('settings'),
|
||||
];
|
||||
|
||||
// For servers that do not support both sub-folders and messages in a folder
|
||||
if (!$dual_use_supported) {
|
||||
if (!strlen($mbox)) {
|
||||
$select = new html_select(['name' => '_type', 'id' => '_type']);
|
||||
$select->add($rcmail->gettext('dualusemail'), 'mail');
|
||||
$select->add($rcmail->gettext('dualusefolder'), 'folder');
|
||||
|
||||
$value = rcube_utils::get_input_string('_type', rcube_utils::INPUT_POST);
|
||||
$value = $select->show($value ?: 'mail');
|
||||
}
|
||||
else {
|
||||
$value = $options['noselect'] ? 'folder' : 'mail';
|
||||
$value = $rcmail->gettext('dualuse' . $value);
|
||||
}
|
||||
|
||||
$form['props']['fieldsets']['settings']['content']['type'] = [
|
||||
'label' => $rcmail->gettext('dualuselabel'),
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
// Settings: threading
|
||||
if ($threading_supported && ($mbox == 'INBOX' || (empty($options['noselect']) && empty($options['is_root'])))) {
|
||||
$value = 0;
|
||||
$select = new html_select(['name' => '_viewmode', 'id' => '_viewmode']);
|
||||
|
||||
$select->add($rcmail->gettext('list'), 0);
|
||||
$select->add($rcmail->gettext('threads'), 1);
|
||||
|
||||
if (isset($_POST['_viewmode'])) {
|
||||
$value = (int) $_POST['_viewmode'];
|
||||
}
|
||||
else if (strlen($mbox)) {
|
||||
$a_threaded = $rcmail->config->get('message_threading', []);
|
||||
$default_mode = $rcmail->config->get('default_list_mode', 'list');
|
||||
|
||||
$value = (int) ($a_threaded[$mbox] ?? $default_mode == 'threads');
|
||||
}
|
||||
|
||||
$form['props']['fieldsets']['settings']['content']['viewmode'] = [
|
||||
'label' => $rcmail->gettext('listmode'),
|
||||
'value' => $select->show($value),
|
||||
];
|
||||
}
|
||||
|
||||
$msgcount = 0;
|
||||
|
||||
// Information (count, size) - Edit mode
|
||||
if (strlen($mbox)) {
|
||||
// Number of messages
|
||||
$form['props']['fieldsets']['info'] = [
|
||||
'name' => $rcmail->gettext('info'),
|
||||
'content' => []
|
||||
];
|
||||
|
||||
if ((!$options['noselect'] && !$options['is_root']) || $mbox == 'INBOX') {
|
||||
$msgcount = (int) $storage->count($mbox, 'ALL', true, false);
|
||||
|
||||
if ($msgcount) {
|
||||
// Get the size on servers with supposed-to-be-fast method for that
|
||||
if ($storage->get_capability('STATUS=SIZE')) {
|
||||
$size = $storage->folder_size($mbox);
|
||||
if ($size !== false) {
|
||||
$size = self::show_bytes($size);
|
||||
}
|
||||
}
|
||||
|
||||
// create link with folder-size command
|
||||
if (!isset($size) || $size === false) {
|
||||
$onclick = sprintf("return %s.command('folder-size', '%s', this)",
|
||||
rcmail_output::JS_OBJECT_NAME, rcube::JQ($mbox));
|
||||
|
||||
$attr = ['href' => '#', 'onclick' => $onclick, 'id' => 'folder-size'];
|
||||
$size = html::a($attr, $rcmail->gettext('getfoldersize'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no messages -> zero size
|
||||
$size = 0;
|
||||
}
|
||||
|
||||
$form['props']['fieldsets']['info']['content']['count'] = [
|
||||
'label' => $rcmail->gettext('messagecount'),
|
||||
'value' => $msgcount
|
||||
];
|
||||
$form['props']['fieldsets']['info']['content']['size'] = [
|
||||
'label' => $rcmail->gettext('size'),
|
||||
'value' => $size,
|
||||
];
|
||||
}
|
||||
|
||||
// show folder type only if we have non-private namespaces
|
||||
if (!empty($namespace['shared']) || !empty($namespace['others'])) {
|
||||
$form['props']['fieldsets']['info']['content']['foldertype'] = [
|
||||
'label' => $rcmail->gettext('foldertype'),
|
||||
'value' => $rcmail->gettext($options['namespace'] . 'folder')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Allow plugins to modify folder form content
|
||||
$plugin = $rcmail->plugins->exec_hook('folder_form', [
|
||||
'form' => $form,
|
||||
'options' => $options,
|
||||
'name' => $mbox,
|
||||
'parent_name' => $parent
|
||||
]);
|
||||
|
||||
$form = $plugin['form'];
|
||||
|
||||
// Set form tags and hidden fields
|
||||
list($form_start, $form_end) = self::get_form_tags($attrib, 'save-folder', null, $hidden_fields);
|
||||
|
||||
unset($attrib['form'], $attrib['id']);
|
||||
|
||||
// return the complete edit form as table
|
||||
$out = "$form_start\n";
|
||||
|
||||
// Create form output
|
||||
foreach ($form as $idx => $tab) {
|
||||
if (!empty($tab['fieldsets']) && is_array($tab['fieldsets'])) {
|
||||
$content = '';
|
||||
foreach ($tab['fieldsets'] as $fieldset) {
|
||||
$subcontent = self::get_form_part($fieldset, $attrib);
|
||||
if ($subcontent) {
|
||||
$subcontent = html::tag('legend', null, rcube::Q($fieldset['name'])) . $subcontent;
|
||||
$content .= html::tag('fieldset', null, $subcontent) ."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$content = self::get_form_part($tab, $attrib);
|
||||
}
|
||||
|
||||
if ($idx != 'props') {
|
||||
$out .= html::tag('fieldset', null, html::tag('legend', null, rcube::Q($tab['name'])) . $content) ."\n";
|
||||
}
|
||||
else {
|
||||
$out .= $content ."\n";
|
||||
}
|
||||
}
|
||||
|
||||
$out .= "\n$form_end";
|
||||
|
||||
$rcmail->output->set_env('messagecount', $msgcount);
|
||||
$rcmail->output->set_env('folder', $mbox);
|
||||
|
||||
if ($mbox !== null && empty($_POST)) {
|
||||
$rcmail->output->command('parent.set_quota', self::quota_content(null, $mbox));
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
public static function get_form_part($form, $attrib = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$content = '';
|
||||
|
||||
if (!empty($form['content']) && is_array($form['content'])) {
|
||||
$table = new html_table(['cols' => 2]);
|
||||
|
||||
foreach ($form['content'] as $col => $colprop) {
|
||||
$colprop['id'] = '_' . $col;
|
||||
$label = !empty($colprop['label']) ? $colprop['label'] : $rcmail->gettext($col);
|
||||
|
||||
$table->add('title', html::label($colprop['id'], rcube::Q($label)));
|
||||
$table->add(null, $colprop['value']);
|
||||
}
|
||||
|
||||
$content = $table->show($attrib);
|
||||
}
|
||||
else if (isset($form['content'])) {
|
||||
$content = $form['content'];
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality of folder purge |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_purge extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
$delimiter = $storage->get_hierarchy_delimiter();
|
||||
$trash_mbox = $rcmail->config->get('trash_mbox');
|
||||
$trash_regexp = '/^' . preg_quote($trash_mbox . $delimiter, '/') . '/';
|
||||
|
||||
// we should only be purging trash (or their subfolders)
|
||||
if (!strlen($trash_mbox) || $mbox === $trash_mbox || preg_match($trash_regexp, $mbox)) {
|
||||
$success = $storage->delete_message('*', $mbox);
|
||||
$delete = true;
|
||||
}
|
||||
// move to Trash
|
||||
else {
|
||||
$success = $storage->move_message('1:*', $trash_mbox, $mbox);
|
||||
$delete = false;
|
||||
}
|
||||
|
||||
if (!empty($success)) {
|
||||
$rcmail->output->set_env('messagecount', 0);
|
||||
|
||||
if ($delete) {
|
||||
$rcmail->output->show_message('folderpurged', 'confirmation');
|
||||
$rcmail->output->command('set_quota', self::quota_content(null, $mbox));
|
||||
}
|
||||
else {
|
||||
$rcmail->output->show_message('messagemoved', 'confirmation');
|
||||
}
|
||||
|
||||
$_SESSION['unseen_count'][$mbox] = 0;
|
||||
$rcmail->output->command('show_folder', $mbox, null, true);
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality of folder rename |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_rename extends rcmail_action_settings_folders
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$name = trim(rcube_utils::get_input_string('_folder_newname', rcube_utils::INPUT_POST, true));
|
||||
$oldname = rcube_utils::get_input_string('_folder_oldname', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (strlen($name) && strlen($oldname)) {
|
||||
$rename = self::rename_folder($oldname, $name);
|
||||
}
|
||||
|
||||
if (!empty($rename)) {
|
||||
self::update_folder_row($name, $oldname);
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
|
||||
public static function rename_folder($oldname, $newname)
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('folder_rename', [
|
||||
'oldname' => $oldname, 'newname' => $newname]);
|
||||
|
||||
if (empty($plugin['abort'])) {
|
||||
$renamed = $storage->rename_folder($oldname, $newname);
|
||||
}
|
||||
else {
|
||||
$renamed = $plugin['result'];
|
||||
}
|
||||
|
||||
// update per-folder options for modified folder and its subfolders
|
||||
if ($renamed) {
|
||||
$delimiter = $storage->get_hierarchy_delimiter();
|
||||
$a_threaded = (array) $rcmail->config->get('message_threading', []);
|
||||
$oldprefix = '/^' . preg_quote($oldname . $delimiter, '/') . '/';
|
||||
|
||||
foreach ($a_threaded as $key => $val) {
|
||||
if ($key == $oldname) {
|
||||
unset($a_threaded[$key]);
|
||||
$a_threaded[$newname] = $val;
|
||||
}
|
||||
else if (preg_match($oldprefix, $key)) {
|
||||
unset($a_threaded[$key]);
|
||||
$a_threaded[preg_replace($oldprefix, $newname . $delimiter, $key)] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->user->save_prefs(['message_threading' => $a_threaded]);
|
||||
|
||||
// #1488692: update session
|
||||
if (isset($_SESSION['mbox']) && $_SESSION['mbox'] === $oldname) {
|
||||
$_SESSION['mbox'] = $newname;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handler for saving the create/edit folder form |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_save extends rcmail_action_settings_folder_edit
|
||||
{
|
||||
protected static $mode = self::MODE_HTTP;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
// WARNING: folder names in UI are encoded with RCUBE_CHARSET
|
||||
|
||||
$name = trim(rcube_utils::get_input_string('_name', rcube_utils::INPUT_POST, true));
|
||||
$path = rcube_utils::get_input_string('_parent', rcube_utils::INPUT_POST, true);
|
||||
$old_imap = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
$type = rcube_utils::get_input_string('_type', rcube_utils::INPUT_POST);
|
||||
$name_imap = rcube_charset::convert($name, RCUBE_CHARSET, 'UTF7-IMAP');
|
||||
// $path is in UTF7-IMAP already
|
||||
|
||||
// init IMAP connection
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$delimiter = $storage->get_hierarchy_delimiter();
|
||||
$options = strlen($old_imap) ? self::folder_options($old_imap) : [];
|
||||
$char = null;
|
||||
|
||||
// Folder name checks
|
||||
if (!empty($options['protected']) || !empty($options['norename'])) {
|
||||
// do nothing
|
||||
}
|
||||
else if (!strlen($name)) {
|
||||
$error = $rcmail->gettext('namecannotbeempty');
|
||||
}
|
||||
else if (mb_strlen($name) > 128) {
|
||||
$error = $rcmail->gettext('nametoolong');
|
||||
}
|
||||
else if ($name[0] == '.' && $rcmail->config->get('imap_skip_hidden_folders')) {
|
||||
$error = $rcmail->gettext('namedotforbidden');
|
||||
}
|
||||
else if (!$storage->folder_validate($name, $char)) {
|
||||
$error = $rcmail->gettext('forbiddencharacter') . " ($char)";
|
||||
}
|
||||
|
||||
if (!empty($error)) {
|
||||
$rcmail->output->command('display_message', $error, 'error');
|
||||
}
|
||||
else {
|
||||
if (!empty($options['protected']) || !empty($options['norename'])) {
|
||||
$name_imap = $old_imap;
|
||||
}
|
||||
else if (strlen($path)) {
|
||||
$name_imap = $path . $delimiter . $name_imap;
|
||||
}
|
||||
else {
|
||||
$name_imap = $storage->mod_folder($name_imap, 'in');
|
||||
}
|
||||
}
|
||||
|
||||
$dual_use_supported = $storage->get_capability(rcube_storage::DUAL_USE_FOLDERS);
|
||||
$acl_supported = $storage->get_capability('ACL');
|
||||
|
||||
// Check access rights to the parent folder
|
||||
if (empty($error) && $acl_supported && strlen($path) && (!strlen($old_imap) || $old_imap != $name_imap)) {
|
||||
$parent_opts = $storage->folder_info($path);
|
||||
if ($parent_opts['namespace'] != 'personal'
|
||||
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
|
||||
) {
|
||||
$error = $rcmail->gettext('parentnotwritable');
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($error)) {
|
||||
$rcmail->output->command('display_message', $error, 'error');
|
||||
$folder = null;
|
||||
}
|
||||
else {
|
||||
$folder = [
|
||||
'name' => $name_imap,
|
||||
'oldname' => $old_imap,
|
||||
'class' => '',
|
||||
'options' => $options,
|
||||
'settings' => [
|
||||
// List view mode: 0-list, 1-threads
|
||||
'view_mode' => (int) rcube_utils::get_input_string('_viewmode', rcube_utils::INPUT_POST),
|
||||
'sort_column' => rcube_utils::get_input_string('_sortcol', rcube_utils::INPUT_POST),
|
||||
'sort_order' => rcube_utils::get_input_string('_sortord', rcube_utils::INPUT_POST),
|
||||
],
|
||||
'subscribe' => false,
|
||||
'noselect' => false,
|
||||
];
|
||||
}
|
||||
|
||||
// create a new mailbox
|
||||
if (empty($error) && !strlen($old_imap)) {
|
||||
$folder['subscribe'] = true;
|
||||
|
||||
// Server does not support both sub-folders and messages in a folder
|
||||
// For folders that are supposed to contain other folders we will:
|
||||
// - disable subscription
|
||||
// - add a separator at the end to make them \NoSelect
|
||||
if (!$dual_use_supported && $type == 'folder') {
|
||||
$folder['subscribe'] = false;
|
||||
$folder['noselect'] = true;
|
||||
}
|
||||
|
||||
$plugin = $rcmail->plugins->exec_hook('folder_create', ['record' => $folder]);
|
||||
|
||||
$folder = $plugin['record'];
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
$created = $storage->create_folder($folder['name'], $folder['subscribe'], null, $folder['noselect']);
|
||||
}
|
||||
else {
|
||||
$created = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($created) {
|
||||
// Save folder settings
|
||||
if (isset($_POST['_viewmode'])) {
|
||||
$a_threaded = (array) $rcmail->config->get('message_threading', []);
|
||||
|
||||
$a_threaded[$folder['name']] = (bool) $_POST['_viewmode'];
|
||||
|
||||
$rcmail->user->save_prefs(['message_threading' => $a_threaded]);
|
||||
}
|
||||
|
||||
self::update_folder_row($folder['name'], null, $folder['subscribe'], $folder['class']);
|
||||
|
||||
$rcmail->output->show_message('foldercreated', 'confirmation');
|
||||
// reset folder preview frame
|
||||
$rcmail->output->command('subscription_select');
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
if (!empty($plugin['message'])) {
|
||||
$rcmail->output->show_message($plugin['message'], 'error', null, false);
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
}
|
||||
}
|
||||
// update a mailbox
|
||||
else if (empty($error)) {
|
||||
$plugin = $rcmail->plugins->exec_hook('folder_update', ['record' => $folder]);
|
||||
|
||||
$folder = $plugin['record'];
|
||||
$rename = $folder['oldname'] != $folder['name'];
|
||||
|
||||
if (!$plugin['abort']) {
|
||||
if ($rename) {
|
||||
$updated = $storage->rename_folder($folder['oldname'], $folder['name']);
|
||||
}
|
||||
else {
|
||||
$updated = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$updated = $plugin['result'];
|
||||
}
|
||||
|
||||
if ($updated) {
|
||||
// Update folder settings,
|
||||
if (isset($_POST['_viewmode'])) {
|
||||
$a_threaded = (array) $rcmail->config->get('message_threading', []);
|
||||
|
||||
// In case of name change update names of children in settings
|
||||
if ($rename) {
|
||||
$oldprefix = '/^' . preg_quote($folder['oldname'] . $delimiter, '/') . '/';
|
||||
foreach ($a_threaded as $key => $val) {
|
||||
if ($key == $folder['oldname']) {
|
||||
unset($a_threaded[$key]);
|
||||
}
|
||||
else if (preg_match($oldprefix, $key)) {
|
||||
unset($a_threaded[$key]);
|
||||
$a_threaded[preg_replace($oldprefix, $folder['name'].$delimiter, $key)] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$a_threaded[$folder['name']] = (bool) $_POST['_viewmode'];
|
||||
|
||||
$rcmail->user->save_prefs(['message_threading' => $a_threaded]);
|
||||
}
|
||||
|
||||
$rcmail->output->show_message('folderupdated', 'confirmation');
|
||||
$rcmail->output->set_env('folder', $folder['name']);
|
||||
|
||||
if ($rename) {
|
||||
// #1488692: update session
|
||||
if (isset($_SESSION['mbox']) && $_SESSION['mbox'] === $folder['oldname']) {
|
||||
$_SESSION['mbox'] = $folder['name'];
|
||||
}
|
||||
self::update_folder_row($folder['name'], $folder['oldname'], $folder['subscribe'], $folder['class']);
|
||||
$rcmail->output->send('iframe');
|
||||
}
|
||||
else if (!empty($folder['class'])) {
|
||||
self::update_folder_row($folder['name'], $folder['oldname'], $folder['subscribe'], $folder['class']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// show error message
|
||||
if (!empty($plugin['message'])) {
|
||||
$rcmail->output->show_message($plugin['message'], 'error', null, false);
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcmail->overwrite_action('edit-folder');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Provide functionality of getting folder size |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_size extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$name = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
$size = $storage->folder_size($name);
|
||||
|
||||
// @TODO: check quota and show percentage usage of specified mailbox?
|
||||
|
||||
if ($size !== false) {
|
||||
$rcmail->output->command('folder_size_update', self::show_bytes($size));
|
||||
}
|
||||
else {
|
||||
self::display_server_error();
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handler for folder subscribe action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_subscribe extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (strlen($mbox)) {
|
||||
$result = $storage->subscribe([$mbox]);
|
||||
|
||||
// Handle virtual (non-existing) folders
|
||||
if (
|
||||
!$result
|
||||
&& $storage->get_error_code() == -1
|
||||
&& $storage->get_response_code() == rcube_storage::TRYCREATE
|
||||
) {
|
||||
$result = $storage->create_folder($mbox, true);
|
||||
if ($result) {
|
||||
// @TODO: remove 'virtual' class of folder's row
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
// Handle subscription of protected folder (#1487656)
|
||||
if ($rcmail->config->get('protect_default_folders') && $storage->is_special_folder($mbox)) {
|
||||
$rcmail->output->command('disable_subscription', $mbox);
|
||||
}
|
||||
|
||||
$rcmail->output->show_message('foldersubscribed', 'confirmation');
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
$rcmail->output->command('reset_subscription', $mbox, false);
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| This file is part of the Roundcube Webmail client |
|
||||
| |
|
||||
| Copyright (C) The Roundcube Dev Team |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the README file for a full license statement. |
|
||||
| |
|
||||
| PURPOSE: |
|
||||
| Handler for folder unsubscribe action |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Thomas Bruederli <roundcube@gmail.com> |
|
||||
| Author: Aleksander Machniak <alec@alec.pl> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
class rcmail_action_settings_folder_unsubscribe extends rcmail_action
|
||||
{
|
||||
protected static $mode = self::MODE_AJAX;
|
||||
|
||||
/**
|
||||
* Request handler.
|
||||
*
|
||||
* @param array $args Arguments from the previous step(s)
|
||||
*/
|
||||
public function run($args = [])
|
||||
{
|
||||
$rcmail = rcmail::get_instance();
|
||||
$storage = $rcmail->get_storage();
|
||||
$mbox = rcube_utils::get_input_string('_mbox', rcube_utils::INPUT_POST, true);
|
||||
|
||||
if (strlen($mbox)) {
|
||||
$result = $storage->unsubscribe([$mbox]);
|
||||
}
|
||||
|
||||
if (!empty($result)) {
|
||||
$rcmail->output->show_message('folderunsubscribed', 'confirmation');
|
||||
}
|
||||
else {
|
||||
self::display_server_error('errorsaving');
|
||||
$rcmail->output->command('reset_subscription', $mbox, true);
|
||||
}
|
||||
|
||||
$rcmail->output->send();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user