Refactor the export and split files

master
Jérôme Lebleu 2018-10-23 17:44:00 +02:00 commité par Jérôme Lebleu
Parent 0b8349a99f
révision c541905947
2 fichiers modifiés avec 282 ajouts et 171 suppressions

259
common.php Normal file
Voir le fichier

@ -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}!");
}
}

Voir le fichier

@ -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);
}