Refactor the export and split files
Parent
0b8349a99f
révision
c541905947
|
@ -0,0 +1,259 @@
|
|||
<?php
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| common.php |
|
||||
| |
|
||||
| This file is part of the import/export contacts script |
|
||||
| |
|
||||
| Copyright (C) 2018, Cliss XXI |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
| any later version with exceptions for skins & plugins. |
|
||||
| See the LICENSE file for a full license statement. |
|
||||
| |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Jerome Lebleu <contact@cliss21.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/');
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
|
||||
// Logging facilities
|
||||
// ------------------
|
||||
|
||||
$LOG_LEVELS = array(
|
||||
'DEBUG' => 0,
|
||||
'INFO' => 1,
|
||||
'WARNING' => 2,
|
||||
'ERROR' => 3,
|
||||
);
|
||||
|
||||
$LOG_LEVEL = $LOG_LEVELS['WARNING'];
|
||||
|
||||
/**
|
||||
* Set the log level to use in the script.
|
||||
*
|
||||
* @param string $level Log level
|
||||
*/
|
||||
function set_log_level($level)
|
||||
{
|
||||
global $LOG_LEVEL, $LOG_LEVELS;
|
||||
|
||||
if (isset($LOG_LEVELS[$level])) {
|
||||
$LOG_LEVEL = $LOG_LEVELS[$level];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a level is logged.
|
||||
*
|
||||
* @param string $level Log level
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
function is_logged_level($level)
|
||||
{
|
||||
global $LOG_LEVEL, $LOG_LEVELS;
|
||||
|
||||
if (!isset($LOG_LEVELS[$level])) {
|
||||
$level = 'DEBUG';
|
||||
}
|
||||
if ($LOG_LEVELS[$level] >= $LOG_LEVEL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message at a given level.
|
||||
*
|
||||
* @param string $level Log level
|
||||
* @param string $message Message
|
||||
*/
|
||||
function log_msg($level, $message)
|
||||
{
|
||||
if (is_logged_level($level)) {
|
||||
echo "[$level] $message\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display error and terminate script execution.
|
||||
*
|
||||
* @param string $message Message
|
||||
* @param int $status Optional exit status
|
||||
*/
|
||||
function raise_err($message, $status = 1)
|
||||
{
|
||||
fputs(STDERR, "Error: $message\n");
|
||||
exit($status);
|
||||
}
|
||||
|
||||
// Import and export helpers
|
||||
// -------------------------
|
||||
|
||||
/**
|
||||
* Copy contact record properties into a vcard object.
|
||||
*
|
||||
* @see program/steps/addressbook/export.inc
|
||||
*/
|
||||
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($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
|
||||
$vcard->reset();
|
||||
|
||||
foreach ($record as $key => $values) {
|
||||
list($field, $section) = 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 = array($values);
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if (is_array($value) || is_a($value, 'DateTime') || @strlen($value)) {
|
||||
$vcard->set($field, $value, strtoupper($section));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append group names
|
||||
if ($groups) {
|
||||
$vcard->set('groups', join(',', $groups), null);
|
||||
}
|
||||
|
||||
$record['vcard'] = $vcard->export();
|
||||
}
|
||||
// patch categories to alread existing vcard block
|
||||
elseif ($record['vcard']) {
|
||||
$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, ',');
|
||||
$record['vcard'] = str_replace('END:VCARD', $vgroups . rcube_vcard::$eol . 'END:VCARD', $record['vcard']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Main class
|
||||
// ----------
|
||||
|
||||
class rcmail_contacts_utils
|
||||
{
|
||||
private $host = null;
|
||||
private $username = null;
|
||||
|
||||
private $db = null;
|
||||
private $user_id = 0;
|
||||
private $contacts = null;
|
||||
|
||||
/**
|
||||
* Object constructor.
|
||||
*
|
||||
* @param string $user User name
|
||||
* @param string $host Optional user host
|
||||
*/
|
||||
public function __construct($user, $host = null)
|
||||
{
|
||||
$this->db = rcmail_utils::db();
|
||||
|
||||
$this->host = $host;
|
||||
$this->username = $user;
|
||||
|
||||
$this->user_id = $this->get_user_id($this->username, $this->host);
|
||||
$this->contacts = new rcube_contacts($this->db, $this->user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the user's address book as vCard file.
|
||||
*
|
||||
* @param resource $out Output file handler
|
||||
*/
|
||||
public function export($out)
|
||||
{
|
||||
// get contacts for this user
|
||||
$this->contacts->set_page(1);
|
||||
$this->contacts->set_pagesize(99999);
|
||||
$result = $this->contacts->list_records(null, 0, true);
|
||||
|
||||
if (0 == $result->count) {
|
||||
raise_err("No address book found for user {$this->username}");
|
||||
}
|
||||
|
||||
while ($result && ($row = $result->next())) {
|
||||
prepare_for_export($row, $this->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']);
|
||||
fwrite($out, rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol);
|
||||
}
|
||||
|
||||
log_msg('INFO', "{$result->count} contacts have been exported for {$this->username}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user id matching the given name and host.
|
||||
*
|
||||
* @param string $user User name
|
||||
* @param string $host Optional user host
|
||||
*
|
||||
* @return int User id
|
||||
*/
|
||||
private function get_user_id($user, $host = null)
|
||||
{
|
||||
// construct SQL query
|
||||
$sql = 'SELECT user_id, mail_host, last_login ' .
|
||||
'FROM ' . $this->db->table_name('users', true) . ' WHERE `username` = ?';
|
||||
$sql_params = array($user);
|
||||
if ($host) {
|
||||
$sql .= ' AND `mail_host` = ?';
|
||||
$sql_params[] = $host;
|
||||
}
|
||||
|
||||
log_msg('DEBUG', "Search user ${user} with SQL query: ${sql} / " .
|
||||
print_r($sql_params, 1));
|
||||
|
||||
$sql_result = $this->db->query($sql, $sql_params);
|
||||
|
||||
if ($sql_result) {
|
||||
if (0 == $this->db->num_rows($sql_result)) {
|
||||
raise_err("User ${user} not found");
|
||||
} elseif ($this->db->num_rows($sql_result) > 1) {
|
||||
// output users details
|
||||
if (is_logged_level('INFO')) {
|
||||
$log_msg = $this->db->num_rows($sql_result) .
|
||||
" users found for ${user}:";
|
||||
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
|
||||
$log_msg .= "\n - id: " . $sql_arr['user_id'] .
|
||||
' / mail_host: ' . $sql_arr['mail_host'] .
|
||||
' / last_login: ' . $sql_arr['last_login'];
|
||||
}
|
||||
log_msg('INFO', $log_msg);
|
||||
}
|
||||
|
||||
raise_err("More thant one user found for username ${user}, you need to specify the host");
|
||||
} else {
|
||||
$sql_arr = $this->db->fetch_assoc($sql_result);
|
||||
$user_id = $sql_arr['user_id'];
|
||||
|
||||
log_msg('DEBUG', "Found user ${user} with id ${user_id}");
|
||||
|
||||
return $user_id;
|
||||
}
|
||||
}
|
||||
|
||||
raise_err("Unable to query the user ${user}!");
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
/**
|
||||
+-----------------------------------------------------------------------+
|
||||
| export-contacts.sh |
|
||||
| |
|
||||
| This script permit to export Roundcube user's address book in vCard |
|
||||
| format. |
|
||||
| |
|
||||
| Copyright (C) 2018, Cliss XXI |
|
||||
| Copyright (C) 2017, Easter-eggs |
|
||||
| |
|
||||
| Licensed under the GNU General Public License version 3 or |
|
||||
|
@ -15,157 +16,28 @@
|
|||
| |
|
||||
+-----------------------------------------------------------------------+
|
||||
| Author: Benjamin Renard <brenard@easter-eggs.com> |
|
||||
| Author: Jerome Lebleu <contact@cliss21.com> |
|
||||
+-----------------------------------------------------------------------+
|
||||
*/
|
||||
*/
|
||||
|
||||
define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/');
|
||||
// Unset memory limit during the export
|
||||
ini_set('memory_limit', -1);
|
||||
|
||||
require_once INSTALL_PATH . 'program/include/clisetup.php';
|
||||
require_once 'common.php';
|
||||
|
||||
function print_usage()
|
||||
{
|
||||
echo "Usage: export-contact.sh [options] username\n";
|
||||
echo "Usage: export-contact.sh [options] username\n\n";
|
||||
echo "Options:\n";
|
||||
echo " -m, --mailhost=domain Mail host (optional, e.g. example.org)\n";
|
||||
echo " -o, --output=path Path to the output file\n";
|
||||
echo " -v, --verbose Enable verbose mode\n";
|
||||
echo " -d, --debug Enable debug mode\n";
|
||||
}
|
||||
|
||||
class rcmail_export extends rcmail_utils
|
||||
{
|
||||
public function get_userid($user, $mailhost = null)
|
||||
{
|
||||
$db = self::db();
|
||||
$sql = 'SELECT user_id, mail_host, last_login FROM ' . $db->table_name('users', true) . ' WHERE username=?';
|
||||
$sql_params = array($user);
|
||||
if ($mailhost) {
|
||||
$sql .= ' AND mail_host=?';
|
||||
$sql_params[] = $mailhost;
|
||||
}
|
||||
//$sql .= " ORDER BY last_login DESC";
|
||||
$sql_result = $db->query($sql, $sql_params);
|
||||
log_msg('DEBUG', "Search user $user SQL query: $sql / " . print_r($sql_params, 1));
|
||||
|
||||
if ($sql_result) {
|
||||
if (0 == $db->num_rows($sql_result)) {
|
||||
log_msg('FATAL', "User $user not found!");
|
||||
} elseif ($db->num_rows($sql_result) > 1) {
|
||||
log_msg('FATAL', "More thant one user found for username $user. You need to specify mailhost!");
|
||||
log_msg('INFO', 'Users found:');
|
||||
while ($sql_arr = $db->fetch_assoc($sql_result)) {
|
||||
log_msg('INFO', " - User: $user / ID: " . $sql_arr['user_id'] . ' / Mailhost: ' . $sql_arr['mail_host'] . ' / Last login: ' . $sql_arr['last_login']);
|
||||
}
|
||||
} else {
|
||||
$sql_arr = $db->fetch_assoc($sql_result);
|
||||
log_msg('DEBUG', "User: $user / ID: " . $sql_arr['user_id']);
|
||||
|
||||
return $sql_arr['user_id'];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function get_user_address_book($user, $mailhost = null)
|
||||
{
|
||||
$userid = $this->get_userid($user, $mailhost);
|
||||
if (false !== $userid) {
|
||||
return new rcube_contacts(self::db(), $userid);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function export_user_contacts($user, $mailhost = null, $out = null)
|
||||
{
|
||||
$contacts = $this->get_user_address_book($user, $mailhost);
|
||||
if (false === $contacts) {
|
||||
log_msg('WARNING', "No address book found for user $user");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$contacts->set_page(1);
|
||||
$contacts->set_pagesize(99999);
|
||||
$result = $contacts->list_records(null, 0, true);
|
||||
|
||||
if (!$out) {
|
||||
$out = $GLOBALS['args']['file'] ? STDOUT : STDERR;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
while ($result && ($row = $result->next())) {
|
||||
if ($CONTACTS) {
|
||||
$this->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']);
|
||||
fwrite($out, rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol);
|
||||
|
||||
++$count;
|
||||
}
|
||||
log_msg('INFO', "$count contact(s) found in address book of user $user");
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy contact record properties into a vcard object.
|
||||
*
|
||||
* @see program/steps/addressbook/export.inc
|
||||
*/
|
||||
public 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($record['vcard'], RCUBE_CHARSET, false, $fieldmap);
|
||||
$vcard->reset();
|
||||
|
||||
foreach ($record as $key => $values) {
|
||||
list($field, $section) = 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 = array($values);
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if (is_array($value) || is_a($value, 'DateTime') || @strlen($value)) {
|
||||
$vcard->set($field, $value, strtoupper($section));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// append group names
|
||||
if ($groups) {
|
||||
$vcard->set('groups', join(',', $groups), null);
|
||||
}
|
||||
|
||||
$record['vcard'] = $vcard->export();
|
||||
}
|
||||
// patch categories to alread existing vcard block
|
||||
elseif ($record['vcard']) {
|
||||
$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, ',');
|
||||
$record['vcard'] = str_replace('END:VCARD', $vgroups . rcube_vcard::$eol . 'END:VCARD', $record['vcard']);
|
||||
}
|
||||
}
|
||||
}
|
||||
echo " -h, --host=domain Mail host (optional, e.g. example.org)\n";
|
||||
echo " -o, --output=path Path to the output file\n";
|
||||
echo " -v, --verbose Enable verbose mode\n";
|
||||
echo " -d, --debug Enable debug mode\n";
|
||||
}
|
||||
|
||||
// get arguments
|
||||
$args = rcube_utils::get_opt(array(
|
||||
'm' => 'mailhost',
|
||||
'h' => 'host',
|
||||
'o' => 'output',
|
||||
'd' => 'debug:bool',
|
||||
'v' => 'verbose:bool',
|
||||
|
@ -174,41 +46,20 @@ $args = rcube_utils::get_opt(array(
|
|||
|
||||
if ('help' == $_SERVER['argv'][1] || $args['help']) {
|
||||
print_usage();
|
||||
exit;
|
||||
exit(0);
|
||||
} elseif (empty($args[0])) {
|
||||
echo "Missing required parameter.\n";
|
||||
echo "Missing required parameter.\n\n";
|
||||
print_usage();
|
||||
exit;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$username = trim($args[0]);
|
||||
|
||||
// define logging level
|
||||
$_log_levels = array(
|
||||
'FATAL' => 0,
|
||||
'WARNING' => 1,
|
||||
'INFO' => 2,
|
||||
'DEBUG' => 3,
|
||||
);
|
||||
|
||||
$log_level = 'WARNING';
|
||||
if ($args['debug']) {
|
||||
$log_level = 'DEBUG';
|
||||
set_log_level('DEBUG');
|
||||
} elseif ($args['verbose']) {
|
||||
$log_level = 'INFO';
|
||||
}
|
||||
$_log_level = $_log_levels[$log_level];
|
||||
|
||||
function log_msg($level, $msg)
|
||||
{
|
||||
global $_log_level, $_log_levels;
|
||||
if (!isset($_log_levels[$level])) {
|
||||
$level = 'DEBUG';
|
||||
}
|
||||
$_level = $_log_levels[$level];
|
||||
if ($_level <= $_log_level) {
|
||||
echo "[$level] $msg\n";
|
||||
}
|
||||
set_log_level('INFO');
|
||||
}
|
||||
|
||||
// check output file
|
||||
|
@ -216,15 +67,16 @@ $out = null;
|
|||
if (!empty($args['output'])) {
|
||||
$out = fopen($args['output'], 'w');
|
||||
if (false === $out) {
|
||||
echo "Fail to open output file !\n";
|
||||
exit(1);
|
||||
raise_err("Fail to open output file!");
|
||||
}
|
||||
} else {
|
||||
$out = $GLOBALS['args']['file'] ? STDOUT : STDERR;
|
||||
}
|
||||
|
||||
$export = new rcmail_export();
|
||||
$export->export_user_contacts($username, $args['mailhost'], $out);
|
||||
$contacts = new rcmail_contacts_utils($username, $args['host']);
|
||||
$contacts->export($out);
|
||||
|
||||
if ($out) {
|
||||
if (!empty($args['output'])) {
|
||||
fclose($out);
|
||||
}
|
||||
|
||||
|
|
Chargement…
Référencer dans un nouveau ticket