1#!/usr/bin/env php 2<?php 3/*********************************************** 4* File : z-push-admin.php 5* Project : Z-Push 6* Descr : This is a small command line 7* client to see and modify the 8* wipe status of Kopano users. 9* 10* Created : 14.05.2010 11* 12* Copyright 2007 - 2016 Zarafa Deutschland GmbH 13* 14* This program is free software: you can redistribute it and/or modify 15* it under the terms of the GNU Affero General Public License, version 3, 16* as published by the Free Software Foundation. 17* 18* This program is distributed in the hope that it will be useful, 19* but WITHOUT ANY WARRANTY; without even the implied warranty of 20* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21* GNU Affero General Public License for more details. 22* 23* You should have received a copy of the GNU Affero General Public License 24* along with this program. If not, see <http://www.gnu.org/licenses/>. 25* 26* Consult LICENSE file for details 27************************************************/ 28 29require_once 'vendor/autoload.php'; 30 31/** 32 * //TODO resync of single folders of a users device 33 */ 34 35/************************************************ 36 * MAIN 37 */ 38 define('BASE_PATH_CLI', dirname(__FILE__) ."/"); 39 set_include_path(get_include_path() . PATH_SEPARATOR . BASE_PATH_CLI); 40 41 if (!defined('ZPUSH_CONFIG')) define('ZPUSH_CONFIG', BASE_PATH_CLI . 'config.php'); 42 include_once(ZPUSH_CONFIG); 43 44 try { 45 ZPush::CheckConfig(); 46 ZPushAdminCLI::CheckEnv(); 47 ZPushAdminCLI::CheckOptions(); 48 49 if (! ZPushAdminCLI::SureWhatToDo()) { 50 // show error message if available 51 if (ZPushAdminCLI::GetErrorMessage()) 52 fwrite(STDERR, ZPushAdminCLI::GetErrorMessage() . "\n"); 53 54 echo ZPushAdminCLI::UsageInstructions(); 55 if (ZPushAdminCLI::$help) { 56 exit(0); 57 } 58 exit(1); 59 } 60 61 ZPushAdminCLI::RunCommand(); 62 } 63 catch (ZPushException $zpe) { 64 fwrite(STDERR, get_class($zpe) . ": ". $zpe->getMessage() . "\n"); 65 exit(1); 66 } 67 68 69/************************************************ 70 * Z-Push-Admin CLI 71 */ 72class ZPushAdminCLI { 73 const COMMAND_SHOWALLDEVICES = 1; 74 const COMMAND_SHOWDEVICESOFUSER = 2; 75 const COMMAND_SHOWUSERSOFDEVICE = 3; 76 const COMMAND_WIPEDEVICE = 4; 77 const COMMAND_REMOVEDEVICE = 5; 78 const COMMAND_RESYNCDEVICE = 6; 79 const COMMAND_CLEARLOOP = 7; 80 const COMMAND_SHOWLASTSYNC = 8; 81 const COMMAND_RESYNCFOLDER = 9; 82 const COMMAND_FIXSTATES = 10; 83 const COMMAND_RESYNCHIERARCHY = 11; 84 const COMMAND_ADDSHARED = 12; 85 const COMMAND_REMOVESHARED = 13; 86 const COMMAND_LISTALLSHARES = 14; 87 const COMMAND_LISTSTORESHARES = 15; 88 const COMMAND_LISTFOLDERSHARES = 16; 89 const COMMAND_LISTFOLDERS = 17; 90 const COMMAND_LISTDETAILS = 18; 91 92 const TYPE_OPTION_EMAIL = "email"; 93 const TYPE_OPTION_CALENDAR = "calendar"; 94 const TYPE_OPTION_CONTACT = "contact"; 95 const TYPE_OPTION_TASK = "task"; 96 const TYPE_OPTION_NOTE = "note"; 97 const TYPE_OPTION_HIERARCHY = "hierarchy"; 98 const TYPE_OPTION_GAB = "gab"; 99 100 static private $command; 101 static private $user = false; 102 static private $device = false; 103 static private $type = false; 104 static private $errormessage; 105 static private $daysold = false; 106 static private $shared = false; 107 static private $devicedriven = false; 108 static private $foldername = false; 109 static private $store = false; 110 static private $folderid = false; 111 static private $flags = 0; 112 113 static public $help = false; 114 115 /** 116 * Returns usage instructions 117 * 118 * @return string 119 * @access public 120 */ 121 static public function UsageInstructions() { 122 return "Usage:\n\tz-push-admin.php -a ACTION [options]\n\n" . 123 "Parameters:\n\t-a list/lastsync/listdetails/wipe/remove/resync/clearloop/fixstates/addshared/removeshared/listshares\n\t[-u] username\n\t[-d] deviceid\n" . 124 "\t[-t] type\tthe following types are available: '".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."', '".self::TYPE_OPTION_NOTE."', '".self::TYPE_OPTION_HIERARCHY."' of '".self::TYPE_OPTION_GAB."' (for KOE) or a folder id.\n" . 125 "\t[--shared|-s]\tshow detailed information about shared folders of a user in list.\n". 126 "\t[--days-old] n\tshow or remove profiles older than n days with lastsync or remove. n must be a positive integer.\n". 127 "\t[--devicedriven]\texecute the fixstates device driven. Recommended for specific enviroments with high traffic.\n\n". 128 "Actions:\n" . 129 "\tlist\t\t\t\t\t Lists all devices and synchronized users.\n" . 130 "\tlist -u USER\t\t\t\t Lists all devices of user USER.\n" . 131 "\tlist -d DEVICE\t\t\t\t Lists all users of device DEVICE.\n" . 132 "\tlastsync\t\t\t\t Lists all devices and synchronized users and the last synchronization time.\n" . 133 "\tlistdetails\t\t\t\t Lists all synchronized devices-users and their details in a tab separated list.\n" . 134 "\twipe -u USER\t\t\t\t Remote wipes all devices of user USER.\n" . 135 "\twipe -d DEVICE\t\t\t\t Remote wipes device DEVICE.\n" . 136 "\twipe -u USER -d DEVICE\t\t\t Remote wipes device DEVICE of user USER.\n" . 137 "\tremove -u USER\t\t\t\t Removes all state data of all devices of user USER.\n" . 138 "\tremove -d DEVICE\t\t\t Removes all state data of all users synchronized on device DEVICE.\n" . 139 "\tremove -u USER -d DEVICE\t\t Removes all related state data of device DEVICE of user USER.\n" . 140 "\tresync -u USER -d DEVICE\t\t Resynchronizes all data of device DEVICE of user USER.\n" . 141 "\tresync -t TYPE \t\t\t\t Resynchronizes all folders of type (possible values above) for all devices and users.\n" . 142 "\tresync -t TYPE -u USER \t\t\t Resynchronizes all folders of type (possible values above) for the user USER.\n" . 143 "\tresync -t TYPE -u USER -d DEVICE\t Resynchronizes all folders of type (possible values above) for a specified device and user.\n" . 144 "\tresync -t FOLDERID -u USER\t\t Resynchronize the specified folder id only. The USER should be specified for better performance.\n" . 145 "\tresync -t hierarchy -u USER -d DEVICE\t Resynchronize the folder hierarchy data for an optional USER and optional DEVICE.\n" . 146 "\tclearloop\t\t\t\t Clears system wide loop detection data.\n" . 147 "\tclearloop -d DEVICE -u USER\t\t Clears all loop detection data of a device DEVICE and an optional user USER.\n" . 148 "\tfixstates\t\t\t\t Checks the states for integrity and fixes potential issues.\n" . 149 "\tfixstates -u USER\t\t\t Checks the states for integrity and fixes potential issues of user USER.\n\n" . 150 "\taddshared -u USER -d DEVICE -n FOLDERNAME -o STORE -t TYPE -f FOLDERID -g FLAGS\n" . 151 "\t\t\t\t\t\t Adds a shared folder for a user.\n" . 152 "\t\t\t\t\t\t USER is required. If no DEVICE is given, the shared folder will be added to all of the devices of the user.\n" . 153 "\t\t\t\t\t\t FOLDERNAME the name of the shared folder. STORE - where this folder is located, e.g. \"SYSTEM\" (for public folder) or a username.\n" . 154 "\t\t\t\t\t\t TYPE is the folder type of the shared folder (possible values above, except 'hierarchy' and 'gab').\n" . 155 "\t\t\t\t\t\t FOLDERID is the id of shared folder.\n" . 156 "\t\t\t\t\t\t FLAGS is optional (default: '0'). Make sure you separate -g and value with \"=\", e.g. -g=4.\n" . 157 "\t\t\t\t\t\t Possible values for FLAGS: 0(none), 1 (Send-As from this folder), 4 (show calendar reminders for this folder), 8 (don't send notification emails for changes\n" . 158 "\t\t\t\t\t\t if the folder is read-only) and all bitwise or combinations of these flags.\n" . 159 "\tremoveshared -u USER -d DEVICE -f FOLDERID\n" . 160 "\t\t\t\t\t\t Removes a shared folder for a user.\n" . 161 "\t\t\t\t\t\t USER is required. If no DEVICE is given, the shared folder will be removed from all of the devices of the user.\n" . 162 "\t\t\t\t\t\t FOLDERID is the id of shared folder.\n" . 163 "\tlistshares -o STORE -f FOLDERID\n". 164 "\t\t\t\t\t\t Lists opened shared folders and who opened them on which device.\n" . 165 "\t\t\t\t\t\t STORE and FOLDERID are optional. If they're not provided then the script will display all open shares.\n" . 166 "\t\t\t\t\t\t STORE - whose shared folders to list, e.g. \"SYSTEM\" (for public folders) or a username.\n" . 167 "\t\t\t\t\t\t FOLDERID - list who opened the shared folder.\n" . 168 "\t\t\t\t\t\t If both STORE and FOLDERID are provided the script will only list who opened the folder ignoring the STORE parameter.\n" . 169 "\tlistfolders -u USER -d DEVICE\n". 170 "\t\t\t\t\t\t Returns each folder and FOLDERID of user USER and device DEVICE. Useful for getting FOLDERID to be used with the command: resync -t FOLDERID -u USER.\n". 171 "\t\t\t\t\t\t Note that if a device is offline, broken or not being synched for some time, this list will not be updated. If folders were created/renamed/removed\n". 172 "\t\t\t\t\t\t after the last synchronization, this will not be reflected in this list.\n". 173 "\n"; 174 } 175 176 /** 177 * Checks the environment 178 * 179 * @return 180 * @access public 181 */ 182 static public function CheckEnv() { 183 if (php_sapi_name() != "cli") 184 self::$errormessage = "This script can only be called from the CLI."; 185 186 if (!function_exists("getopt")) 187 self::$errormessage = "PHP Function getopt not found. Please check your PHP version and settings."; 188 } 189 190 /** 191 * Checks the options from the command line 192 * 193 * @return 194 * @access public 195 */ 196 static public function CheckOptions() { 197 if (self::$errormessage) 198 return; 199 200 $options = getopt("u:d:a:t:sn:o:f:g::h", array('user:', 'device:', 'action:', 'type:', 'days-old:', 'days-ago:', 'shared', 'foldername:', 'store', 'folderid:', 'flags::', 'devicedriven', 'help')); 201 202 // get 'user' 203 if (isset($options['u']) && !empty($options['u'])) 204 self::$user = strtolower(trim($options['u'])); 205 else if (isset($options['user']) && !empty($options['user'])) 206 self::$user = strtolower(trim($options['user'])); 207 208 // get 'device' 209 if (isset($options['d']) && !empty($options['d'])) 210 self::$device = strtolower(trim($options['d'])); 211 else if (isset($options['device']) && !empty($options['device'])) 212 self::$device = strtolower(trim($options['device'])); 213 214 // get 'action' 215 $action = false; 216 if (isset($options['a']) && !empty($options['a'])) 217 $action = strtolower(trim($options['a'])); 218 elseif (isset($options['action']) && !empty($options['action'])) 219 $action = strtolower(trim($options['action'])); 220 221 // get 'type' 222 if (isset($options['t']) && !empty($options['t'])) 223 self::$type = strtolower(trim($options['t'])); 224 elseif (isset($options['type']) && !empty($options['type'])) 225 self::$type = strtolower(trim($options['type'])); 226 227 if (isset($options['days-ago']) && !empty($options['days-ago'])) { 228 $options['days-old'] = $options['days-ago']; 229 } 230 231 if (isset($options['days-old']) && !empty($options['days-old'])) { 232 if (!is_numeric($options['days-old']) || $options['days-old'] < 0) { 233 self::$errormessage = "--days-old parameter must be a positive integer\n"; 234 self::$command = null; 235 return; 236 } 237 self::$daysold = trim($options['days-old']); 238 } 239 240 if (isset($options['s']) || isset($options['shared'])) { 241 self::$shared = true; 242 } 243 244 if (isset($options['devicedriven'])) { 245 if (self::$user){ 246 self::$errormessage = "--devicedriven doesn't accept the user -u parameter.\n"; 247 return; 248 } 249 self::$devicedriven = true; 250 } 251 252 // get 'foldername' 253 if (isset($options['n']) && !empty($options['n'])) 254 self::$foldername = trim($options['n']); 255 elseif (isset($options['foldername']) && !empty($options['foldername'])) 256 self::$foldername = trim($options['foldername']); 257 258 // get 'store' 259 if (isset($options['o']) && !empty($options['o'])) 260 self::$store = trim($options['o']); 261 elseif (isset($options['store']) && !empty($options['store'])) 262 self::$store = trim($options['store']); 263 264 // get 'folderid' 265 if (isset($options['f']) && !empty($options['f'])) 266 self::$folderid = trim($options['f']); 267 elseif (isset($options['folderid']) && !empty($options['folderid'])) 268 self::$folderid = trim($options['folderid']); 269 270 // get 'flags' 271 if (isset($options['flags'])) { 272 $options['g'] = $options['flags']; 273 } 274 275 if (isset($options['g'])) { 276 $flags = intval($options['g']); 277 if ($flags == DeviceManager::FLD_FLAGS_NONE || ($flags & (DeviceManager::FLD_FLAGS_SENDASOWNER | DeviceManager::FLD_FLAGS_CALENDARREMINDERS | DeviceManager::FLD_FLAGS_NOREADONLYNOTIFY))) { 278 self::$flags = $flags; 279 } 280 else { 281 self::$flags = false; 282 self::$errormessage = "Possible values for FLAGS: 0(none), 1 (Send-As from this folder), 4 (show calendar reminders for this folder), 5 (combination of Send-as and calendar reminders).\n"; 283 } 284 } 285 286 // if type is set, it must be one of known types or a 44 or 48 byte long folder id 287 if (self::$type !== false) { 288 if (self::$type !== self::TYPE_OPTION_EMAIL && 289 self::$type !== self::TYPE_OPTION_CALENDAR && 290 self::$type !== self::TYPE_OPTION_CONTACT && 291 self::$type !== self::TYPE_OPTION_TASK && 292 self::$type !== self::TYPE_OPTION_NOTE && 293 self::$type !== self::TYPE_OPTION_HIERARCHY && 294 self::$type !== self::TYPE_OPTION_GAB && 295 strlen(self::$type) !== 6 && // like U1f38d 296 strlen(self::$type) !== 44 && 297 strlen(self::$type) !== 48) { 298 self::$errormessage = "Wrong 'type'. Possible values are: ". 299 "'".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."', '".self::TYPE_OPTION_NOTE."', '".self::TYPE_OPTION_HIERARCHY."', '".self::TYPE_OPTION_GAB."' ". 300 "or a 6, 44 or 48 byte long folder id (as hex)."; 301 return; 302 } 303 } 304 305 if ((isset($options['h']) || isset($options['help'])) && $action === false) { 306 self::$help = true; 307 $action = 'help'; 308 } 309 310 // get a command for the requested action 311 switch ($action) { 312 // list data 313 case "list": 314 if (self::$user === false && self::$device === false) 315 self::$command = self::COMMAND_SHOWALLDEVICES; 316 317 if (self::$user !== false) 318 self::$command = self::COMMAND_SHOWDEVICESOFUSER; 319 320 if (self::$device !== false) 321 self::$command = self::COMMAND_SHOWUSERSOFDEVICE; 322 break; 323 324 // list data 325 case "lastsync": 326 self::$command = self::COMMAND_SHOWLASTSYNC; 327 break; 328 329 // list details 330 case "listdetails": 331 self::$command = self::COMMAND_LISTDETAILS; 332 break; 333 334 // remove wipe device 335 case "wipe": 336 if (self::$user === false && self::$device === false) 337 self::$errormessage = "Not possible to execute remote wipe. Device, user or both must be specified."; 338 else 339 self::$command = self::COMMAND_WIPEDEVICE; 340 break; 341 342 // remove device data of user 343 case "remove": 344 if (self::$user === false && self::$device === false) 345 self::$errormessage = "Not possible to remove data. Device, user or both must be specified."; 346 else 347 self::$command = self::COMMAND_REMOVEDEVICE; 348 break; 349 350 // resync a device 351 case "resync": 352 case "re-sync": 353 case "sync": 354 case "resynchronize": 355 case "re-synchronize": 356 case "synchronize": 357 // full resync 358 if (self::$type === false) { 359 if (self::$user === false || self::$device === false) 360 self::$errormessage = "Not possible to resynchronize device. Device and user must be specified."; 361 else 362 self::$command = self::COMMAND_RESYNCDEVICE; 363 } 364 else if (self::$type === self::TYPE_OPTION_HIERARCHY) { 365 self::$command = self::COMMAND_RESYNCHIERARCHY; 366 } 367 else { 368 self::$command = self::COMMAND_RESYNCFOLDER; 369 } 370 break; 371 372 // clear loop detection data 373 case "clearloop": 374 case "clearloopdetection": 375 self::$command = self::COMMAND_CLEARLOOP; 376 break; 377 378 // fix states 379 case "fixstates": 380 case "fix": 381 self::$command = self::COMMAND_FIXSTATES; 382 break; 383 384 case "addshared": 385 if (self::$user === false || self::$foldername === false || self::$store === false || self::$type === false || self::$folderid === false || self::$flags === false) { 386 if (!self::$errormessage) { 387 self::$errormessage = 'USER, FOLDERNAME, STORE, TYPE and FOLDERID are required for addshared command.'; 388 } 389 return; 390 } 391 else { 392 if (in_array(self::$type, array(self::TYPE_OPTION_CALENDAR, self::TYPE_OPTION_CONTACT, self::TYPE_OPTION_EMAIL, self::TYPE_OPTION_NOTE, self::TYPE_OPTION_TASK))) { 393 self::$command = self::COMMAND_ADDSHARED; 394 } 395 elseif (self::$type == self::TYPE_OPTION_HIERARCHY || self::$type == self::TYPE_OPTION_GAB) { 396 self::$errormessage = "'hierarchy' and 'gab' are not valid types for addshared action."; 397 return; 398 } 399 else { 400 self::$errormessage = sprintf("Adding folder of type '%s' is not supported.", self::$type); 401 return; 402 } 403 } 404 break; 405 406 case "removeshared": 407 if (self::$user === false || self::$folderid === false) { 408 self::$errormessage = 'USER and FOLDERID are required for removeshared command.'; 409 return; 410 } 411 self::$command = self::COMMAND_REMOVESHARED; 412 break; 413 414 case "listshares": 415 if (self::$store === false && self::$device === false) 416 self::$command = self::COMMAND_LISTALLSHARES; 417 418 if (self::$store !== false) 419 self::$command = self::COMMAND_LISTSTORESHARES; 420 421 if (self::$folderid !== false) 422 self::$command = self::COMMAND_LISTFOLDERSHARES; 423 break; 424 425 case "listfolders": 426 self::$command = self::COMMAND_LISTFOLDERS; 427 break; 428 429 case "help": 430 break; 431 432 default: 433 self::UsageInstructions(); 434 self::$help = false; 435 } 436 } 437 438 /** 439 * Indicates if the options from the command line 440 * could be processed correctly 441 * 442 * @return boolean 443 * @access public 444 */ 445 static public function SureWhatToDo() { 446 return isset(self::$command); 447 } 448 449 /** 450 * Returns a errormessage of things which could have gone wrong 451 * 452 * @return string 453 * @access public 454 */ 455 static public function GetErrorMessage() { 456 return (isset(self::$errormessage))?self::$errormessage:""; 457 } 458 459 /** 460 * Runs a command requested from an action of the command line 461 * 462 * @return 463 * @access public 464 */ 465 static public function RunCommand() { 466 echo "\n"; 467 switch(self::$command) { 468 case self::COMMAND_SHOWALLDEVICES: 469 self::CommandShowDevices(); 470 break; 471 472 case self::COMMAND_SHOWDEVICESOFUSER: 473 self::CommandShowDevices(); 474 break; 475 476 case self::COMMAND_SHOWUSERSOFDEVICE: 477 self::CommandDeviceUsers(); 478 break; 479 480 case self::COMMAND_SHOWLASTSYNC: 481 self::CommandShowLastSync(); 482 break; 483 484 case self::COMMAND_LISTDETAILS: 485 self::CommandListDetails(); 486 break; 487 488 case self::COMMAND_WIPEDEVICE: 489 if (self::$device) 490 echo sprintf("Are you sure you want to REMOTE WIPE device '%s' [y/N]: ", self::$device); 491 else 492 echo sprintf("Are you sure you want to REMOTE WIPE all devices of user '%s' [y/N]: ", self::$user); 493 494 $confirm = strtolower(trim(fgets(STDIN))); 495 if ( $confirm === 'y' || $confirm === 'yes') 496 self::CommandWipeDevice(); 497 else 498 echo "Aborted!\n"; 499 break; 500 501 case self::COMMAND_REMOVEDEVICE: 502 self::CommandRemoveDevice(); 503 break; 504 505 case self::COMMAND_RESYNCDEVICE: 506 if (self::$device == false) { 507 echo sprintf("Are you sure you want to re-synchronize all devices of user '%s' [y/N]: ", self::$user); 508 $confirm = strtolower(trim(fgets(STDIN))); 509 if ( !($confirm === 'y' || $confirm === 'yes')) { 510 echo "Aborted!\n"; 511 exit(1); 512 } 513 } 514 self::CommandResyncDevices(); 515 break; 516 517 case self::COMMAND_RESYNCFOLDER: 518 if (self::$device == false && self::$user == false) { 519 echo "Are you sure you want to re-synchronize this folder type of all devices and users [y/N]: "; 520 $confirm = strtolower(trim(fgets(STDIN))); 521 if ( !($confirm === 'y' || $confirm === 'yes')) { 522 echo "Aborted!\n"; 523 exit(1); 524 } 525 } 526 self::CommandResyncFolder(); 527 break; 528 529 case self::COMMAND_RESYNCHIERARCHY: 530 if (self::$device == false && self::$user == false) { 531 echo "Are you sure you want to re-synchronize the hierarchy of all devices and users [y/N]: "; 532 $confirm = strtolower(trim(fgets(STDIN))); 533 if ( !($confirm === 'y' || $confirm === 'yes')) { 534 echo "Aborted!\n"; 535 exit(1); 536 } 537 } 538 self::CommandResyncHierarchy(); 539 break; 540 541 case self::COMMAND_CLEARLOOP: 542 self::CommandClearLoopDetectionData(); 543 break; 544 545 case self::COMMAND_FIXSTATES: 546 if (self::$user === false) { 547 self::CommandFixStates(); 548 } 549 else { 550 self::CommandFixStates(self::$user); 551 } 552 break; 553 554 case self::COMMAND_ADDSHARED: 555 self::CommandAddShared(); 556 break; 557 558 case self::COMMAND_REMOVESHARED: 559 self::CommandRemoveShared(); 560 break; 561 562 case self::COMMAND_LISTALLSHARES: 563 case self::COMMAND_LISTSTORESHARES: 564 case self::COMMAND_LISTFOLDERSHARES: 565 self::CommandListShares(); 566 break; 567 568 case self::COMMAND_LISTFOLDERS: 569 self::CommandListFolders(); 570 break; 571 } 572 echo "\n"; 573 } 574 575 /** 576 * Command "Show all devices" and "Show devices of user" 577 * Prints the device id of/and connected users 578 * 579 * @return 580 * @access public 581 */ 582 static public function CommandShowDevices() { 583 $devicelist = ZPushAdmin::ListDevices(self::$user); 584 if (empty($devicelist)) 585 echo "\tno devices found\n"; 586 else { 587 if (self::$user === false) { 588 echo "All synchronized devices\n\n"; 589 echo str_pad("Device id", 36). "Synchronized users\n"; 590 echo "-----------------------------------------------------\n"; 591 } 592 else 593 echo "Synchronized devices of user: ". self::$user. "\n"; 594 } 595 596 foreach ($devicelist as $deviceId) { 597 if (self::$user === false) { 598 echo str_pad($deviceId, 36) . implode (",", ZPushAdmin::ListUsers($deviceId)) ."\n"; 599 } 600 else 601 self::printDeviceData($deviceId, self::$user); 602 } 603 } 604 605 /** 606 * Command "Show all devices and users with last sync time" 607 * Prints the device id of/and connected users 608 * 609 * @return 610 * @access public 611 */ 612 static public function CommandShowLastSync() { 613 $devicelist = ZPushAdmin::ListDevices(false); 614 if (empty($devicelist)) 615 echo "\tno devices found\n"; 616 else { 617 echo "All known devices and users and their last synchronization time\n\n"; 618 echo str_pad("Device id", 36) . str_pad("Synchronized user", 31) . str_pad("Last sync time", 33) . "Short Ids\n"; 619 echo "------------------------------------------------------------------------------------------------------------------\n"; 620 } 621 622 $now = time(); 623 foreach ($devicelist as $deviceId) { 624 $users = ZPushAdmin::ListUsers($deviceId); 625 foreach ($users as $user) { 626 $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); 627 $daysOld = floor(($now - $device->GetLastSyncTime()) / 86400); 628 if (self::$daysold > $daysOld) { 629 continue; 630 } 631 $lastsync = $device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) . ' (' . str_pad($daysOld, 3, ' ', STR_PAD_LEFT) . ' days ago)' : "never"; 632 $hasShortFolderIds = $device->HasFolderIdMapping() ? "Yes":"No"; 633 echo str_pad($deviceId, 36) . str_pad($user, 30) . " " . str_pad($lastsync, 33) . $hasShortFolderIds . "\n"; 634 } 635 } 636 } 637 638 /** 639 * Command "Lists all synchronized devices-users and their details in a tab separated list" 640 * 641 * Prints the device id of/and connected users: 642 * - Device id 643 * - Synchronized users 644 * - Last sync time 645 * - deviceType 646 * - deviceModel 647 * - deviceOS 648 * - ASVersion 649 * - KoeVersion 650 * - Total folders 651 * - Synchronized folders 652 * - Not synchronized folders 653 * - Shared/impersonated folders 654 * - Ignored messages 655 * - KOE inactive 656 * 657 * @return 658 * @access public 659 */ 660 static public function CommandListDetails() { 661 662 $devicelist = ZPushAdmin::ListDevices(); 663 if (empty($devicelist)) 664 echo "\tno devices found\n"; 665 else { 666 echo "All synchronized devices\n\n", 667 "Device id\t", 668 "Synchronized user\t", 669 "Last sync time\t", 670 "deviceType\t", 671 "UserAgent\t", 672 "deviceModel\t", 673 "deviceOS\t", 674 "ASVersion\t", 675 "KoeVersion\t", 676 "Total folders\t", 677 "Synchronized folders\t", 678 "Not synchronized folders\t", 679 "Shared/impersonated folders\t", 680 "Ignored messages\t", 681 "KOE inactive\t\n", 682 str_repeat('-', 163), "\n"; 683 } 684 $now = time(); 685 foreach ($devicelist as $deviceId) { 686 $users = ZPushAdmin::ListUsers($deviceId); 687 foreach ($users as $usr) { 688 $device = ZPushAdmin::GetDeviceDetails($deviceId, $usr); 689 $daysOld = floor(($now - $device->GetLastSyncTime()) / 86400); 690 if (self::$daysold > $daysOld) { 691 continue; 692 } 693 $lastsync = $device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) . ' (' . str_pad($daysOld, 3, ' ', STR_PAD_LEFT) . ' days ago)' : "never"; 694 $data = self::ListDeviceFolders($deviceId, $usr); 695 echo $deviceId, "\t", 696 $usr, "\t", 697 $lastsync, "\t", 698 ($device->GetDeviceType() !== ASDevice::UNDEFINED ? $device->GetDeviceType() : "unknown"), "\t", 699 ($device->GetDeviceUserAgent()!== ASDevice::UNDEFINED ? $device->GetDeviceUserAgent() : "unknown"), "\t", 700 $device->GetDeviceModel(), "\t", 701 $device->GetDeviceOS(), "\t", 702 ($device->GetASVersion() ? $device->GetASVersion() : "unknown"), "\t", 703 $device->GetKoeVersion(), "\t", 704 $data[0], "\t", 705 $data[1], "\t", 706 $data[2], "\t", 707 $data[3], "\t", 708 ( (isset($device->ignoredmessages) && !empty($device->ignoredmessages)) ? count($device->ignoredmessages) : 0), "\t", 709 (($device->GetKoeLastAccess() && $device->GetKoeLastAccess() + 25260 < $device->GetLastSyncTime()) ? "KOE inactive":""), "\n"; 710 } 711 } 712 } 713 714 /** 715 * Returns an array with the folders stats of a device id: 716 * - Total folders 717 * - Synchronized folders 718 * - Not synchronized folders 719 * - Shared/impersonated folders 720 * 721 * @return 722 * @access private 723 */ 724 static private function ListDeviceFolders($deviceId, $user) { 725 726 $device = ZPushAdmin::GetDeviceDetails($deviceId, $user, true); 727 if (! $device instanceof ASDevice) { 728 printf("Folder details failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); 729 return false; 730 } 731 $folders = $device->GetAllFolderIds(); 732 $synchedFolders = 0; 733 $notSynchedFolders = 0; 734 $sharedFolders = 0; 735 $hc = $device->GetHierarchyCache(); 736 foreach ($folders as $folderid) { 737 if ($device->GetFolderUUID($folderid)) { 738 $synchedFolders++; 739 } 740 else { 741 $notSynchedFolders++; 742 } 743 $folder = $hc->GetFolder($folderid); 744 $name = $folder ? $folder->displayname : "unknown"; 745 if (Utils::GetFolderOriginFromId($folderid) != DeviceManager::FLD_ORIGIN_USER) { 746 $sharedFolders++; 747 } 748 } 749 return array(count($folders), $synchedFolders, $notSynchedFolders, $sharedFolders); 750 } 751 752 /** 753 * Command "Show users of device" 754 * Prints informations about all users which use a device 755 * 756 * @return 757 * @access public 758 */ 759 static public function CommandDeviceUsers() { 760 $users = ZPushAdmin::ListUsers(self::$device); 761 762 if (empty($users)) { 763 echo "\tno user data synchronized to device\n"; 764 } 765 // if a user is specified, we only want to see the devices of this one 766 else if (self::$user !== false && !in_array(self::$user, $users)) { 767 printf("\tuser '%s' not known in device data '%s'\n", self::$user, self::$device); 768 } 769 770 foreach ($users as $user) { 771 if (self::$user !== false && strtolower($user) !== self::$user) { 772 continue; 773 } 774 echo "Synchronized by user: ". $user. "\n"; 775 self::printDeviceData(self::$device, $user); 776 } 777 } 778 779 /** 780 * Command "Wipe device" 781 * Marks a device of that user to be remotely wiped 782 * 783 * @return 784 * @access public 785 */ 786 static public function CommandWipeDevice() { 787 $stat = ZPushAdmin::WipeDevice($_SERVER["LOGNAME"], self::$user, self::$device); 788 789 if (self::$user !== false && self::$device !== false) { 790 echo sprintf("Mark device '%s' of user '%s' to be wiped: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 791 792 if ($stat) { 793 echo "Updated information about this device:\n"; 794 self::printDeviceData(self::$device, self::$user); 795 } 796 } 797 elseif (self::$user !== false) { 798 echo sprintf("Mark devices of user '%s' to be wiped: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 799 self::CommandShowDevices(); 800 } 801 } 802 803 /** 804 * Command "Remove device". 805 * Removes a device of that user from the device list. 806 * 807 * @return 808 * @access public 809 */ 810 static public function CommandRemoveDevice() { 811 $stat = ZPushAdmin::RemoveDevice(self::$user, self::$device, self::$daysold, time()); 812 if (self::$user === false) 813 echo sprintf("State data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 814 elseif (self::$device === false) 815 echo sprintf("State data of all devices of user '%s' removed: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 816 else 817 echo sprintf("State data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 818 819 if (ZPushAdmin::$status == ZPushAdmin::STATUS_DEVICE_SYNCED_AFTER_DAYSOLD) { 820 print("Some devices might not have been removed because of --days-old parameter. Check Z-Push log file for more details.\n"); 821 } 822 } 823 824 /** 825 * Command "Resync device(s)" 826 * Resyncs one or all devices of that user 827 * 828 * @return 829 * @access public 830 */ 831 static public function CommandResyncDevices() { 832 $stat = ZPushAdmin::ResyncDevice(self::$user, self::$device); 833 echo sprintf("Resync of device '%s' of user '%s': %s", self::$device, self::$user, ($stat)?'Requested':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 834 } 835 836 /** 837 * Command "Resync folder(s)" 838 * Resyncs a folder type of a specific device/user or of all users 839 * 840 * @return 841 * @access public 842 */ 843 static public function CommandResyncFolder() { 844 // if no device is specified, search for all devices of a user. If user is not set, all devices are returned. 845 if (self::$device === false) { 846 $devicelist = ZPushAdmin::ListDevices(self::$user); 847 if (empty($devicelist)) { 848 echo "\tno devices/users found\n"; 849 return true; 850 } 851 } 852 else 853 $devicelist = array(self::$device); 854 855 foreach ($devicelist as $deviceId) { 856 $users = ZPushAdmin::ListUsers($deviceId); 857 foreach ($users as $user) { 858 if (self::$user && self::$user != $user) 859 continue; 860 self::resyncFolder($deviceId, $user, self::$type); 861 } 862 } 863 864 } 865 866 /** 867 * Command "Resync hierarchy" 868 * Resyncs a folder type of a specific device/user or of all users 869 * 870 * @return 871 * @access public 872 */ 873 static public function CommandResyncHierarchy() { 874 // if no device is specified, search for all devices of a user. If user is not set, all devices are returned. 875 if (self::$device === false) { 876 $devicelist = ZPushAdmin::ListDevices(self::$user); 877 if (empty($devicelist)) { 878 echo "\tno devices/users found\n"; 879 return true; 880 } 881 } 882 else 883 $devicelist = array(self::$device); 884 885 foreach ($devicelist as $deviceId) { 886 $users = ZPushAdmin::ListUsers($deviceId); 887 foreach ($users as $user) { 888 if (self::$user && self::$user != $user) 889 continue; 890 self::resyncHierarchy($deviceId, $user); 891 } 892 } 893 894 } 895 896 /** 897 * Command to clear the loop detection data 898 * Mobiles may enter loop detection (one-by-one synchring due to timeouts / erros). 899 * 900 * @return 901 * @access public 902 */ 903 static public function CommandClearLoopDetectionData() { 904 $stat = false; 905 $stat = ZPushAdmin::ClearLoopDetectionData(self::$user, self::$device); 906 if (self::$user === false && self::$device === false) 907 echo sprintf("System wide loop detection data removed: %s", ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 908 elseif (self::$user === false) 909 echo sprintf("Loop detection data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 910 elseif (self::$device === false && self::$user !== false) 911 echo sprintf("Error: %s", ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_WARN)). "\n"; 912 else 913 echo sprintf("Loop detection data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; 914 } 915 916 /** 917 * Command to add a shared folder for a user. 918 * 919 * @return 920 * @access public 921 */ 922 static public function CommandAddShared() { 923 // If no device is specified, search for all devices of a user. 924 if (self::$device === false) { 925 $devicelist = ZPushAdmin::ListDevices(self::$user); 926 if (empty($devicelist)) { 927 echo "\tno devices/users found\n"; 928 return true; 929 } 930 } 931 else { 932 $devicelist = array(self::$device); 933 } 934 switch (self::$type) { 935 case self::TYPE_OPTION_EMAIL: 936 $type = SYNC_FOLDER_TYPE_USER_MAIL; 937 break; 938 case self::TYPE_OPTION_CALENDAR: 939 $type = SYNC_FOLDER_TYPE_USER_APPOINTMENT; 940 break; 941 case self::TYPE_OPTION_CONTACT: 942 $type = SYNC_FOLDER_TYPE_USER_CONTACT; 943 break; 944 case self::TYPE_OPTION_TASK: 945 $type = SYNC_FOLDER_TYPE_USER_TASK; 946 break; 947 case self::TYPE_OPTION_NOTE: 948 $type = SYNC_FOLDER_TYPE_USER_NOTE; 949 break; 950 } 951 952 foreach ($devicelist as $devid) { 953 $status = ZPushAdmin::AdditionalFolderAdd(self::$user, $devid, self::$store, self::$folderid, self::$foldername, $type, self::$flags); 954 if ($status) { 955 printf("Successfully added folder '%s' for user '%s' on device '%s'.\n", self::$foldername, self::$user, $devid); 956 } 957 else { 958 printf("Failed adding folder '%s' for user '%s' on device '%s'. %s.\n", self::$foldername, self::$user, $devid, ZLog::GetLastMessage(LOGLEVEL_ERROR)); 959 } 960 } 961 962 } 963 964 /** 965 * Command to remove a shared folder for a user. 966 * 967 * @return 968 * @access public 969 */ 970 static public function CommandRemoveShared() { 971 // if no device is specified, search for all devices of a user. If user is not set, all devices are returned. 972 if (self::$device === false) { 973 $devicelist = ZPushAdmin::ListDevices(self::$user); 974 if (empty($devicelist)) { 975 echo "\tno devices/users found\n"; 976 return true; 977 } 978 } 979 else { 980 $devicelist = array(self::$device); 981 } 982 983 foreach ($devicelist as $devid) { 984 $status = ZPushAdmin::AdditionalFolderRemove(self::$user, $devid, self::$folderid); 985 if ($status) { 986 printf("Successfully removed folder with id '%s' for user '%s' on device '%s'.\n", self::$folderid, self::$user, $devid); 987 } 988 else { 989 printf("Failed removing folder with id '%s' for user '%s' on device '%s'. %s.\n", self::$folderid, self::$user, $devid, ZLog::GetLastMessage(LOGLEVEL_ERROR)); 990 } 991 } 992 993 } 994 995 /** 996 * Command to list all configured shares. 997 * 998 * @access public 999 * @return void 1000 */ 1001 static public function CommandListShares() { 1002 $devicelist = ZPushAdmin::ListDevices(); 1003 if (empty($devicelist)) { 1004 echo "\tno devices/users found\n"; 1005 return true; 1006 } 1007 $shares = array(); 1008 $folderToStore = array(); 1009 1010 // It's necessary to always get all users and devices and then the shares of the device 1011 // as the shares are only available in the device. 1012 foreach ($devicelist as $deviceId) { 1013 $users = ZPushAdmin::ListUsers($deviceId); 1014 foreach ($users as $user) { 1015 $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); 1016 if ($device instanceof ASDevice) { 1017 $sharedFolders = $device->GetAdditionalFolders(); 1018 if (!empty($sharedFolders)) { 1019 foreach ($sharedFolders as $sharedFolder) { 1020 if (!isset($sharedFolder['store'], $sharedFolder['folderid'], $sharedFolder['name'])) { 1021 printf("User '%s' has a shared folder configured on device '%s', but store, folderid, or name of this share are not set: %s", $user, $deviceId, print_r($sharedFolder, 1)); 1022 continue; 1023 } 1024 $folderToStore[$sharedFolder['folderid']] = strtolower($sharedFolder['store']); 1025 $shares[strtolower($sharedFolder['store'])][$sharedFolder['folderid']][] = array('user' => $user, 'deviceId' => $deviceId, 'name' => $sharedFolder['name']); 1026 } 1027 } 1028 } 1029 } 1030 } 1031 1032 1033 if (empty($shares)) { 1034 print("There currently aren't any opened shares.\n"); 1035 return; 1036 } 1037 1038 if (self::$command == self::COMMAND_LISTFOLDERSHARES) { 1039 if (!isset($folderToStore[self::$folderid])) { 1040 printf("The folder with the requested id '%s' isn't currently opened by anyone.\n", self::$folderid); 1041 return; 1042 } 1043 printf("Displaying opened shares for folderid %s.\n", self::$folderid); 1044 self::printShares(array($folderToStore[self::$folderid] => array(self::$folderid => $shares[$folderToStore[self::$folderid]][self::$folderid]))); 1045 } 1046 elseif (self::$command == self::COMMAND_LISTSTORESHARES) { 1047 $store = strtolower(self::$store); 1048 if (!isset($shares[$store])) { 1049 printf("None of the folders of the requested store '%s' is currently opened.\n", self::$store); 1050 return; 1051 } 1052 self::printShares(array($store => $shares[$store])); 1053 } 1054 else { 1055 print("Displaying opened shares of all users.\n"); 1056 self::printShares($shares); 1057 } 1058 } 1059 1060 /** 1061 * Command to list each folder and FOLDERID of user USER and device DEVICE. 1062 * 1063 * @access public 1064 * @return void 1065 */ 1066 static public function CommandListFolders() { 1067 1068 $device = ZPushAdmin::GetDeviceDetails(self::$device, self::$user, true); 1069 if (! $device instanceof ASDevice) { 1070 printf("Folder details failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); 1071 return false; 1072 } 1073 1074 echo "Folders list of DeviceId: ".self::$device."\n"; 1075 echo "-----------------------------------------------------------------------\n"; 1076 $folders = $device->GetAllFolderIds(); 1077 $synchedFolders = 0; 1078 $notSynchedFolders = 0; 1079 $sharedFolders = 0; 1080 $hc = $device->GetHierarchyCache(); 1081 echo "FolderID\t\t\t\t\tShortID\tDisplay Name\n"; 1082 echo "-----------------------------------------------------------------------\n"; 1083 foreach ($folders as $folderid) { 1084 if ($device->GetFolderUUID($folderid)) { 1085 $synchedFolders++; 1086 $notSynced = ''; 1087 } 1088 else { 1089 $notSynchedFolders++; 1090 $notSynced = "\t"."NOT SYNCHED"; 1091 } 1092 $folder = $hc->GetFolder($folderid); 1093 $name = $folder ? $folder->displayname : "unknown"; 1094 if (strcmp($name, 'unknown') == 0) { 1095 echo "\t\t\t\t\t"; 1096 } 1097 echo $folder->BackendId."\t".$folderid."\t".$name.$notSynced."\n"; 1098 if (Utils::GetFolderOriginFromId($folderid) != DeviceManager::FLD_ORIGIN_USER) { 1099 $sharedFolders++; 1100 } 1101 } 1102 echo "\nTotal folders: ".count($folders)."\n"; 1103 echo "Synchronized folders: ".$synchedFolders."\n"; 1104 echo "Not synchronized folders: ".$notSynchedFolders."\n"; 1105 echo "Shared/impersonated folders: ".$sharedFolders."\n"; 1106 echo "Short folder Ids: ". ($device->HasFolderIdMapping() ? "Yes":"No") ."\n"; 1107 } 1108 1109 /** 1110 * Resynchronizes a folder type of a device & user 1111 * 1112 * @param string $deviceId the id of the device 1113 * @param string $user the user 1114 * @param string $type the folder type 1115 * 1116 * @return 1117 * @access private 1118 */ 1119 static private function resyncFolder($deviceId, $user, $type) { 1120 $device = ZPushAdmin::GetDeviceDetails($deviceId, $user); 1121 1122 if (! $device instanceof ASDevice) { 1123 echo sprintf("Folder resync failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); 1124 return false; 1125 } 1126 1127 $folders = array(); 1128 $searchFor = $type; 1129 // get the KOE gab folderid 1130 if ($type == self::TYPE_OPTION_GAB) { 1131 if (@constant('KOE_GAB_FOLDERID') !== '') { 1132 $gab = KOE_GAB_FOLDERID; 1133 } 1134 else { 1135 $gab = $device->GetKoeGabBackendFolderId(); 1136 } 1137 if (!$gab) { 1138 printf("Could not find KOE GAB folderid for device '%s' of user '%s'\n", $deviceId, $user); 1139 return false; 1140 } 1141 $searchFor = $gab; 1142 } 1143 // potential long ids are converted to folderids here, incl. the gab id 1144 $searchFor = strtolower($device->GetFolderIdForBackendId($searchFor, false, false, null)); 1145 1146 foreach ($device->GetAllFolderIds() as $folderid) { 1147 // if submitting a folderid as type to resync a specific folder. 1148 if (strtolower($folderid) === $searchFor) { 1149 printf("Found and resynching requested folderid '%s' on device '%s' of user '%s'\n", $folderid, $deviceId, $user); 1150 $folders[] = $folderid; 1151 break; 1152 } 1153 1154 if ($device->GetFolderUUID($folderid)) { 1155 $foldertype = $device->GetFolderType($folderid); 1156 switch($foldertype) { 1157 case SYNC_FOLDER_TYPE_APPOINTMENT: 1158 case SYNC_FOLDER_TYPE_USER_APPOINTMENT: 1159 if ($searchFor == "calendar") 1160 $folders[] = $folderid; 1161 break; 1162 case SYNC_FOLDER_TYPE_CONTACT: 1163 case SYNC_FOLDER_TYPE_USER_CONTACT: 1164 if ($searchFor == "contact") 1165 $folders[] = $folderid; 1166 break; 1167 case SYNC_FOLDER_TYPE_TASK: 1168 case SYNC_FOLDER_TYPE_USER_TASK: 1169 if ($searchFor == "task") 1170 $folders[] = $folderid; 1171 break; 1172 case SYNC_FOLDER_TYPE_NOTE: 1173 case SYNC_FOLDER_TYPE_USER_NOTE: 1174 if ($searchFor == "note") 1175 $folders[] = $folderid; 1176 break; 1177 default: 1178 if ($searchFor == "email") 1179 $folders[] = $folderid; 1180 break; 1181 } 1182 } 1183 } 1184 1185 $stat = ZPushAdmin::ResyncFolder($user, $deviceId, $folders); 1186 echo sprintf("Resync of %d folders of type '%s' on device '%s' of user '%s': %s\n", count($folders), $type, $deviceId, $user, ($stat)?'Requested':ZLog::GetLastMessage(LOGLEVEL_ERROR)); 1187 } 1188 1189 /** 1190 * Resynchronizes the hierarchy of a device & user 1191 * 1192 * @param string $deviceId the id of the device 1193 * @param string $user the user 1194 * 1195 * @return 1196 * @access private 1197 */ 1198 static private function resyncHierarchy($deviceId, $user) { 1199 $stat = ZPushAdmin::ResyncHierarchy($user, $deviceId); 1200 echo sprintf("Removing hierarchy information for resync on device '%s' of user '%s': %s\n", $deviceId, $user, ($stat)?'Requested':ZLog::GetLastMessage(LOGLEVEL_ERROR)); 1201 } 1202 1203 /** 1204 * Fixes the states for potential issues. 1205 * 1206 * @param string $username the user 1207 * 1208 * @return 1209 * @access private 1210 */ 1211 static private function CommandFixStates($username=false) { 1212 echo "Validating and fixing states (this can take some time):\n"; 1213 if(!self::$devicedriven){ 1214 1215 echo "\t".date('H:i:s')." Checking username casings: "; 1216 if ($stat = ZPushAdmin::FixStatesDifferentUsernameCases($username)) 1217 printf("Processed: %d - Converted: %d - Removed: %d\n", $stat[0], $stat[1], $stat[2]); 1218 else 1219 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1220 1221 // fixes ZP-339 1222 echo "\t".date('H:i:s')." Checking available devicedata & user linking: "; 1223 if ($stat = ZPushAdmin::FixStatesDeviceToUserLinking($username)) 1224 printf("Processed: %d - Fixed: %d\n", $stat[0], $stat[1]); 1225 else 1226 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1227 1228 echo "\t".date('H:i:s')." Checking for unreferenced (obsolete) state files: "; 1229 if (($stat = ZPushAdmin::FixStatesUserToStatesLinking($username)) !== false) 1230 printf("Processed: %d - Deleted: %d\n", $stat[0], $stat[1]); 1231 else 1232 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1233 1234 echo "\t".date('H:i:s')." Checking for hierarchy folder data state: "; 1235 if (($stat = ZPushAdmin::FixStatesHierarchyFolderData($username)) !== false) 1236 printf("Devices: %d - Processed: %d - Fixed: %d - Device+User without hierarchy: %d\n", $stat[0], $stat[1], $stat[2], $stat[3]); 1237 else 1238 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1239 1240 echo "\t".date('H:i:s')." Checking flags of shared folders: "; 1241 if (($stat = ZPushAdmin::FixStatesAdditionalFolders($username)) !== false) 1242 printf("Devices: %d - Devices with additional folders: %d - Fixed: %d\n", $stat[0], $stat[1], $stat[2]); 1243 else 1244 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1245 } else{ 1246 //used for recording the total process time 1247 $fsStartTime= new DateTime("now"); 1248 //load devices list 1249 $devices = ZPushAdmin::GetAllDevices(); 1250 $devicesCount = count($devices); 1251 echo "Found ".$devicesCount." devices\r\n"; 1252 $processedDevices = 0; 1253 //structure for hold stats 1254 $stats = array( 1255 // Results of ZPushAdmin::FixStatesDifferentUsernameCases 1256 array( "processed" => 0, "converted" => 0, "removed" => 0 ), 1257 // Results of ZPushAdmin::FixStatesDeviceToUserLinking 1258 array( "processed" => 0, "fixed" => 0 ), 1259 // Results of ZPushAdmin::FixStatesUserToStatesLinking 1260 array( "processed" => 0, "deleted" => 0 ), 1261 // Results of ZPushAdmin::FixStatesHierarchyFolderData 1262 array( "devices" => 0, "processed" => 0, "fixed" => 0, "noHierarchy" =>0 ), 1263 // Results of ZPushAdmin::FixStatesAdditionalFolders 1264 array( "devices" => 0, "devicesWithAddFolders" => 0, "fixed" => 0) 1265 ); 1266 1267 // loop every device 1268 foreach ($devices as $devid) { 1269 $processedDevices++; 1270 if (defined('LOGFIXSTATES') && LOGFIXSTATES === true) { 1271 echo "\tProcessing ".$processedDevices."/".$devicesCount." devices: ".$devid."\r\n"; 1272 ZLog::Write(LOGLEVEL_INFO, sprintf("FixStatesDeviceDriven(): Processing %d of %d . Device %s", $processedDevices , $devicesCount, $devid)); 1273 } 1274 1275 if ($stat = ZPushAdmin::FixStatesDifferentUsernameCases(false,$devid)){ 1276 $stats[0]["processed"] += $stat[0]; 1277 $stats[0]["converted"] += $stat[1]; 1278 $stats[0]["removed"] += $stat[2]; 1279 } 1280 else 1281 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1282 1283 // fixes ZP-339 1284 if ($stat = ZPushAdmin::FixStatesDeviceToUserLinking(false,$devid)){ 1285 $stats[1]["processed"] += $stat[0]; 1286 $stats[1]["fixed"] += $stat[1]; 1287 } 1288 else 1289 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1290 1291 if (($stat = ZPushAdmin::FixStatesUserToStatesLinking(false,$devid)) !== false){ 1292 $stats[2]["processed"] += $stat[0]; 1293 $stats[2]["deleted"] += $stat[1]; 1294 } 1295 else 1296 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1297 1298 if (($stat = ZPushAdmin::FixStatesHierarchyFolderData(false,$devid)) !== false){ 1299 $stats[3]["devices"] += $stat[0]; 1300 $stats[3]["processed"] += $stat[1]; 1301 $stats[3]["fixed"] += $stat[2]; 1302 $stats[3]["noHierarchy"] += $stat[3]; 1303 } 1304 else 1305 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1306 1307 if (($stat = ZPushAdmin::FixStatesAdditionalFolders(false,$devid)) !== false){ 1308 $stats[4]["devices"] += $stat[0]; 1309 $stats[4]["devicesWithAddFolders"] += $stat[1]; 1310 $stats[4]["fixed"] += $stat[2]; 1311 } 1312 else 1313 echo ZLog::GetLastMessage(LOGLEVEL_ERROR) . "\n"; 1314 } 1315 //used for recording the total process time 1316 $fsEndTime = new DateTime("now"); 1317 $timeInterval = date_diff($fsStartTime, $fsEndTime)->format('%H:%I:%S'); 1318 echo "Total process time: ".$timeInterval."\n"."\n"; 1319 ZLog::Write(LOGLEVEL_INFO, sprintf("FixStatesDeviceDriven(): Finished. Total process time: ".$timeInterval)); 1320 printf("Check username casings. Processed: %d - Converted: %d - Removed: %d\n", 1321 $stats[0]["processed"], $stats[0]["converted"], $stats[0]["removed"] ); 1322 printf("Check available devicedata & user linking. Processed: %d - Fixed: %d\n", 1323 $stats[1]["processed"], $stats[1]["fixed"] ); 1324 printf("Check for unreferenced (obsolete) state files. Processed: %d - Deleted: %d\n", 1325 $stats[2]["processed"], $stats[2]["deleted"] ); 1326 printf("Check for hierarchy folder data state. Devices: %d - Processed: %d - Fixed: %d - Device+User without hierarchy: %d\n", 1327 $stats[3]["devices"], $stats[3]["processed"], $stats[3]["fixed"], $stats[3]["noHierarchy"] ); 1328 printf("Check flags of shared folders. Devices: %d - Devices with additional folders: %d - Fixed: %d\n", 1329 $stats[4]["devices"], $stats[4]["devicesWithAddFolders"], $stats[4]["fixed"] ); 1330 } 1331 } 1332 1333 /** 1334 * Prints detailed informations about a device 1335 * 1336 * @param string $deviceId the id of the device 1337 * @param string $user the user 1338 * 1339 * @return 1340 * @access private 1341 */ 1342 static private function printDeviceData($deviceId, $user) { 1343 global $additionalFolders; 1344 $device = ZPushAdmin::GetDeviceDetails($deviceId, $user, true); 1345 1346 if (! $device instanceof ASDevice) { 1347 echo sprintf("Folder resync failed: %s\n", ZLog::GetLastMessage(LOGLEVEL_ERROR)); 1348 return false; 1349 } 1350 1351 echo "-----------------------------------------------------\n"; 1352 echo "DeviceId:\t\t$deviceId\n"; 1353 echo "Device type:\t\t". ($device->GetDeviceType() !== ASDevice::UNDEFINED ? $device->GetDeviceType() : "unknown") ."\n"; 1354 echo "UserAgent:\t\t".($device->GetDeviceUserAgent()!== ASDevice::UNDEFINED ? $device->GetDeviceUserAgent() : "unknown") ."\n"; 1355 // TODO implement $device->GetDeviceUserAgentHistory() 1356 1357 if (!self::$shared) { 1358 // Gather some statistics about synchronized folders 1359 $folders = $device->GetAllFolderIds(); 1360 $synchedFolders = 0; 1361 $synchedFolderTypes = array(); 1362 $syncedFoldersInProgress = 0; 1363 $hc = $device->GetHierarchyCache(); 1364 foreach ($folders as $folderid) { 1365 if ($device->GetFolderUUID($folderid)) { 1366 $synchedFolders++; 1367 $type = $device->GetFolderType($folderid); 1368 $folder = $hc->GetFolder($folderid); 1369 $name = $folder ? $folder->displayname : "unknown"; 1370 switch($type) { 1371 case SYNC_FOLDER_TYPE_APPOINTMENT: 1372 case SYNC_FOLDER_TYPE_USER_APPOINTMENT: 1373 if ($name == KOE_GAB_NAME) { 1374 $gentype = "GAB"; 1375 } 1376 else { 1377 $gentype = "Calendars"; 1378 } 1379 break; 1380 case SYNC_FOLDER_TYPE_CONTACT: 1381 case SYNC_FOLDER_TYPE_USER_CONTACT: 1382 $gentype = "Contacts"; 1383 break; 1384 case SYNC_FOLDER_TYPE_TASK: 1385 case SYNC_FOLDER_TYPE_USER_TASK: 1386 $gentype = "Tasks"; 1387 break; 1388 case SYNC_FOLDER_TYPE_NOTE: 1389 case SYNC_FOLDER_TYPE_USER_NOTE: 1390 $gentype = "Notes"; 1391 break; 1392 default: 1393 $gentype = "Emails"; 1394 break; 1395 } 1396 if (!isset($synchedFolderTypes[$gentype])) 1397 $synchedFolderTypes[$gentype] = 0; 1398 $synchedFolderTypes[$gentype]++; 1399 1400 // set the folder name for all folders which are not fully synchronized yet 1401 $fstatus = $device->GetFolderSyncStatus($folderid); 1402 if ($fstatus !== false && is_array($fstatus)) { 1403 $fstatus['name'] = $name ? $name : $gentype; 1404 $device->SetFolderSyncStatus($folderid, $fstatus); 1405 $syncedFoldersInProgress++; 1406 } 1407 } 1408 } 1409 $folderinfo = ""; 1410 foreach ($synchedFolderTypes as $gentype=>$count) { 1411 $folderinfo .= $gentype; 1412 if ($count>1) $folderinfo .= "($count)"; 1413 $folderinfo .= " "; 1414 } 1415 if (!$folderinfo) $folderinfo = "None available"; 1416 1417 // device information transmitted during Settings command 1418 if ($device->GetDeviceModel()) 1419 echo "Device Model:\t\t". $device->GetDeviceModel(). "\n"; 1420 if ($device->GetDeviceIMEI()) 1421 echo "Device IMEI:\t\t". $device->GetDeviceIMEI(). "\n"; 1422 if ($device->GetDeviceFriendlyName()) 1423 echo "Device friendly name:\t". $device->GetDeviceFriendlyName(). "\n"; 1424 if ($device->GetDeviceOS()) 1425 echo "Device OS:\t\t". $device->GetDeviceOS(). "\n"; 1426 if ($device->GetDeviceOSLanguage()) 1427 echo "Device OS Language:\t". $device->GetDeviceOSLanguage(). "\n"; 1428 if ($device->GetDevicePhoneNumber()) 1429 echo "Device Phone nr:\t". $device->GetDevicePhoneNumber(). "\n"; 1430 if ($device->GetDeviceMobileOperator()) 1431 echo "Device Operator:\t". $device->GetDeviceMobileOperator(). "\n"; 1432 if ($device->GetDeviceEnableOutboundSMS()) 1433 echo "Device Outbound SMS:\t". $device->GetDeviceEnableOutboundSMS(). "\n"; 1434 1435 echo "ActiveSync version:\t".($device->GetASVersion() ? $device->GetASVersion() : "unknown") ."\n"; 1436 echo "First sync:\t\t". strftime("%Y-%m-%d %H:%M", $device->GetFirstSyncTime()) ."\n"; 1437 echo "Last sync:\t\t". ($device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) : "never")."\n"; 1438 1439 1440 $filterType = (defined('SYNC_FILTERTIME_MAX') && SYNC_FILTERTIME_MAX > SYNC_FILTERTYPE_ALL) ? SYNC_FILTERTIME_MAX : SYNC_FILTERTYPE_ALL; 1441 $maxDevice = $device->GetSyncFilterType(); 1442 if ($maxDevice !== false && $maxDevice > SYNC_FILTERTYPE_ALL && ($filterType == SYNC_FILTERTYPE_ALL || $maxDevice < $filterType)) { 1443 $filterType = $maxDevice; 1444 } 1445 switch($filterType) { 1446 case SYNC_FILTERTYPE_1DAY: 1447 $filterTypeString = "1 day back"; 1448 break; 1449 case SYNC_FILTERTYPE_3DAYS: 1450 $filterTypeString = "3 days back"; 1451 break; 1452 case SYNC_FILTERTYPE_1WEEK: 1453 $filterTypeString = "1 week back"; 1454 break; 1455 case SYNC_FILTERTYPE_2WEEKS: 1456 $filterTypeString = "2 weeks back"; 1457 break; 1458 case SYNC_FILTERTYPE_1MONTH: 1459 $filterTypeString = "1 month back"; 1460 break; 1461 case SYNC_FILTERTYPE_3MONTHS: 1462 $filterTypeString = "3 months back"; 1463 break; 1464 case SYNC_FILTERTYPE_6MONTHS: 1465 $filterTypeString = "6 months back"; 1466 break; 1467 default: 1468 $filterTypeString = "unlimited"; 1469 } 1470 echo "Sync Period:\t\t". $filterTypeString . " (".$filterType.")\n"; 1471 echo "Total folders:\t\t". count($folders). "\n"; 1472 echo "Short folder Ids:\t". ($device->HasFolderIdMapping() ? "Yes":"No") ."\n"; 1473 echo "Synchronized folders:\t". $synchedFolders; 1474 if ($syncedFoldersInProgress > 0) 1475 echo " (". $syncedFoldersInProgress. " in progress)"; 1476 echo "\n"; 1477 echo "Synchronized data:\t$folderinfo\n"; 1478 if ($syncedFoldersInProgress > 0) { 1479 echo "Synchronization progress:\n"; 1480 foreach ($folders as $folderid) { 1481 $d = $device->GetFolderSyncStatus($folderid); 1482 if ($d) { 1483 $status = ""; 1484 if ($d['total'] > 0) { 1485 $percent = round($d['done']*100/$d['total']); 1486 $status = sprintf("Status: %s%d%% (%d/%d)", ($percent < 10)?" ":"", $percent, $d['done'], $d['total']); 1487 } 1488 if (strlen($d['name']) > 20) { 1489 $d['name'] = substr($d['name'], 0, 18) . ".."; 1490 } 1491 printf("\tFolder: %s Sync: %s %s\n", str_pad($d['name'], 20), str_pad($d['status'], 13), $status); 1492 } 1493 } 1494 } 1495 } 1496 // additional folders 1497 $addFolders = array(); 1498 $sharedFolders = $device->GetAdditionalFolders(); 1499 array_walk($sharedFolders, function (&$key) { $key["origin"] = 'Shared'; }); 1500 // $additionalFolders comes directly from the config 1501 array_walk($additionalFolders, function (&$key) { $key["origin"] = 'Configured'; }); 1502 foreach(array_merge($additionalFolders,$sharedFolders) as $df) { 1503 $df['additional'] = ''; 1504 $syncfolderid = $device->GetFolderIdForBackendId($df['folderid'], false, false, null); 1505 switch($df['type']) { 1506 case SYNC_FOLDER_TYPE_USER_APPOINTMENT: 1507 if ($df['name'] == KOE_GAB_NAME) { 1508 $gentype = "GAB"; 1509 } 1510 else { 1511 $gentype = "Calendar"; 1512 } 1513 break; 1514 case SYNC_FOLDER_TYPE_USER_CONTACT: 1515 $gentype = "Contact"; 1516 break; 1517 case SYNC_FOLDER_TYPE_USER_TASK: 1518 $gentype = "Task"; 1519 break; 1520 case SYNC_FOLDER_TYPE_USER_NOTE: 1521 $gentype = "Note"; 1522 break; 1523 default: 1524 $gentype = "Email"; 1525 break; 1526 } 1527 if ($device->GetFolderType($syncfolderid) == SYNC_FOLDER_TYPE_UNKNOWN) { 1528 $df['additional'] = "(KOE patching incomplete)"; 1529 } 1530 $df['type'] = $gentype; 1531 $df['synched'] = ($device->GetFolderUUID($syncfolderid)) ? 'Active' : 'Inactive (not yet synchronized or no permissions)'; 1532 $addFolders[] = $df; 1533 } 1534 $addFoldersTotal = !empty($addFolders) ? count($addFolders) : 'none'; 1535 echo "Additional Folders:\t$addFoldersTotal\n"; 1536 if ($addFoldersTotal != 'none') { 1537 if (!self::$shared) { 1538 print("\tFolder name Store Type Origin Synched\n"); 1539 } 1540 } 1541 foreach ($addFolders as $folder) { 1542 // Configured folders are always under root 1543 if (!isset($folder['parentid'])) $folder['parentid'] = '0'; 1544 1545 if (!self::$shared) { 1546 if (strlen($folder['store']) > 14) { 1547 $folder['store'] = substr($folder['store'], 0, 12) . ".."; 1548 } 1549 if (strlen($folder['name']) > 30) { 1550 $folder['name'] = substr($folder['name'], 0, 28) . ".."; 1551 } 1552 printf("\t%s %s %s %s %s %s\n", str_pad($folder['name'], 30), str_pad($folder['store'], 14), str_pad($folder['type'], 8), str_pad($folder['origin'], 10), $folder['synched'], $folder['additional']); 1553 } 1554 else { 1555 printf("\tFolder name:\t%s\n", $folder['name']); 1556 printf("\tStore:\t\t%s\n", $folder['store']); 1557 printf("\tType:\t\t%s\n", $folder['type']); 1558 printf("\tOrigin:\t\t%s\n", $folder['origin']); 1559 printf("\tFolder id:\t%s\n", $folder['folderid']); 1560 printf("\tParent id:\t%s\n", $folder['parentid']); 1561 printf("\tSynched:\t%s\n", $folder['synched']); 1562 if (!empty($folder['additional'])) printf("\tAdditional:\t%s\n", $folder['additional']); 1563 echo "\t------------------------\n"; 1564 } 1565 } 1566 1567 if (!self::$shared) { 1568 echo "Status:\t\t\t"; 1569 switch ($device->GetWipeStatus()) { 1570 case SYNC_PROVISION_RWSTATUS_OK: 1571 echo "OK\n"; 1572 break; 1573 case SYNC_PROVISION_RWSTATUS_PENDING: 1574 echo "Pending wipe\n"; 1575 break; 1576 case SYNC_PROVISION_RWSTATUS_REQUESTED: 1577 echo "Wipe requested on device\n"; 1578 break; 1579 case SYNC_PROVISION_RWSTATUS_WIPED: 1580 echo "Wiped\n"; 1581 break; 1582 default: 1583 echo "Not available\n"; 1584 break; 1585 } 1586 echo "WipeRequest on:\t\t". ($device->GetWipeRequestedOn() ? strftime("%Y-%m-%d %H:%M", $device->GetWipeRequestedOn()) : "not set")."\n"; 1587 echo "WipeRequest by:\t\t". ($device->GetWipeRequestedBy() ? $device->GetWipeRequestedBy() : "not set")."\n"; 1588 echo "Wiped on:\t\t". ($device->GetWipeActionOn() ? strftime("%Y-%m-%d %H:%M", $device->GetWipeActionOn()) : "not set")."\n"; 1589 echo "Policy name:\t\t". ($device->GetPolicyName() ? $device->GetPolicyName() : ASDevice::DEFAULTPOLICYNAME)."\n"; 1590 } 1591 1592 if ($device->GetKoeVersion()) { 1593 echo "Kopano Outlook Extension:\n"; 1594 echo "\tVersion:\t". $device->GetKoeVersion() ."\n"; 1595 echo "\tBuild:\t\t". $device->GetKoeBuild() ."\n"; 1596 echo "\tBuild Date:\t". strftime("%Y-%m-%d %H:%M", $device->GetKoeBuildDate()) ."\n"; 1597 echo "\tCapabilities:\t". (count($device->GetKoeCapabilities()) ? implode(',', $device->GetKoeCapabilities()) : 'unknown') ."\n"; 1598 echo "\tLast access:\t". ($device->GetKoeLastAccess() ? strftime("%Y-%m-%d", $device->GetKoeLastAccess()) : 'unknown') ."\n"; 1599 } 1600 1601 echo "Attention needed:\t"; 1602 1603 if ($device->GetDeviceError()) { 1604 echo $device->GetDeviceError() ."\n"; 1605 } 1606 // if KOE's access time is older than 7:01 h than the last successful sync it's probably inactive 1607 elseif ($device->GetKoeLastAccess() && $device->GetKoeLastAccess() + 25260 < $device->GetLastSyncTime()) { 1608 echo "KOE seems to be inactive on client\n"; 1609 } 1610 if (!isset($device->ignoredmessages) || empty($device->ignoredmessages)) { 1611 echo "No errors known\n"; 1612 } 1613 elseif (!self::$shared) { 1614 printf("%d messages need attention because they could not be synchronized\n", count($device->ignoredmessages)); 1615 foreach ($device->ignoredmessages as $im) { 1616 $info = ""; 1617 if (isset($im->asobject->subject)) 1618 $info .= sprintf("Subject: '%s'", $im->asobject->subject); 1619 if (isset($im->asobject->fileas)) 1620 $info .= sprintf("FileAs: '%s'", $im->asobject->fileas); 1621 if (isset($im->asobject->from)) 1622 $info .= sprintf(" - From: '%s'", $im->asobject->from); 1623 if (isset($im->asobject->starttime)) 1624 $info .= sprintf(" - On: '%s'", strftime("%Y-%m-%d %H:%M", $im->asobject->starttime)); 1625 $reason = $im->reasonstring; 1626 if ($im->reasoncode == 2) 1627 $reason = "Message was causing loop"; 1628 printf("\tBroken object:\t'%s' ignored on '%s'\n", $im->asclass, strftime("%Y-%m-%d %H:%M", $im->timestamp)); 1629 printf("\tInformation:\t%s\n", $info); 1630 printf("\tReason: \t%s (%s)\n", $reason, $im->reasoncode); 1631 printf("\tItem/Parent id: %s/%s\n", $im->id, $im->folderid); 1632 echo "\n"; 1633 } 1634 } 1635 else { 1636 print("There are some messages which need attention because they could not be synchronized. Run z-push-admin without -s or --shared.\n"); 1637 } 1638 1639 } 1640 1641 /** 1642 * Prints information about opened shares. 1643 * 1644 * @param array $shares 1645 * 1646 * @access private 1647 * @return void 1648 */ 1649 static private function printShares($shares) { 1650 $dashes = str_repeat('-', 145); 1651 foreach ($shares as $user => $userShares) { 1652 printf("Shares of user %s\n\n", $user); 1653 1654 printf("%s\n%-30s %-48s %-30s Device id\n%s", $dashes, "Foldername", "Folder id", "Username", $dashes); 1655 foreach ($userShares as $folderid => $folderShares) { 1656 foreach ($folderShares as $share) { 1657 if (strlen($share['name']) > 26) { 1658 $share['name'] = substr($share['name'], 0, 26) . '...'; 1659 } 1660 printf("\n%-30s %-48s %-30s %-10s", $share['name'], $folderid, $share['user'], $share['deviceId']); 1661 } 1662 } 1663 printf("\n%s\n\n", $dashes); 1664 } 1665 } 1666} 1667