1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file contains the command handler class. The following commands are supported: 27 * 28 * $set:debug {1,2,3} set debug level to 1, 2, or 3 29 * $set:debug t toggle debug timing on/off 30 * $set:debugtrace {on,off} turn offline debug trace on/off 31 * $set:instant_notify show whether instant notify is on or off 32 * $set:instant_notify {on,off} turn instant notify on/off 33 * $set:poll [N] set poll interval to N seconds (unless doing instant notify) 34 * $set:noop send a poll (get notifications) 35 * $set:rr refresh reminders 36 * $set:rh run reminder housekeeping 37 * $set:toast show sample toast messages 38 * $set:get version show client version 39 * $set:expire expire session; refresh block will come on next response 40 * $set:refresh force immediate return of a refresh block 41 * $set:relogin logout the user 42 * $set:alert issue a test sound alert 43 * $set:alert {browser,desktop,app} N issue a test alert in given context in N seconds 44 * $set:leak {begin,report,end} manage leak detection 45 * $set:tabs show tab groups (in debug window) 46 * $set:ymid [id] set Yahoo IM user to id 47 * $set:log [type] dump log contents for type 48 * $set:log [type] [size] set number of msgs to keep for type 49 * $set:compose compose msg based on mailto: in query string 50 * $set:error show error dialog 51 * $set:modify [setting] [value] set setting to value, then optionally restart 52 * $set:clearAutocompleteCache clear contacts autocomplete cache 53 * 54 * TODO: should probably I18N the alert messages 55 */ 56 57 /** 58 * Creates the client. command handler 59 * @class 60 * This class represents a client command handler. 61 */ 62 ZmClientCmdHandler = function() { 63 this._settings = {}; 64 this._dbg = window.DBG; // trick to fool minimizer regex 65 }; 66 67 /** 68 * Executes the command. 69 * 70 * @param {String} cmdStr the command 71 * @param {ZmSearchController} searchController the search controller 72 * @param {Object} [cmdArg1] command arguments 73 */ 74 ZmClientCmdHandler.prototype.execute = 75 function(cmdStr, searchController) { 76 77 if (!cmdStr) { return; } 78 79 cmdStr = AjxStringUtil.trim(cmdStr, true); 80 81 if (cmdStr == "") { return; } 82 83 var argv = cmdStr.split(/\s/); 84 var arg0 = argv[0]; 85 86 if (arg0 != "modify") { 87 cmdStr = cmdStr.toLowerCase(); 88 } 89 90 var func = this["execute_" + arg0]; 91 if (func) { 92 var args = [].concat(cmdStr, searchController, argv); 93 return func.apply(this, args); 94 } 95 }; 96 97 /** 98 * @private 99 */ 100 ZmClientCmdHandler.prototype._alert = 101 function(msg, level) { 102 appCtxt.setStatusMsg(msg, level); 103 }; 104 105 /** 106 * Executes the command with debug. 107 * 108 * @param {String} cmdStr the command 109 * @param {ZmSearchController} searchController the search controller 110 * @param {Object} [cmdArg1] command arguments 111 */ 112 ZmClientCmdHandler.prototype.execute_debug = 113 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 114 if (!cmdArg1 || !this._dbg) { return; } 115 if (cmdArg1 == "t") { 116 var on = this._dbg._showTiming; 117 var newState = on ? "off" : "on"; 118 this._alert("Turning timing info " + newState); 119 this._dbg.showTiming(!on); 120 } else { 121 this._dbg.setDebugLevel(cmdArg1); 122 this._alert("Setting debug level to: " + cmdArg1); 123 } 124 }; 125 126 /** 127 * Executes the command with debug trace. 128 * 129 * @param {String} cmdStr the command 130 * @param {ZmSearchController} searchController the search controller 131 * @param {Object} [cmdArg1] command arguments 132 */ 133 ZmClientCmdHandler.prototype.execute_debugtrace = 134 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 135 if (!cmdArg1) return; 136 137 var val; 138 if (cmdArg1 == "on") { 139 val = true; 140 } else if (cmdArg1 == "off") { 141 val = false; 142 } 143 144 if (val != undefined) { 145 appCtxt.set(ZmSetting.OFFLINE_DEBUG_TRACE, val, null, null, true); 146 this._alert("Debug trace is " + cmdArg1); 147 } 148 }; 149 150 /** 151 * Executes the instant notify "ON" command. 152 * 153 * @param {String} cmdStr the command 154 * @param {ZmSearchController} searchController the search controller 155 * @param {Object} [cmdArg1] command arguments 156 */ 157 ZmClientCmdHandler.prototype.execute_instant_notify = 158 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 159 if (typeof cmdArg1 == "undefined") { 160 this._alert("Instant notify is "+ (appCtxt.getAppController().getInstantNotify() ? "ON" : "OFF")); 161 } else { 162 var on = cmdArg1 && (cmdArg1.toLowerCase() == "on"); 163 this._alert("Set instant notify to "+ (on ? "ON" : "OFF")); 164 appCtxt.getAppController().setInstantNotify(on); 165 } 166 }; 167 168 /** 169 * Executes the poll interval command. 170 * 171 * @param {String} cmdStr the command 172 * @param {ZmSearchController} searchController the search controller 173 * @param {Object} [cmdArg1] command arguments 174 */ 175 ZmClientCmdHandler.prototype.execute_poll = 176 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 177 if (!cmdArg1) return; 178 appCtxt.set(ZmSetting.POLLING_INTERVAL, cmdArg1); 179 var pi = appCtxt.get(ZmSetting.POLLING_INTERVAL); // LDAP time format converted to seconds 180 if (appCtxt.getAppController().setPollInterval(true)) { 181 this._alert("Set polling interval to " + pi + " seconds"); 182 } else { 183 this._alert("Ignoring polling interval b/c we are in Instant_Polling mode ($set:instant_notify 0|1)"); 184 } 185 }; 186 187 /** 188 * Executes the no op command. 189 * 190 * @param {String} cmdStr the command 191 * @param {ZmSearchController} searchController the search controller 192 * @param {Object} [cmdArg1] command arguments 193 */ 194 ZmClientCmdHandler.prototype.execute_noop = 195 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 196 appCtxt.getAppController().sendNoOp(); 197 this._alert("Sent NoOpRequest"); 198 }; 199 200 /** 201 * Executes the reminder refresh command. 202 * 203 * @param {String} cmdStr the command 204 * @param {ZmSearchController} searchController the search controller 205 * @param {Object} [cmdArg1] command arguments 206 */ 207 ZmClientCmdHandler.prototype.execute_rr = 208 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 209 appCtxt.getApp(ZmApp.CALENDAR).getReminderController().refresh(); 210 }; 211 212 /** 213 * Executes the reminder housekeeping command. 214 * 215 * @param {String} cmdStr the command 216 * @param {ZmSearchController} searchController the search controller 217 * @param {Object} [cmdArg1] command arguments 218 */ 219 ZmClientCmdHandler.prototype.execute_rh = 220 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 221 appCtxt.getApp(ZmApp.CALENDAR).getReminderController()._housekeepingAction(); 222 }; 223 224 /** 225 * Executes the toast command. 226 * 227 * @param {String} cmdStr the command 228 * @param {ZmSearchController} searchController the search controller 229 * @param {Object} [cmdArg1] command arguments 230 */ 231 ZmClientCmdHandler.prototype.execute_toast = 232 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 233 appCtxt.setStatusMsg("Your options have been saved.", ZmStatusView.LEVEL_INFO); 234 appCtxt.setStatusMsg("Unable to save options.", ZmStatusView.LEVEL_WARNING); 235 appCtxt.setStatusMsg("Message not sent.", ZmStatusView.LEVEL_CRITICAL); 236 }; 237 238 /** 239 * Executes the get version/release/date command. 240 * 241 * @param {String} cmdStr the command 242 * @param {ZmSearchController} searchController the search controller 243 * @param {Object} [cmdArg1] command arguments 244 */ 245 ZmClientCmdHandler.prototype.execute_get = 246 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 247 if (cmdArg1 && cmdArg1 == "version") { 248 alert("Client Information\n\n" + 249 "Client Version: " + appCtxt.get(ZmSetting.CLIENT_VERSION) + "\n" + 250 "Client Release: " + appCtxt.get(ZmSetting.CLIENT_RELEASE) + "\n" + 251 " Build Date: " + appCtxt.get(ZmSetting.CLIENT_DATETIME)); 252 } 253 }; 254 255 /** 256 * Executes the expire command. 257 * 258 * @param {String} cmdStr the command 259 * @param {ZmSearchController} searchController the search controller 260 * @param {Object} [cmdArg1] command arguments 261 */ 262 ZmClientCmdHandler.prototype.execute_expire = 263 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 264 ZmCsfeCommand.clearSessionId(); 265 this._alert("Session expired"); 266 }; 267 268 /** 269 * Executes the refresh command. 270 * 271 * @param {String} cmdStr the command 272 * @param {ZmSearchController} searchController the search controller 273 * @param {Object} [cmdArg1] command arguments 274 */ 275 ZmClientCmdHandler.prototype.execute_refresh = 276 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 277 ZmCsfeCommand.clearSessionId(); 278 appCtxt.getAppController().sendNoOp(); 279 }; 280 281 /** 282 * Executes the re-login command. 283 * 284 * @param {String} cmdStr the command 285 * @param {ZmSearchController} searchController the search controller 286 * @param {Object} [cmdArg1] command arguments 287 * @param {Object} [cmdArg2] command arguments 288 */ 289 ZmClientCmdHandler.prototype.execute_relogin = 290 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 291 ZmCsfeCommand.clearAuthToken(); 292 appCtxt.getAppController().sendNoOp(); 293 }; 294 295 /** 296 * Executes the alert command. 297 * 298 * @param {String} cmdStr the command 299 * @param {ZmSearchController} searchController the search controller 300 * @param {Object} [cmdArg1] command arguments 301 * @param {Object} [cmdArg2] command arguments 302 */ 303 ZmClientCmdHandler.prototype.execute_alert = 304 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 305 // $set:alert [sound/browser/app] [delay in seconds] 306 function doIt() { 307 if (cmdArg1 == "browser") { 308 AjxDispatcher.require("Alert"); 309 ZmBrowserAlert.getInstance().start("Alert Test!"); 310 } else if (cmdArg1 == "desktop") { 311 AjxDispatcher.require("Alert"); 312 ZmDesktopAlert.getInstance().start("Title!", "Message!"); 313 } else if (cmdArg1 == "app") { 314 appCtxt.getApp(ZmApp.MAIL).startAlert(); 315 } else { 316 AjxDispatcher.require("Alert"); 317 ZmSoundAlert.getInstance().start(); 318 } 319 } 320 setTimeout(doIt, Number(cmdArg2) * 1000); 321 }; 322 323 /** 324 * Executes the leak detector command. 325 * 326 * @param {String} cmdStr the command 327 * @param {ZmSearchController} searchController the search controller 328 * @param {Object} [cmdArg1] command arguments 329 */ 330 ZmClientCmdHandler.prototype.execute_leak = 331 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 332 AjxPackage.require("ajax.debug.AjxLeakDetector"); 333 if (!window.AjxLeakDetector) { 334 this._alert("AjxLeakDetector could not be loaded", ZmStatusView.LEVEL_WARNING); 335 } else { 336 var leakResult = AjxLeakDetector.execute(cmdArg1); 337 this._alert(leakResult.message, leakResult.success ? ZmStatusView.LEVEL_INFO : ZmStatusView.LEVEL_WARNING); 338 } 339 }; 340 341 /** 342 * Executes the tabs command. 343 * 344 * @param {String} cmdStr the command 345 * @param {ZmSearchController} searchController the search controller 346 * @param {Object} [cmdArg1] command arguments 347 */ 348 ZmClientCmdHandler.prototype.execute_tabs = 349 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 350 appCtxt.getRootTabGroup().dump(AjxDebug.DBG1); 351 }; 352 353 /** 354 * Executes the Yahoo! IM command. 355 * 356 * @param {String} cmdStr the command 357 * @param {ZmSearchController} searchController the search controller 358 * @param {Object} [cmdArg1] command arguments 359 */ 360 ZmClientCmdHandler.prototype.execute_ymid = 361 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 362 var settings = appCtxt.getSettings(), 363 setting = settings.getSetting(ZmSetting.IM_YAHOO_ID); 364 setting.setValue(cmdArg1 || ""); 365 settings.save([setting]); 366 this._alert("Done"); 367 }; 368 369 /** 370 * Executes the log command. 371 * 372 * @param {String} cmdStr the command 373 * @param {ZmSearchController} searchController the search controller 374 * @param {Object} [cmdArg1] command arguments 375 */ 376 ZmClientCmdHandler.prototype.execute_log = 377 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 378 379 var type = cmdArg1; 380 if (cmdArg2 != null) { 381 var size = parseInt(cmdArg2); 382 if (!isNaN(size)) { 383 AjxDebug.BUFFER_MAX[type] = size; 384 this._alert("Debug log size for '" + type + "' set to " + size); 385 } 386 } 387 else { 388 appCtxt.getDebugLogDialog().popup(AjxDebug.getDebugLog(type), type); 389 } 390 }; 391 392 /** 393 * Executes the compose command. 394 * 395 * @param {String} cmdStr the command 396 * @param {ZmSearchController} searchController the search controller 397 * @param {Object} [cmdArg1] command arguments 398 */ 399 ZmClientCmdHandler.prototype.execute_compose = 400 function(cmdStr, searchController, cmdName, cmdArg1 /* ..., cmdArgN */) { 401 var mailApp = appCtxt.getApp(ZmApp.MAIL); 402 var idx = (location.search.indexOf("mailto")); 403 if (idx >= 0) { 404 var query = "to=" + decodeURIComponent(location.search.substring(idx+7)); 405 query = query.replace(/\?/g, "&"); 406 407 mailApp._showComposeView(null, query); 408 return true; 409 } 410 }; 411 412 /** 413 * Executes the error command. 414 * 415 * @param {String} cmdStr the command 416 * @param {ZmSearchController} searchController the search controller 417 * @param {Object} [cmdArg1] command arguments 418 * @param {Object} [cmdArg2] command arguments 419 */ 420 ZmClientCmdHandler.prototype.execute_error = 421 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 422 var errDialog = appCtxt.getErrorDialog(); 423 errDialog.setMessage("Error Message!", "Details!!", DwtMessageDialog.WARNING_STYLE); 424 errDialog.popup(); 425 426 }; 427 428 /** 429 * Executes the modify command. 430 * 431 * @param {String} cmdStr the command 432 * @param {ZmSearchController} searchController the search controller 433 * @param {Object} [cmdArg1] command arguments 434 * @param {Object} [cmdArg2] command arguments 435 */ 436 ZmClientCmdHandler.prototype.execute_modify = 437 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 438 var settings = appCtxt.getSettings(); 439 var id = cmdArg1 && settings.getSettingByName(cmdArg1); 440 if (id) { 441 var setting = settings.getSetting(id); 442 setting.setValue(cmdArg2 || setting.getDefaultValue()); 443 if (ZmSetting.IS_IMPLICIT[setting.id]) { 444 appCtxt.accountList.saveImplicitPrefs(); 445 } else { 446 settings.save([setting]); 447 } 448 } 449 450 settings._showConfirmDialog(ZmMsg.accountChangeRestart, settings._refreshBrowserCallback.bind(settings)); 451 }; 452 453 /** 454 * Executes the clearAutocompleteCache command. 455 * 456 * @param {String} cmdStr the command 457 * @param {ZmSearchController} searchController the search controller 458 * @param {Object} [cmdArg1] command arguments 459 * @param {Object} [cmdArg2] command arguments 460 */ 461 ZmClientCmdHandler.prototype.execute_clearAutocompleteCache = 462 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 463 appCtxt.clearAutocompleteCache(ZmAutocomplete.AC_TYPE_CONTACT); 464 this._alert("Contacts autocomplete cache cleared"); 465 }; 466 467 /** 468 * Executes the getCharWidth command. 469 * 470 * @param {String} cmdStr the command 471 * @param {ZmSearchController} searchController the search controller 472 * @param {Object} [cmdArg1] command arguments 473 * @param {Object} [cmdArg2] command arguments 474 */ 475 ZmClientCmdHandler.prototype.execute_getCharWidth = 476 function(cmdStr, searchController, cmdName, cmdArg1, cmdArg2 /* ..., cmdArgN */) { 477 var cla = appCtxt.getApp(ZmApp.CONTACTS).getContactList().getArray(); 478 for (var i = 0; i < cla.length; i++) { 479 ZmClientCmdHandler._testWidth(cla[i]._attrs["firstLast"] || cla[i]._attrs["company"] || ""); 480 } 481 var text = []; 482 text.push("Avg char width: " + ZmClientCmdHandler.WIDTH / ZmClientCmdHandler.CHARS); 483 text.push("Avg bold char width: " + ZmClientCmdHandler.BWIDTH / ZmClientCmdHandler.CHARS); 484 var w = ZmClientCmdHandler._testWidth(AjxStringUtil.ELLIPSIS); 485 text.push("Ellipsis width: " + w.w); 486 w = ZmClientCmdHandler._testWidth(", "); 487 text.push("Comma + space width: " + w.w); 488 alert(text.join("\n")); 489 }; 490 491 ZmClientCmdHandler.CHARS = 0; 492 ZmClientCmdHandler.WIDTH = 0; 493 ZmClientCmdHandler.BWIDTH = 0; 494 495 ZmClientCmdHandler._testWidth = 496 function(str) { 497 var div = document.createElement("DIV"); 498 div.style.position = Dwt.ABSOLUTE_STYLE; 499 var shellEl = appCtxt.getShell().getHtmlElement(); 500 shellEl.appendChild(div); 501 Dwt.setLocation(div, Dwt.LOC_NOWHERE, Dwt.LOC_NOWHERE); 502 div.innerHTML = str; 503 var size = Dwt.getSize(div); 504 ZmClientCmdHandler.CHARS += str.length; 505 ZmClientCmdHandler.WIDTH += size.x; 506 div.style.fontWeight = "bold"; 507 var bsize = Dwt.getSize(div); 508 ZmClientCmdHandler.BWIDTH += bsize.x; 509 shellEl.removeChild(div); 510 return {w:size.x, bw:bsize.x}; 511 }; 512