#!/usr/bin/env php | +-----------------------------------------------------------------------+ */ define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' ); ini_set('memory_limit', -1); require_once INSTALL_PATH.'program/include/clisetup.php'; function print_usage() { print "Usage: export-contact.sh -u username -m mailhost.fqdn\n"; print "-u / --user User name\n"; print "-m / --mailhost Mailhost (optional)\n"; print "-o / --output Output file\n"; print "-l / --last-login-on-duplicated Use last login user on duplicated\n"; print "-a / --all-on-duplicated Retreive all user contacts on duplicated\n"; print "-v / --verbose Enable verbose mode\n"; print "-d / --debug Enable debug mode\n"; } function vputs($str) { $out = $GLOBALS['args']['output'] ? STDERR : STDOUT; 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)); } class rcmail_export extends rcmail_utils { function get_userid($user,$mailhost=null,$onDuplicatedPolicy='error') { $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 ($db -> num_rows($sql_result)==0) { log_msg('FATAL',"User $user not found !"); } elseif ($db -> num_rows($sql_result)>1) { log_msg("DEBUG","On duplicated user policy : $onDuplicatedPolicy"); switch ($onDuplicatedPolicy) { case 'last-login': log_msg('INFO', "More thant one user found for username $user : take the last connected one."); $user_id = false; $mailhost = false; $last_connected="1900-01-01 00:00:00"; while($sql_arr = $db->fetch_assoc($sql_result)) { log_msg('DEBUG', " - User : $user / ID : ".$sql_arr['user_id']." / Mailhost : ".$sql_arr['mail_host']." / Last login : ".$sql_arr['last_login']); if ( strnatcmp($last_connected, $sql_arr['last_login']) <= 0 ) { $last_connected=$sql_arr['last_login']; $user_id = $sql_arr['user_id']; } } log_msg('INFO', "Last connected one is ID $user_id / Mailhost : $mailhost / Last login : $last_connected"); return $user_id; break; case 'all': log_msg('INFO', "More thant one user found for username $user : take all of them."); $user_ids = array(); while($sql_arr = $db->fetch_assoc($sql_result)) { log_msg('DEBUG', " - User : $user / ID : ".$sql_arr['user_id']." / Mailhost : ".$sql_arr['mail_host']." / Last login : ".$sql_arr['last_login']); $user_ids[] = $sql_arr['user_id']; } return $user_ids; break; case 'error': default: 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); return $sql_arr['user_id']; } } return false; } function get_user_address_book($user, $mailhost = null, $onDuplicatedPolicy = 'error') { $userids=$this -> get_userid($user, $mailhost, $onDuplicatedPolicy); if ($userid === false) return false; if (!is_array($userids)) $userids = array($userids); $address_books = array(); foreach ($userids as $userid) { log_msg('DEBUG', "User $user ID : $userid"); $address_books[] = new rcube_contacts(self::db(), $userid); } return $address_books; } function export_user_contacts($user, $mailhost = null, $out = null, $onDuplicatedPolicy = 'error') { $address_books = $this->get_user_address_book($user, $mailhost, $onDuplicatedPolicy); if ($address_books === false) { log_msg('WARNING', "No address book found for user $user"); return false; } if (!$out) { $out = STDOUT; } log_msg('DEBUG', count($address_books). " address book(s) found for user $user."); $total_count=0; for($idx=0; $idx < count($address_books); $idx++) { log_msg('DEBUG', "Export contact of address book #$idx of user $user."); $address_books[$idx]->set_page(1); $address_books[$idx]->set_pagesize(99999); $result = $address_books[$idx]->list_records(null, 0, true); $count=0; while ($result && ($row = $result->next())) { if ($address_books[$idx]) { $this -> prepare_for_export($row, $address_books[$idx]); } // 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 #$idx of user $user"); $total_count += $count; } log_msg("INFO","$total_count contact(s) found in total for user $user"); return True; } 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 else if ($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']); } } } } // get arguments $opts = array( 'u' => 'user', 'm' => 'mailhost', 'o' => 'output', 'l' => 'last-login-on-duplicated', 'a' => 'all-on-duplicated', 'd' => 'debug', 'v' => 'verbose', 'h' => 'help', ); $args = rcube_utils::get_opt($opts); if ($_SERVER['argv'][1] == 'help' || isset($args['help'])) { print_usage(); exit; } $_log_levels=array( 'FATAL' => 0, 'WARNING' => 1, 'INFO' => 2, 'DEBUG' => 3 ); $log_level='WARNING'; if (isset($args['debug'])) { $log_level='DEBUG'; } elseif (isset($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"; } } // prompt for username if not set if (empty($args['user'])) { vputs("User: "); $args['user'] = trim(fgets(STDIN)); } $out=null; if (!empty($args['output'])) { $out=fopen($args['output'], 'w'); if ($out===false) { print "Fail to open output file !\n"; exit(1); } } if (isset($args['last-login-on-duplicated'])) { $onDuplicatedPolicy = 'last-login'; } elseif (isset($args['all-on-duplicated'])) { $onDuplicatedPolicy = 'all'; } else { $onDuplicatedPolicy = 'error'; } log_msg("DEBUG","On duplicated user policy : $onDuplicatedPolicy"); $export = new rcmail_export(); $result = $export -> export_user_contacts( $args['user'], $args['mailhost'], $out, $onDuplicatedPolicy ); if ($out) fclose($out); exit($result?0:1);