Java tutorial
/* * ***** BEGIN LICENSE BLOCK ***** * Zimbra Collaboration Suite Server * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, * version 2 of the License. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with this program. * If not, see <https://www.gnu.org/licenses/>. * ***** END LICENSE BLOCK ***** */ package com.zimbra.cs.account; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import net.spy.memcached.DefaultHashAlgorithm; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.cli.PosixParser; import org.apache.commons.codec.binary.Base64; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.methods.PostMethod; import com.google.common.base.Charsets; import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import com.google.common.collect.Sets; import com.zimbra.common.account.Key; import com.zimbra.common.account.Key.AccountBy; import com.zimbra.common.account.ZAttrProvisioning.AccountStatus; import com.zimbra.common.auth.ZAuthToken; import com.zimbra.common.localconfig.LC; import com.zimbra.common.net.SocketFactories; import com.zimbra.common.service.ServiceException; import com.zimbra.common.soap.AdminConstants; import com.zimbra.common.soap.BackupConstants; import com.zimbra.common.soap.Element; import com.zimbra.common.soap.SoapHttpTransport; import com.zimbra.common.soap.SoapHttpTransport.HttpDebugListener; import com.zimbra.common.soap.SoapTransport; import com.zimbra.common.util.AccountLogger; import com.zimbra.common.util.ByteUtil; import com.zimbra.common.util.CliUtil; import com.zimbra.common.util.DateUtil; import com.zimbra.common.util.FileUtil; import com.zimbra.common.util.Pair; import com.zimbra.common.util.SetUtil; import com.zimbra.common.util.StringUtil; import com.zimbra.common.util.Version; import com.zimbra.common.util.ZimbraLog; import com.zimbra.common.zclient.ZClientException; import com.zimbra.cs.account.Provisioning.CacheEntry; import com.zimbra.cs.account.Provisioning.CountAccountResult; import com.zimbra.cs.account.Provisioning.MailMode; import com.zimbra.cs.account.Provisioning.PublishedShareInfoVisitor; import com.zimbra.cs.account.Provisioning.RightsDoc; import com.zimbra.cs.account.Provisioning.SearchGalResult; import com.zimbra.cs.account.Provisioning.SetPasswordResult; import com.zimbra.cs.account.SearchAccountsOptions.IncludeType; import com.zimbra.cs.account.SearchDirectoryOptions.MakeObjectOpt; import com.zimbra.cs.account.SearchDirectoryOptions.ObjectType; import com.zimbra.cs.account.SearchDirectoryOptions.SortOpt; import com.zimbra.cs.account.accesscontrol.AdminRight; import com.zimbra.cs.account.accesscontrol.AttrRight; import com.zimbra.cs.account.accesscontrol.ComboRight; import com.zimbra.cs.account.accesscontrol.GranteeType; import com.zimbra.cs.account.accesscontrol.Help; import com.zimbra.cs.account.accesscontrol.Right; import com.zimbra.cs.account.accesscontrol.Right.RightType; import com.zimbra.cs.account.accesscontrol.RightClass; import com.zimbra.cs.account.accesscontrol.RightCommand; import com.zimbra.cs.account.accesscontrol.RightManager; import com.zimbra.cs.account.accesscontrol.RightModifier; import com.zimbra.cs.account.accesscontrol.TargetType; import com.zimbra.cs.account.ldap.LdapEntrySearchFilter; import com.zimbra.cs.account.ldap.LdapProv; import com.zimbra.cs.account.soap.SoapProvisioning; import com.zimbra.cs.account.soap.SoapProvisioning.IndexStatsInfo; import com.zimbra.cs.account.soap.SoapProvisioning.MailboxInfo; import com.zimbra.cs.account.soap.SoapProvisioning.MemcachedClientConfig; import com.zimbra.cs.account.soap.SoapProvisioning.QuotaUsage; import com.zimbra.cs.account.soap.SoapProvisioning.ReIndexBy; import com.zimbra.cs.account.soap.SoapProvisioning.ReIndexInfo; import com.zimbra.cs.extension.ExtensionDispatcherServlet; import com.zimbra.cs.fb.FbCli; import com.zimbra.cs.httpclient.URLUtil; import com.zimbra.cs.ldap.LdapClient; import com.zimbra.cs.ldap.ZLdapFilterFactory; import com.zimbra.cs.ldap.ZLdapFilterFactory.FilterId; import com.zimbra.cs.util.BuildInfo; import com.zimbra.cs.util.SoapCLI; import com.zimbra.cs.zclient.ZMailboxUtil; import com.zimbra.soap.JaxbUtil; import com.zimbra.soap.admin.message.LockoutMailboxRequest; import com.zimbra.soap.admin.message.UnregisterMailboxMoveOutRequest; import com.zimbra.soap.admin.type.CacheEntryType; import com.zimbra.soap.admin.type.CountObjectsType; import com.zimbra.soap.admin.type.DataSourceType; import com.zimbra.soap.admin.type.GranteeSelector.GranteeBy; import com.zimbra.soap.admin.type.MailboxMoveSpec; import com.zimbra.soap.type.AccountNameSelector; import com.zimbra.soap.type.GalSearchType; import com.zimbra.soap.type.TargetBy; /** * @author schemers */ public class ProvUtil implements HttpDebugListener { private static final String ERR_VIA_SOAP_ONLY = "can only be used with SOAP"; private static final String ERR_VIA_LDAP_ONLY = "can only be used with \"zmprov -l/--ldap\""; private static final String ERR_INVALID_ARG_EV = "arg -e is invalid unless -v is also specified"; private static final PrintStream console = System.out; private static final PrintStream errConsole = System.err; enum SoapDebugLevel { none, // no SOAP debug normal, // SOAP request and response payload high; // SOAP payload and http transport header } private boolean batchMode = false; private boolean interactiveMode = false; private boolean verboseMode = false; private SoapDebugLevel debugLevel = SoapDebugLevel.none; private boolean useLdap = LC.zimbra_zmprov_default_to_ldap.booleanValue(); private boolean useLdapMaster = false; private String account = null; private String password = null; private ZAuthToken authToken = null; private String serverHostname = LC.zimbra_zmprov_default_soap_server.value(); private int serverPort = LC.zimbra_admin_service_port.intValue(); private Command command; private Map<String, Command> commandIndex; private Provisioning prov; private BufferedReader cliReader; private boolean outputBinaryToFile; private boolean allowMultiValuedAttrReplacement; private long sendStart; private boolean forceDisplayAttrValue; private boolean errorOccursDuringInteraction = false; // bug 58554 public void setDebug(SoapDebugLevel value) { debugLevel = value; } public void setVerbose(boolean value) { verboseMode = value; } public void setUseLdap(boolean ldap, boolean master) { useLdap = ldap; useLdapMaster = master; } public void setAccount(String value) { account = value; useLdap = false; } public void setPassword(String value) { password = value; useLdap = false; } public void setAuthToken(ZAuthToken value) { authToken = value; useLdap = false; } private void setOutputBinaryToFile(boolean value) { outputBinaryToFile = value; } private void setBatchMode(boolean value) { batchMode = value; } private void setAllowMultiValuedAttrReplacement(boolean value) { allowMultiValuedAttrReplacement = value; } private boolean outputBinaryToFile() { return outputBinaryToFile; } private void setForceDisplayAttrValue(boolean value) { this.forceDisplayAttrValue = value; } public void setServer(String value) { int i = value.indexOf(":"); if (i == -1) { serverHostname = value; } else { serverHostname = value.substring(0, i); serverPort = Integer.parseInt(value.substring(i + 1)); } useLdap = false; } public boolean useLdap() { return useLdap; } private void deprecated() { console.println("This command has been deprecated."); System.exit(1); } private void usage() { usage(null); } private void usage(Command.Via violatedVia) { boolean givenHelp = false; if (command != null) { if (violatedVia == null) { console.printf("usage: %s(%s) %s\n", command.getName(), command.getAlias(), command.getHelp()); givenHelp = true; CommandHelp extraHelp = command.getExtraHelp(); if (extraHelp != null) { extraHelp.printHelp(); } } else { if (violatedVia == Command.Via.ldap) { console.printf("%s %s\n", command.getName(), ERR_VIA_LDAP_ONLY); } else { console.printf("%s %s\n", command.getName(), ERR_VIA_SOAP_ONLY); } } } if (interactiveMode) { return; } if (givenHelp) { console.println("For general help, type : zmprov --help"); System.exit(1); } console.println(""); console.println("zmprov [args] [cmd] [cmd-args ...]"); console.println(""); console.println(" -h/--help display usage"); console.println(" -f/--file use file as input stream"); console.println(" -s/--server {host}[:{port}] server hostname and optional port"); console.println(" -l/--ldap provision via LDAP instead of SOAP"); console.println(" -L/--logpropertyfile log4j property file, valid only with -l"); console.println(" -a/--account {name} account name to auth as"); console.println(" -p/--password {pass} password for account"); console.println(" -P/--passfile {file} read password from file"); console.println( " -z/--zadmin use zimbra admin name/password from localconfig for admin/password"); console.println(" -y/--authtoken {authtoken} " + SoapCLI.OPT_AUTHTOKEN.getDescription()); console.println(" -Y/--authtokenfile {authtoken file} " + SoapCLI.OPT_AUTHTOKENFILE.getDescription()); console.println(" -v/--verbose verbose mode (dumps full exception stack trace)"); console.println(" -d/--debug debug mode (dumps SOAP messages)"); console.println(" -m/--master use LDAP master (only valid with -l)"); console.println( " -r/--replace allow replacement of safe-guarded multi-valued attributes configured in localconfig key \"zmprov_safeguarded_attrs\""); console.println(""); doHelp(null); System.exit(1); } public static enum Category { ACCOUNT("help on account-related commands"), CALENDAR( "help on calendar resource-related commands"), COMMANDS("help on all commands"), CONFIG( "help on config-related commands"), COS("help on COS-related commands"), DOMAIN( "help on domain-related commands"), FREEBUSY( "help on free/busy-related commands"), LIST( "help on distribution list-related commands"), LOG( "help on logging commands"), MISC( "help on misc commands"), MAILBOX( "help on mailbox-related commands"), REVERSEPROXY( "help on reverse proxy related commands"), RIGHT( "help on right-related commands"), SEARCH( "help on search-related commands"), SERVER( "help on server-related commands"), ALWAYSONCLUSTER( "help on alwaysOnCluster-related commands"), UCSERVICE( "help on ucservice-related commands"), SHARE( "help on share related commands"); private final String description; public String getDescription() { return description; } Category(String desc) { description = desc; } static void help(Category cat) { switch (cat) { case CALENDAR: helpCALENDAR(); break; case RIGHT: helpRIGHT(); break; case LOG: helpLOG(); break; } } static void helpCALENDAR() { console.println(""); StringBuilder sb = new StringBuilder(); EntrySearchFilter.Operator vals[] = EntrySearchFilter.Operator.values(); for (int i = 0; i < vals.length; i++) { if (i > 0) { sb.append(", "); } sb.append(vals[i].toString()); } console.println(" op = " + sb.toString()); } static void helpRIGHT() { helpRIGHTCommon(true); helpRIGHTRights(false, true); } static void helpRIGHTCommand(boolean printRights, boolean secretPossible, boolean modifierPossible) { helpRIGHTCommon(secretPossible); helpRIGHTRights(false, modifierPossible); } static void helpRIGHTRights(boolean printRights, boolean modifierPossible) { // rights console.println(); if (modifierPossible) { console.println(" {right}: can have the following prefixes:"); for (RightModifier rm : RightModifier.values()) { console.println(" " + rm.getModifier() + " : " + rm.getDescription()); } console.println(); } if (printRights) { try { Map<String, AdminRight> allAdminRights = RightManager.getInstance().getAllAdminRights(); // print non-combo rights first for (com.zimbra.cs.account.accesscontrol.Right r : allAdminRights.values()) { if (RightType.combo != r.getRightType()) { console.println(" " + r.getName() + " (" + r.getRightType().toString() + ")"); } } // then combo rights for (com.zimbra.cs.account.accesscontrol.Right r : allAdminRights.values()) { if (RightType.combo == r.getRightType()) { console.println(" " + r.getName() + " (" + r.getRightType().toString() + ")"); } } } catch (ServiceException e) { console.println("cannot get RightManager instance: " + e.getMessage()); } } else { console.println(" for complete list of rights, do \"zmprov gar -c ALL\""); } console.println(); } static void helpRIGHTCommon(boolean secretPossible) { // target types console.println(); StringBuilder tt = new StringBuilder(); StringBuilder ttNeedsTargetIdentity = new StringBuilder(); StringBuilder ttNoTargetId = new StringBuilder(); TargetType[] tts = TargetType.values(); for (int i = 0; i < tts.length; i++) { if (i > 0) { tt.append(", "); } tt.append(tts[i].getCode()); if (tts[i].needsTargetIdentity()) { ttNeedsTargetIdentity.append(tts[i].getCode() + " "); } else { ttNoTargetId.append(tts[i].getCode() + " "); } } console.println(" {target-type} = " + tt.toString()); console.println(); console.println(" {target-id|target-name} is required if target-type is: " + ttNeedsTargetIdentity); console.println( " {target-id|target-name} should not be specified if target-type is: " + ttNoTargetId); // grantee types console.println(); StringBuilder gt = new StringBuilder(); StringBuilder gtNeedsGranteeIdentity = new StringBuilder(); StringBuilder gtNoGranteeId = new StringBuilder(); StringBuilder gtNeedsSecret = new StringBuilder(); StringBuilder gtNoSecret = new StringBuilder(); GranteeType[] gts = GranteeType.values(); for (int i = 0; i < gts.length; i++) { if (i > 0) { gt.append(", "); } gt.append(gts[i].getCode()); if (gts[i].needsGranteeIdentity()) { gtNeedsGranteeIdentity.append(gts[i].getCode() + " "); } else { gtNoGranteeId.append(gts[i].getCode() + " "); } if (secretPossible) { if (gts[i].allowSecret()) { gtNeedsSecret.append(gts[i].getCode() + " "); } else { gtNoSecret.append(gts[i].getCode() + " "); } } } console.println(" {grantee-type} = " + gt.toString()); console.println(); console.println(" {grantee-id|grantee-name} is required if grantee-type is one of: " + gtNeedsGranteeIdentity); console.println(" {grantee-id|grantee-name} should not be specified if grantee-type is one of: " + gtNoGranteeId); if (secretPossible) { console.println(); console.println(" {secret} is required if grantee-type is one of: " + gtNeedsSecret); console.println(" {secret} should not be specified if grantee-type is one of: " + gtNoSecret); } } static void helpLOG() { console.println(" Log categories:"); int maxNameLength = 0; for (String name : ZimbraLog.CATEGORY_DESCRIPTIONS.keySet()) { if (name.length() > maxNameLength) { maxNameLength = name.length(); } } for (String name : ZimbraLog.CATEGORY_DESCRIPTIONS.keySet()) { console.print(" " + name); for (int i = 0; i < (maxNameLength - name.length()); i++) { console.print(" "); } console.format(" - %s\n", ZimbraLog.CATEGORY_DESCRIPTIONS.get(name)); } } } // TODO: refactor to own class interface CommandHelp { public void printHelp(); } static class RightCommandHelp implements CommandHelp { boolean printRights; boolean secretPossible; boolean modifierPossible; RightCommandHelp(boolean printRights, boolean secretPossible, boolean modifierPossible) { this.printRights = printRights; this.secretPossible = secretPossible; this.modifierPossible = modifierPossible; } @Override public void printHelp() { Category.helpRIGHTCommand(printRights, secretPossible, modifierPossible); } } static class ReindexCommandHelp implements CommandHelp { @Override public void printHelp() { /* * copied from soap-admin.txt Not exactly match all types in MailboxIndex TODO: cleanup */ console.println(); console.println("Valid types:"); console.println(" appointment"); // console.println(" briefcase"); // console.println(" chat"); console.println(" contact"); console.println(" conversation"); console.println(" document"); console.println(" message"); console.println(" note"); // console.println(" tag"); console.println(" task"); console.println(); } } public enum Command { ADD_ACCOUNT_ALIAS("addAccountAlias", "aaa", "{name@domain|id} {alias@domain}", Category.ACCOUNT, 2, 2), ADD_ACCOUNT_LOGGER("addAccountLogger", "aal", "[-s/--server hostname] {name@domain|id} {logging-category} {trace|debug|info|warn|error}", Category.LOG, 3, 5), ADD_DISTRIBUTION_LIST_ALIAS("addDistributionListAlias", "adla", "{list@domain|id} {alias@domain}", Category.LIST, 2, 2), ADD_DISTRIBUTION_LIST_MEMBER("addDistributionListMember", "adlm", "{list@domain|id} {member@domain}+", Category.LIST, 2, Integer.MAX_VALUE), AUTO_COMPLETE_GAL("autoCompleteGal", "acg", "{domain} {name}", Category.SEARCH, 2, 2), AUTO_PROV_CONTROL("autoProvControl", "apc", "{start|status|stop}", Category.COMMANDS, 1, 1), CHECK_PASSWORD_STRENGTH("checkPasswordStrength", "cps", "{name@domain|id} {password}", Category.ACCOUNT, 2, 2), CHECK_RIGHT("checkRight", "ckr", "{target-type} [{target-id|target-name}] {grantee-id|grantee-name (note:can only check internal user)} {right}", Category.RIGHT, 3, 4, null, new RightCommandHelp(false, false, true)), COPY_COS("copyCos", "cpc", "{src-cos-name|id} {dest-cos-name}", Category.COS, 2, 2), COUNT_ACCOUNT( "countAccount", "cta", "{domain|id}", Category.DOMAIN, 1, 1), COUNT_OBJECTS( "countObjects", "cto", "{" + CountObjectsType .names("|") + "} [-d {domain|id}] [-u {UCService|id}]", Category.MISC, 1, 4), CREATE_ACCOUNT( "createAccount", "ca", "{name@domain} {password} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 2, Integer.MAX_VALUE), CREATE_ALIAS_DOMAIN( "createAliasDomain", "cad", "{alias-domain-name} {local-domain-name|id} [attr1 value1 [attr2 value2...]]", Category.DOMAIN, 2, Integer.MAX_VALUE), CREATE_ALWAYSONCLUSTER( "createAlwaysOnCluster", "caoc", "{name} [attr1 value1 [attr2 value2...]]", Category.ALWAYSONCLUSTER, 1, Integer.MAX_VALUE), CREATE_BULK_ACCOUNTS( "createBulkAccounts", "cabulk", "{domain} {namemask} {number of accounts to create}", Category.MISC, 3, 3), CREATE_CALENDAR_RESOURCE( "createCalendarResource", "ccr", "{name@domain} {password} [attr1 value1 [attr2 value2...]]", Category.CALENDAR, 2, Integer.MAX_VALUE), CREATE_COS( "createCos", "cc", "{name} [attr1 value1 [attr2 value2...]]", Category.COS, 1, Integer.MAX_VALUE), CREATE_DATA_SOURCE( "createDataSource", "cds", "{name@domain} {ds-type} {ds-name} zimbraDataSourceEnabled {TRUE|FALSE} zimbraDataSourceFolderId {folder-id} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 3, Integer.MAX_VALUE), CREATE_DISTRIBUTION_LIST( "createDistributionList", "cdl", "{list@domain}", Category.LIST, 1, Integer.MAX_VALUE), CREATE_DYNAMIC_DISTRIBUTION_LIST( "createDynamicDistributionList", "cddl", "{list@domain}", Category.LIST, 1, Integer.MAX_VALUE), CREATE_DISTRIBUTION_LISTS_BULK( "createDistributionListsBulk", "cdlbulk"), CREATE_DOMAIN( "createDomain", "cd", "{domain} [attr1 value1 [attr2 value2...]]", Category.DOMAIN, 1, Integer.MAX_VALUE), CREATE_SERVER( "createServer", "cs", "{name} [attr1 value1 [attr2 value2...]]", Category.SERVER, 1, Integer.MAX_VALUE), CREATE_UC_SERVICE( "createUCService", "cucs", "{name} [attr1 value1 [attr2 value2...]]", Category.UCSERVICE, 1, Integer.MAX_VALUE), CREATE_IDENTITY( "createIdentity", "cid", "{name@domain} {identity-name} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 2, Integer.MAX_VALUE), CREATE_SIGNATURE( "createSignature", "csig", "{name@domain} {signature-name} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 2, Integer.MAX_VALUE), CREATE_XMPP_COMPONENT( "createXMPPComponent", "cxc", "{short-name} {domain} {server} {classname} {category} {type} [attr value1 [attr2 value2...]]", Category.CONFIG, 6, Integer.MAX_VALUE), DELETE_ACCOUNT( "deleteAccount", "da", "{name@domain|id}", Category.ACCOUNT, 1, 1), DELETE_ALWAYSONCLUSTER( "deleteAlwaysOnCluster", "daoc", "{name|id}", Category.ALWAYSONCLUSTER, 1, 1), DELETE_CALENDAR_RESOURCE( "deleteCalendarResource", "dcr", "{name@domain|id}", Category.CALENDAR, 1, 1), DELETE_COS( "deleteCos", "dc", "{name|id}", Category.COS, 1, 1), DELETE_DATA_SOURCE( "deleteDataSource", "dds", "{name@domain|id} {ds-name|ds-id}", Category.ACCOUNT, 2, 2), DELETE_DISTRIBUTION_LIST( "deleteDistributionList", "ddl", "{list@domain|id}", Category.LIST, 1, 1), DELETE_DOMAIN( "deleteDomain", "dd", "{domain|id}", Category.DOMAIN, 1, 1), DELETE_IDENTITY( "deleteIdentity", "did", "{name@domain|id} {identity-name}", Category.ACCOUNT, 2, 2), DELETE_SIGNATURE( "deleteSignature", "dsig", "{name@domain|id} {signature-name}", Category.ACCOUNT, 2, 2), DELETE_SERVER( "deleteServer", "ds", "{name|id}", Category.SERVER, 1, 1), DELETE_UC_SERVICE( "deleteUCService", "ducs", "{name|id}", Category.UCSERVICE, 1, 1), DELETE_XMPP_COMPONENT( "deleteXMPPComponent", "dxc", "{xmpp-component-name}", Category.CONFIG, 1, 1), DESCRIBE( "describe", "desc", "[[-v] [-ni] [{entry-type}]] | [-a {attribute-name}]", Category.MISC, 0, Integer.MAX_VALUE, null, null, true), EXIT( "exit", "quit", "", Category.MISC, 0, 0), FLUSH_CACHE( "flushCache", "fc", "[-a] {" + CacheEntryType .names() + "|<extension-cache-type>} [name1|id1 [name2|id2...]]", Category.MISC, 1, Integer.MAX_VALUE), GENERATE_DOMAIN_PRE_AUTH( "generateDomainPreAuth", "gdpa", "{domain|id} {name|id|foreignPrincipal} {by} {timestamp|0} {expires|0}", Category.MISC, 5, 6), GENERATE_DOMAIN_PRE_AUTH_KEY( "generateDomainPreAuthKey", "gdpak", "[-f] {domain|id}", Category.MISC, 1, 2), GET_ACCOUNT( "getAccount", "ga", "[-e] {name@domain|id} [attr1 [attr2...]]", Category.ACCOUNT, 1, Integer.MAX_VALUE), GET_ALWAYSONCLUSTER( "getAlwaysOnCluster", "gaoc", "{name|id} [attr1 [attr2...]]", Category.ALWAYSONCLUSTER, 1, Integer.MAX_VALUE), GET_DATA_SOURCES( "getDataSources", "gds", "{name@domain|id} [arg1 [arg2...]]", Category.ACCOUNT, 1, Integer.MAX_VALUE), GET_IDENTITIES( "getIdentities", "gid", "{name@domain|id} [arg1 [arg...]]", Category.ACCOUNT, 1, Integer.MAX_VALUE), GET_SIGNATURES( "getSignatures", "gsig", "{name@domain|id} [arg1 [arg...]]", Category.ACCOUNT, 1, Integer.MAX_VALUE), GET_ACCOUNT_MEMBERSHIP( "getAccountMembership", "gam", "{name@domain|id}", Category.ACCOUNT, 1, 2), GET_ALL_ACCOUNTS( "getAllAccounts", "gaa", "[-v] [-e] [-s server] [{domain}]", Category.ACCOUNT, 0, 5, Via.ldap), GET_ACCOUNT_LOGGERS( "getAccountLoggers", "gal", "[-s/--server hostname] {name@domain|id}", Category.LOG, 1, 3), GET_ALL_ACTIVE_SERVERS( "getAllActiveServers", "gaas", "[-v]", Category.SERVER, 0, 1), GET_ALL_ACCOUNT_LOGGERS( "getAllAccountLoggers", "gaal", "[-s/--server hostname]", Category.LOG, 0, 2), GET_ALL_ADMIN_ACCOUNTS( "getAllAdminAccounts", "gaaa", "[-v] [-e] [attr1 [attr2...]]", Category.ACCOUNT, 0, Integer.MAX_VALUE), GET_ALL_ALWAYSONCLUSTERS( "getAllAlwaysOnClusters", "gaaoc", "[-v]", Category.ALWAYSONCLUSTER, 0, 1), GET_ALL_CALENDAR_RESOURCES( "getAllCalendarResources", "gacr", "[-v] [-e] [-s server] [{domain}]", Category.CALENDAR, 0, 5), GET_ALL_CONFIG( "getAllConfig", "gacf", "[attr1 [attr2...]]", Category.CONFIG, 0, Integer.MAX_VALUE), GET_ALL_COS( "getAllCos", "gac", "[-v]", Category.COS, 0, 1), GET_ALL_DISTRIBUTION_LISTS( "getAllDistributionLists", "gadl", "[-v] [{domain}]", Category.LIST, 0, 2), GET_ALL_DOMAINS( "getAllDomains", "gad", "[-v] [-e] [attr1 [attr2...]]", Category.DOMAIN, 0, Integer.MAX_VALUE), GET_ALL_EFFECTIVE_RIGHTS( "getAllEffectiveRights", "gaer", "{grantee-type} {grantee-id|grantee-name} [expandSetAttrs] [expandGetAttrs]", Category.RIGHT, 2, 4), GET_ALL_FREEBUSY_PROVIDERS( "getAllFbp", "gafbp", "[-v]", Category.FREEBUSY, 0, 1), GET_ALL_RIGHTS( "getAllRights", "gar", "[-v] [-t {target-type}] [-c " + RightClass .allValuesInString( "|") + "]", Category.RIGHT, 0, 5), GET_ALL_SERVERS( "getAllServers", "gas", "[-v] [-e] [service]", Category.SERVER, 0, 3), GET_ALL_UC_SERVICES( "getAllUCServices", "gaucs", "[-v]", Category.UCSERVICE, 0, 3), GET_ALL_XMPP_COMPONENTS( "getAllXMPPComponents", "gaxcs", "", Category.CONFIG, 0, 0), GET_AUTH_TOKEN_INFO( "getAuthTokenInfo", "gati", "{auth-token}", Category.MISC, 1, 1), GET_CALENDAR_RESOURCE( "getCalendarResource", "gcr", "{name@domain|id} [attr1 [attr2...]]", Category.CALENDAR, 1, Integer.MAX_VALUE), GET_CONFIG( "getConfig", "gcf", "{name}", Category.CONFIG, 1, 1), GET_COS( "getCos", "gc", "{name|id} [attr1 [attr2...]]", Category.COS, 1, Integer.MAX_VALUE), GET_DISTRIBUTION_LIST( "getDistributionList", "gdl", "{list@domain|id} [attr1 [attr2...]]", Category.LIST, 1, Integer.MAX_VALUE), GET_DISTRIBUTION_LIST_MEMBERSHIP( "getDistributionListMembership", "gdlm", "{name@domain|id}", Category.LIST, 1, 1), GET_DOMAIN( "getDomain", "gd", "[-e] {domain|id} [attr1 [attr2...]]", Category.DOMAIN, 1, Integer.MAX_VALUE), GET_DOMAIN_INFO( "getDomainInfo", "gdi", "name|id|virtualHostname {value} [attr1 [attr2...]]", Category.DOMAIN, 2, Integer.MAX_VALUE), GET_CONFIG_SMIME_CONFIG( "getConfigSMIMEConfig", "gcsc", "[configName]", Category.DOMAIN, 0, 1), GET_DOMAIN_SMIME_CONFIG( "getDomainSMIMEConfig", "gdsc", "name|id [configName]", Category.DOMAIN, 1, 2), GET_EFFECTIVE_RIGHTS( "getEffectiveRights", "ger", "{target-type} [{target-id|target-name}] {grantee-id|grantee-name} [expandSetAttrs] [expandGetAttrs]", Category.RIGHT, 1, 5, null, new RightCommandHelp( false, false, false)), // for testing the provisioning interface only, comment out after testing, the soap is only used by admin console GET_CREATE_OBJECT_ATTRS("getCreateObjectAttrs", "gcoa", "{target-type} {domain-id|domain-name} {cos-id|cos-name} {grantee-id|grantee-name}", Category.RIGHT, 3, 4), GET_FREEBUSY_QUEUE_INFO("getFreebusyQueueInfo", "gfbqi", "[{provider-name}]", Category.FREEBUSY, 0, 1), GET_GRANTS("getGrants", "gg", "[-t {target-type} [{target-id|target-name}]] [-g {grantee-type} {grantee-id|grantee-name} [{0|1 (whether to include grants granted to groups the grantee belongs)}]]", Category.RIGHT, 2, 7, null, new RightCommandHelp(false, false, false)), GET_MAILBOX_INFO("getMailboxInfo", "gmi", "{account}", Category.MAILBOX, 1, 1), GET_QUOTA_USAGE("getQuotaUsage", "gqu", "{server}", Category.MAILBOX, 1, 1), GET_RIGHT("getRight", "gr", "{right} [-e] (whether to expand combo rights recursively)", Category.RIGHT, 1, 2), GET_RIGHTS_DOC("getRightsDoc", "grd", "[java packages]", Category.RIGHT, 0, Integer.MAX_VALUE), GET_SERVER("getServer", "gs", "[-e] {name|id} [attr1 [attr2...]]", Category.SERVER, 1, Integer.MAX_VALUE), GET_UC_SERVICES( "getUCService", "gucs", "[-e] {name|id} [attr1 [attr2...]]", Category.UCSERVICE, 1, Integer.MAX_VALUE), GET_SHARE_INFO( "getShareInfo", "gsi", "{owner-name|owner-id}", Category.SHARE, 1, 1), GET_SPNEGO_DOMAIN( "getSpnegoDomain", "gsd", "", Category.MISC, 0, 0), GET_XMPP_COMPONENT( "getXMPPComponent", "gxc", "{name|id} [attr1 [attr2...]]", Category.CONFIG, 1, Integer.MAX_VALUE), GRANT_RIGHT( "grantRight", "grr", "{target-type} [{target-id|target-name}] {grantee-type} [{grantee-id|grantee-name} [secret]] {right}", Category.RIGHT, 3, 6, null, new RightCommandHelp( false, true, true)), HELP( "help", "?", "commands", Category.MISC, 0, 1), LDAP( ".ldap", ".l"), MODIFY_ACCOUNT( "modifyAccount", "ma", "{name@domain|id} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 3, Integer.MAX_VALUE), MODIFY_ALWAYSONCLUSTER( "modifyAlwaysOnCluster", "maoc", "{name|id} [attr1 value1 [attr2 value2...]]", Category.ALWAYSONCLUSTER, 3, Integer.MAX_VALUE), MODIFY_CALENDAR_RESOURCE( "modifyCalendarResource", "mcr", "{name@domain|id} [attr1 value1 [attr2 value2...]]", Category.CALENDAR, 3, Integer.MAX_VALUE), MODIFY_CONFIG( "modifyConfig", "mcf", "attr1 value1 [attr2 value2...]", Category.CONFIG, 2, Integer.MAX_VALUE), MODIFY_COS( "modifyCos", "mc", "{name|id} [attr1 value1 [attr2 value2...]]", Category.COS, 3, Integer.MAX_VALUE), MODIFY_DATA_SOURCE( "modifyDataSource", "mds", "{name@domain|id} {ds-name|ds-id} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 4, Integer.MAX_VALUE), MODIFY_DISTRIBUTION_LIST( "modifyDistributionList", "mdl", "{list@domain|id} attr1 value1 [attr2 value2...]", Category.LIST, 3, Integer.MAX_VALUE), MODIFY_DOMAIN( "modifyDomain", "md", "{domain|id} [attr1 value1 [attr2 value2...]]", Category.DOMAIN, 3, Integer.MAX_VALUE), MODIFY_CONFIG_SMIME_CONFIG( "modifyConfigSMIMEConfig", "mcsc", "configName [attr2 value2...]]", Category.DOMAIN, 1, Integer.MAX_VALUE), MODIFY_DOMAIN_SMIME_CONFIG( "modifyDomainSMIMEConfig", "mdsc", "name|id configName [attr2 value2...]]", Category.DOMAIN, 2, Integer.MAX_VALUE), MODIFY_IDENTITY( "modifyIdentity", "mid", "{name@domain|id} {identity-name} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 4, Integer.MAX_VALUE), MODIFY_SIGNATURE( "modifySignature", "msig", "{name@domain|id} {signature-name|signature-id} [attr1 value1 [attr2 value2...]]", Category.ACCOUNT, 4, Integer.MAX_VALUE), MODIFY_SERVER( "modifyServer", "ms", "{name|id} [attr1 value1 [attr2 value2...]]", Category.SERVER, 3, Integer.MAX_VALUE), MODIFY_UC_SERVICE( "modifyUCService", "mucs", "{name|id} [attr1 value1 [attr2 value2...]]", Category.UCSERVICE, 3, Integer.MAX_VALUE), MODIFY_XMPP_COMPONENT( "modifyXMPPComponent", "mxc", "{name@domain} [attr1 value1 [attr value2...]]", Category.CONFIG, 3, Integer.MAX_VALUE), PUSH_FREEBUSY( "pushFreebusy", "pfb", "[account-id ...]", Category.FREEBUSY, 1, Integer.MAX_VALUE), PUSH_FREEBUSY_DOMAIN( "pushFreebusyDomain", "pfbd", "{domain}", Category.FREEBUSY, 1, 1), PURGE_ACCOUNT_CALENDAR_CACHE( "purgeAccountCalendarCache", "pacc", "{name@domain|id} [...]", Category.CALENDAR, 1, Integer.MAX_VALUE), PURGE_FREEBUSY_QUEUE( "purgeFreebusyQueue", "pfbq", "[{provider-name}]", Category.FREEBUSY, 0, 1), RECALCULATE_MAILBOX_COUNTS( "recalculateMailboxCounts", "rmc", "{name@domain|id}", Category.MAILBOX, 1, 1), REMOVE_ACCOUNT_ALIAS( "removeAccountAlias", "raa", "{name@domain|id} {alias@domain}", Category.ACCOUNT, 2, 2), REMOVE_ACCOUNT_LOGGER( "removeAccountLogger", "ral", "[-s/--server hostname] [{name@domain|id}] [{logging-category}]", Category.LOG, 0, 4), REMOVE_DISTRIBUTION_LIST_ALIAS( "removeDistributionListAlias", "rdla", "{list@domain|id} {alias@domain}", Category.LIST, 2, 2), REMOVE_DISTRIBUTION_LIST_MEMBER( "removeDistributionListMember", "rdlm", "{list@domain|id} {member@domain}", Category.LIST, 2, Integer.MAX_VALUE), REMOVE_CONFIG_SMIME_CONFIG( "removeConfigSMIMEConfig", "rcsc", "configName", Category.DOMAIN, 1, 1), REMOVE_DOMAIN_SMIME_CONFIG( "removeDomainSMIMEConfig", "rdsc", "name|id configName", Category.DOMAIN, 2, 2), RENAME_ACCOUNT( "renameAccount", "ra", "{name@domain|id} {newName@domain}", Category.ACCOUNT, 2, 2), RENAME_CALENDAR_RESOURCE( "renameCalendarResource", "rcr", "{name@domain|id} {newName@domain}", Category.CALENDAR, 2, 2), RENAME_COS( "renameCos", "rc", "{name|id} {newName}", Category.COS, 2, 2), RENAME_DISTRIBUTION_LIST( "renameDistributionList", "rdl", "{list@domain|id} {newName@domain}", Category.LIST, 2, 2), RENAME_DOMAIN( "renameDomain", "rd", "{domain|id} {newDomain}", Category.DOMAIN, 2, 2, Via.ldap), RENAME_UCSERVICE( "renameUCService", "rucs", "{name|id} {newName}", Category.UCSERVICE, 2, 2), REINDEX_MAILBOX( "reIndexMailbox", "rim", "{name@domain|id} {start|status|cancel} [{types|ids} {type or id} [,type or id...]]", Category.MAILBOX, 2, Integer.MAX_VALUE, null, new ReindexCommandHelp()), COMPACT_INBOX_MAILBOX( "compactIndexMailbox", "cim", "{name@domain|id} {start|status}", Category.MAILBOX, 2, Integer.MAX_VALUE), VERIFY_INDEX( "verifyIndex", "vi", "{name@domain|id}", Category.MAILBOX, 1, 1), GET_INDEX_STATS( "getIndexStats", "gis", "{name@domain|id}", Category.MAILBOX, 1, 1), REVOKE_RIGHT( "revokeRight", "rvr", "{target-type} [{target-id|target-name}] {grantee-type} [{grantee-id|grantee-name}] {right}", Category.RIGHT, 3, 5, null, new RightCommandHelp( false, false, true)), SEARCH_ACCOUNTS( "searchAccounts", "sa", "[-v] {ldap-query} [limit {limit}] [offset {offset}] [sortBy {attr}] [sortAscending 0|1*] [domain {domain}]", Category.SEARCH, 1, Integer.MAX_VALUE), SEARCH_CALENDAR_RESOURCES( "searchCalendarResources", "scr", "[-v] domain attr op value [attr op value...]", Category.SEARCH, 1, Integer.MAX_VALUE, Via.ldap), SEARCH_GAL( "searchGal", "sg", "{domain} {name} [limit {limit}] [offset {offset}] [sortBy {attr}]", Category.SEARCH, 2, Integer.MAX_VALUE), SET_LOCAL_SERVER_ONLINE( "setLocalServerOnline", "slso", "", Category.SERVER, 0, 0), SELECT_MAILBOX( "selectMailbox", "sm", "{account-name} [{zmmailbox commands}]", Category.MAILBOX, 1, Integer.MAX_VALUE), SET_ACCOUNT_COS( "setAccountCos", "sac", "{name@domain|id} {cos-name|cos-id}", Category.ACCOUNT, 2, 2), SET_PASSWORD( "setPassword", "sp", "{name@domain|id} {password}", Category.ACCOUNT, 2, 2), SET_SERVER_OFFLINE( "setServerOffline", "sso", "{name|id}", Category.SERVER, 1, 1), GET_ALL_MTA_AUTH_URLS( "getAllMtaAuthURLs", "gamau", "", Category.SERVER, 0, 0), GET_ALL_REVERSE_PROXY_URLS( "getAllReverseProxyURLs", "garpu", "", Category.REVERSEPROXY, 0, 0), GET_ALL_REVERSE_PROXY_BACKENDS( "getAllReverseProxyBackends", "garpb", "", Category.REVERSEPROXY, 0, 0), GET_ALL_REVERSE_PROXY_DOMAINS( "getAllReverseProxyDomains", "garpd", "", Category.REVERSEPROXY, 0, 0, Via.ldap), GET_ALL_MEMCACHED_SERVERS( "getAllMemcachedServers", "gamcs", "", Category.SERVER, 0, 0), RELOAD_MEMCACHED_CLIENT_CONFIG( "reloadMemcachedClientConfig", "rmcc", "all | mailbox-server [...]", Category.MISC, 1, Integer.MAX_VALUE, Via.soap), GET_MEMCACHED_CLIENT_CONFIG( "getMemcachedClientConfig", "gmcc", "all | mailbox-server [...]", Category.MISC, 1, Integer.MAX_VALUE, Via.soap), SOAP( ".soap", ".s"), SYNC_GAL( "syncGal", "syg", "{domain} [{token}]", Category.MISC, 1, 2), UPDATE_PRESENCE_SESSION_ID( "updatePresenceSessionId", "upsid", "{UC service name or id} {app-username} {app-password}", Category.MISC, 3, 3, Via.soap), RESET_ALL_LOGGERS( "resetAllLoggers", "rlog", "[-s/--server hostname]", Category.LOG, 0, 2), UNLOCK_MAILBOX( "unlockMailbox", "ulm", "{name@domain|id} [hostname (When unlocking a mailbox after a failed move attempt provide the hostname of the server that was the target for the failed move. Otherwise, do not include hostname parameter)]", Category.MAILBOX, 1, 2, Via.soap); private String mName; private String mAlias; private String mHelp; private CommandHelp mExtraHelp; private Category mCat; private int mMinArgLength = 0; private int mMaxArgLength = Integer.MAX_VALUE; private Via mVia; private boolean mNeedsSchemaExtension = false; public static enum Via { soap, ldap; } public String getName() { return mName; } public String getAlias() { return mAlias; } public String getHelp() { return mHelp; } public CommandHelp getExtraHelp() { return mExtraHelp; } public Category getCategory() { return mCat; } public boolean hasHelp() { return mHelp != null; } public boolean checkArgsLength(String args[]) { int len = args == null ? 0 : args.length - 1; return len >= mMinArgLength && len <= mMaxArgLength; } public Via getVia() { return mVia; } public boolean needsSchemaExtension() { return mNeedsSchemaExtension || (mCat == Category.RIGHT); } public boolean isDeprecated() { return false; // Used to return true if mCat was Category.NOTEBOOK - which has now been removed } private Command(String name, String alias) { mName = name; mAlias = alias; } private Command(String name, String alias, String help, Category cat) { mName = name; mAlias = alias; mHelp = help; mCat = cat; } private Command(String name, String alias, String help, Category cat, int minArgLength, int maxArgLength) { mName = name; mAlias = alias; mHelp = help; mCat = cat; mMinArgLength = minArgLength; mMaxArgLength = maxArgLength; } private Command(String name, String alias, String help, Category cat, int minArgLength, int maxArgLength, Via via) { mName = name; mAlias = alias; mHelp = help; mCat = cat; mMinArgLength = minArgLength; mMaxArgLength = maxArgLength; mVia = via; } private Command(String name, String alias, String help, Category cat, int minArgLength, int maxArgLength, Via via, CommandHelp extraHelp) { mName = name; mAlias = alias; mHelp = help; mCat = cat; mMinArgLength = minArgLength; mMaxArgLength = maxArgLength; mVia = via; mExtraHelp = extraHelp; } private Command(String name, String alias, String help, Category cat, int minArgLength, int maxArgLength, Via via, CommandHelp extraHelp, boolean needsSchemaExtension) { this(name, alias, help, cat, minArgLength, maxArgLength, via, extraHelp); mNeedsSchemaExtension = needsSchemaExtension; } } private void addCommand(Command command) { String name = command.getName().toLowerCase(); if (commandIndex.get(name) != null) { throw new RuntimeException("duplicate command: " + name); } String alias = command.getAlias().toLowerCase(); if (commandIndex.get(alias) != null) { throw new RuntimeException("duplicate command: " + alias); } commandIndex.put(name, command); commandIndex.put(alias, command); } private void initCommands() { commandIndex = new HashMap<String, Command>(); for (Command c : Command.values()) { addCommand(c); } } private Command lookupCommand(String command) { return commandIndex.get(command.toLowerCase()); } /** * Commands that should always use LdapProv, but for convenience don't require the -l option specified. * * Commands that must use -l (e.g. gaa) are indicated in the Via field of the command definition */ private boolean forceLdapButDontRequireUseLdapOption(Command command) { return (command == Command.DESCRIBE); } private boolean needProvisioningInstance(Command command) { return !(command == Command.HELP); } private ProvUtil() { initCommands(); } public void initProvisioning() throws ServiceException { if (useLdap) { if (useLdapMaster) { LdapClient.masterOnly(); } prov = Provisioning.getInstance(); } else { SoapProvisioning sp = new SoapProvisioning(); sp.soapSetURI(LC.zimbra_admin_service_scheme.value() + serverHostname + ":" + serverPort + AdminConstants.ADMIN_SERVICE_URI); if (debugLevel != SoapDebugLevel.none) { sp.soapSetHttpTransportDebugListener(this); } if (account != null && password != null) { sp.soapAdminAuthenticate(account, password); } else if (authToken != null) { sp.soapAdminAuthenticate(authToken); } else { sp.soapZimbraAdminAuthenticate(); } prov = sp; } } private Command.Via violateVia(Command cmd) { Command.Via via = cmd.getVia(); if (via == null) { return null; } if (via == Command.Via.ldap && !(prov instanceof LdapProv)) { return Command.Via.ldap; } if (via == Command.Via.soap && !(prov instanceof SoapProvisioning)) { return Command.Via.soap; } return null; } private boolean execute(String args[]) throws ServiceException, ArgException, IOException { String[] members; Account account; AccountLoggerOptions alo; command = lookupCommand(args[0]); if (command == null) { return false; } Command.Via violatedVia = violateVia(command); if (violatedVia != null) { usage(violatedVia); return true; } if (!command.checkArgsLength(args)) { usage(); return true; } if (command.needsSchemaExtension()) { loadLdapSchemaExtensionAttrs(); } switch (command) { case ADD_ACCOUNT_ALIAS: prov.addAlias(lookupAccount(args[1]), args[2]); break; case ADD_ACCOUNT_LOGGER: alo = parseAccountLoggerOptions(args); if (!command.checkArgsLength(alo.args)) { usage(); return true; } doAddAccountLogger(alo); break; case AUTO_COMPLETE_GAL: doAutoCompleteGal(args); break; case AUTO_PROV_CONTROL: prov.autoProvControl(args[1]); break; case COPY_COS: console.println(prov.copyCos(lookupCos(args[1]).getId(), args[2]).getId()); break; case COUNT_ACCOUNT: doCountAccount(args); break; case COUNT_OBJECTS: doCountObjects(args); break; case CREATE_ACCOUNT: console.println( prov.createAccount(args[1], args[2].equals("") ? null : args[2], getMapAndCheck(args, 3, true)) .getId()); break; case CREATE_ALIAS_DOMAIN: console.println(doCreateAliasDomain(args[1], args[2], getMapAndCheck(args, 3, true)).getId()); break; case CREATE_ALWAYSONCLUSTER: console.println(prov.createAlwaysOnCluster(args[1], getMapAndCheck(args, 2, true)).getId()); break; case CREATE_COS: console.println(prov.createCos(args[1], getMapAndCheck(args, 2, true)).getId()); break; case CREATE_DOMAIN: console.println(prov.createDomain(args[1], getMapAndCheck(args, 2, true)).getId()); break; case CREATE_IDENTITY: prov.createIdentity(lookupAccount(args[1]), args[2], getMapAndCheck(args, 3, true)); break; case CREATE_SIGNATURE: console.println( prov.createSignature(lookupAccount(args[1]), args[2], getMapAndCheck(args, 3, true)).getId()); break; case CREATE_DATA_SOURCE: console.println(prov.createDataSource(lookupAccount(args[1]), DataSourceType.fromString(args[2]), args[3], getMapAndCheck(args, 4, true)).getId()); break; case CREATE_SERVER: console.println(prov.createServer(args[1], getMapAndCheck(args, 2, true)).getId()); break; case CREATE_UC_SERVICE: console.println(prov.createUCService(args[1], getMapAndCheck(args, 2, true)).getId()); break; case CREATE_XMPP_COMPONENT: doCreateXMPPComponent(args); break; case DESCRIBE: doDescribe(args); break; case EXIT: System.exit(errorOccursDuringInteraction ? 2 : 0); break; case FLUSH_CACHE: doFlushCache(args); break; case GENERATE_DOMAIN_PRE_AUTH_KEY: doGenerateDomainPreAuthKey(args); break; case GENERATE_DOMAIN_PRE_AUTH: doGenerateDomainPreAuth(args); break; case GET_ACCOUNT: doGetAccount(args); break; case GET_ACCOUNT_MEMBERSHIP: doGetAccountMembership(args); break; case GET_ALWAYSONCLUSTER: doGetAlwaysOnCluster(args); break; case GET_IDENTITIES: doGetAccountIdentities(args); break; case GET_SIGNATURES: doGetAccountSignatures(args); break; case GET_DATA_SOURCES: doGetAccountDataSources(args); break; case GET_ACCOUNT_LOGGERS: alo = parseAccountLoggerOptions(args); if (!command.checkArgsLength(alo.args)) { usage(); return true; } doGetAccountLoggers(alo); break; case GET_ALL_ACCOUNT_LOGGERS: alo = parseAccountLoggerOptions(args); if (!command.checkArgsLength(alo.args)) { usage(); return true; } doGetAllAccountLoggers(alo); break; case GET_ALL_ACCOUNTS: doGetAllAccounts(args); break; case GET_ALL_ACTIVE_SERVERS: doGetAllActiveServers(args); break; case GET_ALL_ADMIN_ACCOUNTS: doGetAllAdminAccounts(args); break; case GET_ALL_ALWAYSONCLUSTERS: doGetAllAlwaysOnClusters(args); break; case GET_ALL_CONFIG: dumpAttrs(prov.getConfig().getAttrs(), getArgNameSet(args, 1)); break; case GET_ALL_COS: doGetAllCos(args); break; case GET_ALL_DOMAINS: doGetAllDomains(args); break; case GET_ALL_FREEBUSY_PROVIDERS: doGetAllFreeBusyProviders(); break; case GET_ALL_RIGHTS: doGetAllRights(args); break; case GET_ALL_SERVERS: doGetAllServers(args); break; case GET_ALL_UC_SERVICES: doGetAllUCServices(args); break; case GET_CONFIG: doGetConfig(args); break; case GET_COS: dumpCos(lookupCos(args[1]), getArgNameSet(args, 2)); break; case GET_DISTRIBUTION_LIST_MEMBERSHIP: doGetDistributionListMembership(lookupGroup(args[1])); break; case GET_DOMAIN: doGetDomain(args); break; case GET_DOMAIN_INFO: doGetDomainInfo(args); break; case GET_CONFIG_SMIME_CONFIG: doGetConfigSMIMEConfig(args); break; case GET_DOMAIN_SMIME_CONFIG: doGetDomainSMIMEConfig(args); break; case GET_FREEBUSY_QUEUE_INFO: doGetFreeBusyQueueInfo(args); break; case GET_RIGHT: doGetRight(args); break; case GET_RIGHTS_DOC: doGetRightsDoc(args); break; case GET_SERVER: doGetServer(args); break; case GET_UC_SERVICES: dumpUCService(lookupUCService(args[1]), getArgNameSet(args, 2)); break; case GET_XMPP_COMPONENT: doGetXMPPComponent(args); break; case CHECK_RIGHT: doCheckRight(args); break; case GET_ALL_EFFECTIVE_RIGHTS: doGetAllEffectiveRights(args); break; case GET_EFFECTIVE_RIGHTS: doGetEffectiveRights(args); break; case GET_CREATE_OBJECT_ATTRS: doGetCreateObjectAttrs(args); break; case GET_GRANTS: doGetGrants(args); break; case GRANT_RIGHT: doGrantRight(args); break; case REVOKE_RIGHT: doRevokeRight(args); break; case HELP: doHelp(args); break; case MODIFY_ACCOUNT: prov.modifyAttrs(lookupAccount(args[1]), getMapAndCheck(args, 2, false), true); break; case MODIFY_ALWAYSONCLUSTER: prov.modifyAttrs(lookupAlwaysOnCluster(args[1]), getMapAndCheck(args, 2, false), true); break; case MODIFY_DATA_SOURCE: account = lookupAccount(args[1]); prov.modifyDataSource(account, lookupDataSourceId(account, args[2]), getMapAndCheck(args, 3, false)); break; case MODIFY_IDENTITY: account = lookupAccount(args[1]); prov.modifyIdentity(account, args[2], getMapAndCheck(args, 3, false)); break; case MODIFY_SIGNATURE: account = lookupAccount(args[1]); prov.modifySignature(account, lookupSignatureId(account, args[2]), getMapAndCheck(args, 3, false)); break; case MODIFY_COS: prov.modifyAttrs(lookupCos(args[1]), getMapAndCheck(args, 2, false), true); break; case MODIFY_CONFIG: prov.modifyAttrs(prov.getConfig(), getMapAndCheck(args, 1, false), true); break; case MODIFY_DOMAIN: prov.modifyAttrs(lookupDomain(args[1]), getMapAndCheck(args, 2, false), true); break; case MODIFY_CONFIG_SMIME_CONFIG: doModifyConfigSMIMEConfig(args); break; case MODIFY_DOMAIN_SMIME_CONFIG: doModifyDomainSMIMEConfig(args); break; case MODIFY_SERVER: prov.modifyAttrs(lookupServer(args[1]), getMapAndCheck(args, 2, false), true); break; case MODIFY_UC_SERVICE: prov.modifyAttrs(lookupUCService(args[1]), getMapAndCheck(args, 2, false), true); break; case DELETE_ACCOUNT: doDeleteAccount(args); break; case DELETE_ALWAYSONCLUSTER: prov.deleteAlwaysOnCluster(lookupAlwaysOnCluster(args[1]).getId()); break; case DELETE_COS: prov.deleteCos(lookupCos(args[1]).getId()); break; case DELETE_DOMAIN: prov.deleteDomain(lookupDomain(args[1]).getId()); break; case DELETE_IDENTITY: prov.deleteIdentity(lookupAccount(args[1]), args[2]); break; case DELETE_SIGNATURE: account = lookupAccount(args[1]); prov.deleteSignature(account, lookupSignatureId(account, args[2])); break; case DELETE_DATA_SOURCE: account = lookupAccount(args[1]); prov.deleteDataSource(account, lookupDataSourceId(account, args[2])); break; case DELETE_SERVER: prov.deleteServer(lookupServer(args[1]).getId()); break; case DELETE_UC_SERVICE: prov.deleteUCService(lookupUCService(args[1]).getId()); break; case DELETE_XMPP_COMPONENT: prov.deleteXMPPComponent(lookupXMPPComponent(args[1])); break; case PUSH_FREEBUSY: doPushFreeBusy(args); break; case PUSH_FREEBUSY_DOMAIN: doPushFreeBusyForDomain(args); break; case PURGE_FREEBUSY_QUEUE: doPurgeFreeBusyQueue(args); break; case PURGE_ACCOUNT_CALENDAR_CACHE: doPurgeAccountCalendarCache(args); break; case REMOVE_ACCOUNT_ALIAS: Account acct = lookupAccount(args[1], false); prov.removeAlias(acct, args[2]); // even if acct is null, we still invoke removeAlias and throw an exception afterwards. // this is so dangling aliases can be cleaned up as much as possible if (acct == null) { throw AccountServiceException.NO_SUCH_ACCOUNT(args[1]); } break; case REMOVE_ACCOUNT_LOGGER: alo = parseAccountLoggerOptions(args); if (!command.checkArgsLength(alo.args)) { usage(); return true; } doRemoveAccountLogger(alo); break; case REMOVE_CONFIG_SMIME_CONFIG: doRemoveConfigSMIMEConfig(args); break; case REMOVE_DOMAIN_SMIME_CONFIG: doRemoveDomainSMIMEConfig(args); break; case RENAME_ACCOUNT: doRenameAccount(args); break; case RENAME_COS: prov.renameCos(lookupCos(args[1]).getId(), args[2]); break; case RENAME_DOMAIN: doRenameDomain(args); break; case RENAME_UCSERVICE: prov.renameUCService(lookupUCService(args[1]).getId(), args[2]); break; case SET_ACCOUNT_COS: prov.setCOS(lookupAccount(args[1]), lookupCos(args[2])); break; case SET_SERVER_OFFLINE: doSetServerOffline(args); break; case SET_LOCAL_SERVER_ONLINE: doSetLocalServerOnline(); break; case SEARCH_ACCOUNTS: doSearchAccounts(args); break; case SEARCH_GAL: doSearchGal(args); break; case SYNC_GAL: doSyncGal(args); break; case SET_PASSWORD: SetPasswordResult result = prov.setPassword(lookupAccount(args[1]), args[2]); if (result.hasMessage()) { console.println(result.getMessage()); } break; case CHECK_PASSWORD_STRENGTH: prov.checkPasswordStrength(lookupAccount(args[1]), args[2]); console.println("Password passed strength check."); break; case CREATE_DISTRIBUTION_LIST: console.println(prov.createGroup(args[1], getMapAndCheck(args, 2, true), false).getId()); break; case CREATE_DYNAMIC_DISTRIBUTION_LIST: console.println(prov.createGroup(args[1], getMapAndCheck(args, 2, true), true).getId()); break; case CREATE_DISTRIBUTION_LISTS_BULK: doCreateDistributionListsBulk(args); break; case GET_ALL_DISTRIBUTION_LISTS: doGetAllDistributionLists(args); break; case GET_DISTRIBUTION_LIST: dumpGroup(lookupGroup(args[1]), getArgNameSet(args, 2)); break; case GET_ALL_XMPP_COMPONENTS: doGetAllXMPPComponents(); break; case MODIFY_DISTRIBUTION_LIST: prov.modifyAttrs(lookupGroup(args[1]), getMapAndCheck(args, 2, false), true); break; case DELETE_DISTRIBUTION_LIST: prov.deleteGroup(lookupGroup(args[1]).getId()); break; case ADD_DISTRIBUTION_LIST_MEMBER: members = new String[args.length - 2]; System.arraycopy(args, 2, members, 0, args.length - 2); prov.addGroupMembers(lookupGroup(args[1]), members); break; case REMOVE_DISTRIBUTION_LIST_MEMBER: members = new String[args.length - 2]; System.arraycopy(args, 2, members, 0, args.length - 2); prov.removeGroupMembers(lookupGroup(args[1]), members); break; case CREATE_BULK_ACCOUNTS: doCreateAccountsBulk(args); break; case ADD_DISTRIBUTION_LIST_ALIAS: prov.addGroupAlias(lookupGroup(args[1]), args[2]); break; case REMOVE_DISTRIBUTION_LIST_ALIAS: Group dl = lookupGroup(args[1], false); // Even if dl is null, we still invoke removeAlias. // This is so dangling aliases can be cleaned up as much as possible. // If dl is null, the NO_SUCH_DISTRIBUTION_LIST thrown by SOAP will contain // null as the dl identity, because SoapProvisioning sends no id to the server. // In this case, we catch the NO_SUCH_DISTRIBUTION_LIST and throw another one // with the named/id entered on the comand line. try { prov.removeGroupAlias(dl, args[2]); } catch (ServiceException e) { if (!(dl == null && AccountServiceException.NO_SUCH_DISTRIBUTION_LIST.equals(e.getCode()))) { throw e; } // else eat the exception, we will throw below } if (dl == null) { throw AccountServiceException.NO_SUCH_DISTRIBUTION_LIST(args[1]); } break; case RENAME_DISTRIBUTION_LIST: prov.renameGroup(lookupGroup(args[1]).getId(), args[2]); break; case CREATE_CALENDAR_RESOURCE: console.println(prov.createCalendarResource(args[1], args[2].isEmpty() ? null : args[2], getMapAndCheck(args, 3, true)).getId()); break; case DELETE_CALENDAR_RESOURCE: prov.deleteCalendarResource(lookupCalendarResource(args[1]).getId()); break; case MODIFY_CALENDAR_RESOURCE: prov.modifyAttrs(lookupCalendarResource(args[1]), getMapAndCheck(args, 2, false), true); break; case RENAME_CALENDAR_RESOURCE: prov.renameCalendarResource(lookupCalendarResource(args[1]).getId(), args[2]); break; case GET_CALENDAR_RESOURCE: dumpCalendarResource(lookupCalendarResource(args[1]), true, getArgNameSet(args, 2)); break; case GET_ALL_CALENDAR_RESOURCES: doGetAllCalendarResources(args); break; case SEARCH_CALENDAR_RESOURCES: doSearchCalendarResources(args); break; case GET_SHARE_INFO: doGetShareInfo(args); break; case GET_SPNEGO_DOMAIN: doGetSpnegoDomain(); break; case GET_QUOTA_USAGE: doGetQuotaUsage(args); break; case GET_MAILBOX_INFO: doGetMailboxInfo(args); break; case REINDEX_MAILBOX: doReIndexMailbox(args); break; case COMPACT_INBOX_MAILBOX: doCompactIndexMailbox(args); break; case VERIFY_INDEX: doVerifyIndex(args); break; case GET_INDEX_STATS: doGetIndexStats(args); break; case RECALCULATE_MAILBOX_COUNTS: doRecalculateMailboxCounts(args); break; case SELECT_MAILBOX: if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } ZMailboxUtil util = new ZMailboxUtil(); util.setVerbose(verboseMode); util.setDebug(debugLevel != SoapDebugLevel.none); boolean smInteractive = interactiveMode && args.length < 3; util.setInteractive(smInteractive); util.selectMailbox(args[1], (SoapProvisioning) prov); if (smInteractive) { util.interactive(cliReader); } else if (args.length > 2) { String newArgs[] = new String[args.length - 2]; System.arraycopy(args, 2, newArgs, 0, newArgs.length); util.execute(newArgs); } else { throw ZClientException.CLIENT_ERROR("command only valid in interactive mode or with arguments", null); } break; case GET_ALL_MTA_AUTH_URLS: doGetAllMtaAuthURLs(); break; case GET_ALL_REVERSE_PROXY_URLS: doGetAllReverseProxyURLs(); break; case GET_ALL_REVERSE_PROXY_BACKENDS: doGetAllReverseProxyBackends(); break; case GET_ALL_REVERSE_PROXY_DOMAINS: doGetAllReverseProxyDomains(); break; case GET_ALL_MEMCACHED_SERVERS: doGetAllMemcachedServers(); break; case RELOAD_MEMCACHED_CLIENT_CONFIG: doReloadMemcachedClientConfig(args); break; case GET_MEMCACHED_CLIENT_CONFIG: doGetMemcachedClientConfig(args); break; case GET_AUTH_TOKEN_INFO: doGetAuthTokenInfo(args); break; case UPDATE_PRESENCE_SESSION_ID: doUpdatePresenceSessionId(args); break; case SOAP: // HACK FOR NOW SoapProvisioning sp = new SoapProvisioning(); sp.soapSetURI("https://localhost:" + serverPort + AdminConstants.ADMIN_SERVICE_URI); sp.soapZimbraAdminAuthenticate(); prov = sp; break; case LDAP: // HACK FOR NOW prov = Provisioning.getInstance(); break; case RESET_ALL_LOGGERS: doResetAllLoggers(args); break; case UNLOCK_MAILBOX: doUnlockMailbox(args); break; default: return false; } return true; } private void sendMailboxLockoutRequest(String acctName, String server, String operation) throws ServiceException, IOException { LockoutMailboxRequest req = LockoutMailboxRequest.create(AccountNameSelector.fromName(acctName)); req.setOperation(operation); String url = URLUtil.getAdminURL(server); ZAuthToken token = ((SoapProvisioning) prov).getAuthToken(); SoapHttpTransport transport = new SoapHttpTransport(url); transport.setAuthToken(token); transport.invokeWithoutSession(JaxbUtil.jaxbToElement(req)); } private void doUnlockMailbox(String[] args) throws ServiceException { String accountVal = null; if (args.length > 1) { accountVal = args[1]; } else { usage(); return; } if (accountVal != null) { Account acct = lookupAccount(accountVal); //will throw NO_SUCH_ACCOUNT if not found if (!acct.getAccountStatus().isActive()) { throw ServiceException.FAILURE(String.format( "Cannot unlock mailbox for account %s. Account status must be %s. Curent account status is %s. " + "You must change the value of zimbraAccountStatus to '%s' first", accountVal, AccountStatus.active, acct.getAccountStatus(), AccountStatus.active), null); } String accName = acct.getName(); String server = acct.getMailHost(); try { sendMailboxLockoutRequest(accName, server, AdminConstants.A_END); } catch (ServiceException e) { if (ServiceException.UNKNOWN_DOCUMENT.equals(e.getCode())) { throw ServiceException.FAILURE( "source server version does not support " + AdminConstants.E_LOCKOUT_MAILBOX_REQUEST, e); } else if (ServiceException.NOT_FOUND.equals(e.getCode())) { //if mailbox is not locked, move on printOutput("Warning: " + e.getMessage()); } else { throw e; } } catch (IOException e) { throw ServiceException.FAILURE( String.format("Error sending %s (operation = %s) request for %s to %s", AdminConstants.E_LOCKOUT_MAILBOX_REQUEST, AdminConstants.A_END, accountVal, server), e); } //unregister moveout if hostname is provided if (args.length > 2) { //set account status to maintenance and lock the mailbox to avoid race conditions acct.setAccountStatus(AccountStatus.maintenance); try { sendMailboxLockoutRequest(accName, server, AdminConstants.A_START); } catch (IOException e) { throw ServiceException.FAILURE(String.format( "Error sending %s (opertion = %s) request for %s to %s.\n Warning: Account is left in maintenance state!", AdminConstants.E_LOCKOUT_MAILBOX_REQUEST, AdminConstants.A_START, accountVal, server), e); } //unregister moveout via SOAP String targetServer = args[2]; try { UnregisterMailboxMoveOutRequest unregisterReq = UnregisterMailboxMoveOutRequest .create(MailboxMoveSpec.createForNameAndTarget(accName, targetServer)); String url = URLUtil.getAdminURL(server); ZAuthToken token = ((SoapProvisioning) prov).getAuthToken(); SoapHttpTransport transport = new SoapHttpTransport(url); transport.setAuthToken(token); transport.invokeWithoutSession(JaxbUtil.jaxbToElement(unregisterReq)); } catch (ServiceException e) { if (ServiceException.UNKNOWN_DOCUMENT.equals(e.getCode())) { throw ServiceException.FAILURE(String.format("target server version does not support %s.", BackupConstants.E_UNREGISTER_MAILBOX_MOVE_OUT_REQUEST), e); } else { throw ServiceException.FAILURE("Failed to unregister mailbox moveout", e); } } catch (IOException e) { throw ServiceException.FAILURE(String.format("Error sending %s request for %s to %s.", BackupConstants.E_UNREGISTER_MAILBOX_MOVE_OUT_REQUEST, accountVal, server), e); } finally { //unlock mailbox object and end account maintenance even if failed to unregister moveout try { sendMailboxLockoutRequest(accName, server, AdminConstants.A_END); } catch (ServiceException e) { //print error messages, but don't throw any more exceptions, because we have to set account status back to 'active' if (ServiceException.UNKNOWN_DOCUMENT.equals(e.getCode())) { printError("source server version does not support " + AdminConstants.E_LOCKOUT_MAILBOX_REQUEST); } else { printError( String.format("Error: failed to unregister mailbox moveout.\n Exception: %s.", e.getMessage())); } } catch (IOException e) { printError(String.format( "Error sending %s (operation = %s) request for %s to %s after unregistering moveout. Exception: %s", AdminConstants.E_LOCKOUT_MAILBOX_REQUEST, AdminConstants.A_END, accountVal, server, e.getMessage())); } //end account maintenance acct.setAccountStatus(AccountStatus.active); } } } } private void doGetDomain(String[] args) throws ServiceException { boolean applyDefault = true; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-e")) { applyDefault = false; } else { break; } i++; } if (i >= args.length) { usage(); return; } dumpDomain(lookupDomain(args[i], prov, applyDefault), applyDefault, getArgNameSet(args, i + 1)); } private void doGetDomainInfo(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Key.DomainBy by = Key.DomainBy.fromString(args[1]); String key = args[2]; Domain domain = sp.getDomainInfo(by, key); if (domain == null) { throw AccountServiceException.NO_SUCH_DOMAIN(key); } else { dumpDomain(domain, getArgNameSet(args, 3)); } } private void doRenameDomain(String[] args) throws ServiceException { // bug 56768 // if we are not already using master only, force it to use master. // Note: after rename domain, the zmprov instance will stay in "master only" mode. if (!useLdapMaster) { ((LdapProv) prov).alwaysUseMaster(); } LdapProv lp = (LdapProv) prov; Domain domain = lookupDomain(args[1]); lp.renameDomain(domain.getId(), args[2]); printOutput("domain " + args[1] + " renamed to " + args[2]); printOutput("Note: use zmlocalconfig to check and update any localconfig settings referencing domain '" + args[1] + "' on all servers."); printOutput( "Use /opt/zimbra/libexec/zmdkimkeyutil to recreate the DKIM entries for new domain name if required."); } private void doGetQuotaUsage(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; List<QuotaUsage> result = sp.getQuotaUsage(args[1]); for (QuotaUsage u : result) { console.printf("%s %d %d\n", u.getName(), u.getLimit(), u.getUsed()); } } private void doGetMailboxInfo(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(args[1]); MailboxInfo info = sp.getMailbox(acct); console.printf("mailboxId: %s\nquotaUsed: %d\n", info.getMailboxId(), info.getUsed()); } private void doReIndexMailbox(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(args[1]); ReIndexBy by = null; String[] values = null; if (args.length > 3) { try { by = ReIndexBy.valueOf(args[3]); } catch (IllegalArgumentException e) { throw ServiceException.INVALID_REQUEST("invalid reindex-by", null); } if (args.length > 4) { values = new String[args.length - 4]; System.arraycopy(args, 4, values, 0, args.length - 4); } else { throw ServiceException.INVALID_REQUEST("missing reindex-by values", null); } } ReIndexInfo info = sp.reIndex(acct, args[2], by, values); ReIndexInfo.Progress progress = info.getProgress(); console.printf("status: %s\n", info.getStatus()); if (progress != null) { console.printf("progress: numSucceeded=%d, numFailed=%d, numRemaining=%d\n", progress.getNumSucceeded(), progress.getNumFailed(), progress.getNumRemaining()); } } private void doCompactIndexMailbox(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(args[1]); String status = sp.compactIndex(acct, args[2]); console.printf("status: %s\n", status); } private void doVerifyIndex(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } console.println("Verifying, on a large index it can take quite a long time..."); SoapProvisioning soap = (SoapProvisioning) prov; SoapProvisioning.VerifyIndexResult result = soap.verifyIndex(lookupAccount(args[1])); console.println(); console.print(result.message); if (!result.status) { throw ServiceException.FAILURE("The index may be corrupted. Run reIndexMailbox(rim) to repair.", null); } } private void doGetIndexStats(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(args[1]); IndexStatsInfo stats = sp.getIndexStats(acct); console.printf("stats: maxDocs:%d numDeletedDocs:%d\n", stats.getMaxDocs(), stats.getNumDeletedDocs()); } private void doRecalculateMailboxCounts(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account account = lookupAccount(args[1]); long quotaUsed = sp.recalculateMailboxCounts(account); console.printf("account: " + account.getName() + "\nquotaUsed: " + quotaUsed + "\n"); } private class AccountLoggerOptions { String server; String[] args; } /** * Handles an optional <tt>-s</tt> or <tt>--server</tt> argument that may be passed to the logging commands. Returns * an <tt>AccountLogggerOptions</tt> object that contains all arguments except the server option and value. */ private AccountLoggerOptions parseAccountLoggerOptions(String[] args) throws ServiceException { AccountLoggerOptions alo = new AccountLoggerOptions(); if (args.length > 1 && (args[1].equals("-s") || args[1].equals("--server"))) { if (args.length == 2) { throw ServiceException.FAILURE("Server name not specified.", null); } alo.server = args[2]; int numArgs = args.length - 2; alo.args = new String[numArgs]; alo.args[0] = args[0]; for (int i = 1; i < numArgs; i++) { alo.args[i] = args[i + 2]; } } else { alo.args = args; } return alo; } private void doAddAccountLogger(AccountLoggerOptions alo) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(alo.args[1]); sp.addAccountLogger(acct, alo.args[2], alo.args[3], alo.server); } private void doGetAccountLoggers(AccountLoggerOptions alo) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = lookupAccount(alo.args[1]); for (AccountLogger accountLogger : sp.getAccountLoggers(acct, alo.server)) { console.printf("%s=%s\n", accountLogger.getCategory(), accountLogger.getLevel()); } } private void doGetAllAccountLoggers(AccountLoggerOptions alo) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Map<String, List<AccountLogger>> allLoggers = sp.getAllAccountLoggers(alo.server); for (String accountName : allLoggers.keySet()) { console.printf("# name %s\n", accountName); for (AccountLogger logger : allLoggers.get(accountName)) { console.printf("%s=%s\n", logger.getCategory(), logger.getLevel()); } } } private void doRemoveAccountLogger(AccountLoggerOptions alo) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sp = (SoapProvisioning) prov; Account acct = null; String category = null; if (alo.args.length == 2) { // Hack: determine if it's an account or category, based on the name. String arg = alo.args[1]; if (arg.startsWith("zimbra.") || arg.startsWith("com.zimbra")) { category = arg; } else { acct = lookupAccount(alo.args[1]); } } if (alo.args.length == 3) { acct = lookupAccount(alo.args[1]); category = alo.args[2]; } sp.removeAccountLoggers(acct, category, alo.server); } private void doResetAllLoggers(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } SoapProvisioning sprov = (SoapProvisioning) prov; String server = null; if (args.length > 1 && ("-s".equals(args[1]) || "--server".equals(args[1]))) { server = args.length > 0 ? args[2] : null; } sprov.resetAllLoggers(server); } private void doCreateAccountsBulk(String[] args) throws ServiceException { if (args.length < 3) { usage(); } else { String domain = args[1]; String password = "test123"; String nameMask = args[2]; int numAccounts = Integer.parseInt(args[3]); for (int ix = 0; ix < numAccounts; ix++) { String name = nameMask + Integer.toString(ix) + "@" + domain; Map<String, Object> attrs = new HashMap<String, Object>(); String displayName = nameMask + " N. " + Integer.toString(ix); StringUtil.addToMultiMap(attrs, "displayName", displayName); Account account = prov.createAccount(name, password, attrs); console.println(account.getId()); } } } private Domain doCreateAliasDomain(String aliasDomain, String localDoamin, Map<String, Object> attrs) throws ServiceException { Domain local = lookupDomain(localDoamin); if (!local.isLocal()) { throw ServiceException.INVALID_REQUEST("target domain must be a local domain", null); } attrs.put(Provisioning.A_zimbraDomainType, Provisioning.DomainType.alias.name()); attrs.put(Provisioning.A_zimbraDomainAliasTargetId, local.getId()); return prov.createDomain(aliasDomain, attrs); } private void doGetAccount(String[] args) throws ServiceException { boolean applyDefault = true; int acctPos = 1; if (args[1].equals("-e")) { if (args.length > 1) { applyDefault = false; acctPos = 2; } else { usage(); return; } } dumpAccount(lookupAccount(args[acctPos], true, applyDefault), applyDefault, getArgNameSet(args, acctPos + 1)); } private void doGetAccountMembership(String[] args) throws ServiceException { String key = null; boolean idsOnly = false; if (args.length > 2) { idsOnly = args[1].equals("-i"); key = args[2]; } else { key = args[1]; } Account account = lookupAccount(key); if (idsOnly) { Set<String> lists = prov.getGroups(account); for (String id : lists) { console.println(id); } } else { HashMap<String, String> via = new HashMap<String, String>(); List<Group> groups = prov.getGroups(account, false, via); for (Group group : groups) { String viaDl = via.get(group.getName()); if (viaDl != null) { console.println(group.getName() + " (via " + viaDl + ")"); } else { console.println(group.getName()); } } } } private static class ShareInfoVisitor implements PublishedShareInfoVisitor { private static final String mFormat = "%-36.36s %-15.15s %-15.15s %-5.5s %-20.20s %-10.10s %-10.10s %-10.10s %-5.5s %-5.5s %-36.36s %-15.15s %-15.15s\n"; private static void printHeadings() { console.printf(mFormat, "owner id", "owner email", "owner display", "id", "path", "view", "type", "rights", "mid", "gt", "grantee id", "grantee name", "grantee display"); console.printf(mFormat, "------------------------------------", // owner id "---------------", // owner email "---------------", // owner display "-----", // id "--------------------", // path "----------", // default view "----------", // type "----------", // rights "-----", // mountpoint id if mounted "-----", // grantee type "------------------------------------", // grantee id "---------------", // grantee name "---------------"); // grantee display } @Override public void visit(ShareInfoData shareInfoData) throws ServiceException { console.printf(mFormat, shareInfoData.getOwnerAcctId(), shareInfoData.getOwnerAcctEmail(), shareInfoData.getOwnerAcctDisplayName(), String.valueOf(shareInfoData.getItemId()), shareInfoData.getPath(), shareInfoData.getFolderDefaultView(), shareInfoData.getType().name(), shareInfoData.getRights(), shareInfoData.getMountpointId_zmprov_only(), shareInfoData.getGranteeType(), shareInfoData.getGranteeId(), shareInfoData.getGranteeName(), shareInfoData.getGranteeDisplayName()); } }; private void doGetShareInfo(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } Account owner = lookupAccount(args[1]); ShareInfoVisitor.printHeadings(); prov.getShareInfo(owner, new ShareInfoVisitor()); } private void doGetSpnegoDomain() throws ServiceException { Config config = prov.getConfig(); String spnegoAuthRealm = config.getSpnegoAuthRealm(); if (spnegoAuthRealm != null) { Domain domain = prov.get(Key.DomainBy.krb5Realm, spnegoAuthRealm); if (domain != null) { console.println(domain.getName()); } } } private boolean confirm(String msg) { if (batchMode) { return true; } console.println(msg); console.print("Continue? [Y]es, [N]o: "); BufferedReader in; try { in = new BufferedReader(new InputStreamReader(System.in, "UTF-8")); String line = StringUtil.readLine(in); if ("y".equalsIgnoreCase(line) || "yes".equalsIgnoreCase(line)) { return true; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return false; } private void doDeleteAccount(String[] args) throws ServiceException { if (prov instanceof LdapProv) { boolean confirmed = confirm( "-l option is specified. " + "Only the LDAP entry of the account will be deleted.\n" + "DB data of the account and associated blobs will not be deleted.\n"); if (!confirmed) { console.println("aborted"); return; } } String key = args[1]; Account acct = lookupAccount(key); if (key.equalsIgnoreCase(acct.getId()) || key.equalsIgnoreCase(acct.getName()) || acct.getName().equalsIgnoreCase(key + "@" + acct.getDomainName())) { prov.deleteAccount(acct.getId()); } else { throw ServiceException.INVALID_REQUEST( "argument to deleteAccount must be an account id or the account's primary name", null); } } private void doRenameAccount(String[] args) throws ServiceException { if (prov instanceof LdapProv) { boolean confirmed = confirm( "-l option is specified. " + "Only the LDAP portion of the account will be deleted.\n" + "DB data of the account will not be renamed.\n"); if (!confirmed) { console.println("aborted"); return; } } prov.renameAccount(lookupAccount(args[1]).getId(), args[2]); } private void doGetAccountIdentities(String[] args) throws ServiceException { Account account = lookupAccount(args[1]); Set<String> argNameSet = getArgNameSet(args, 2); for (Identity identity : prov.getAllIdentities(account)) { dumpIdentity(identity, argNameSet); } } private void doGetAccountSignatures(String[] args) throws ServiceException { Account account = lookupAccount(args[1]); Set<String> argNameSet = getArgNameSet(args, 2); for (Signature signature : prov.getAllSignatures(account)) { dumpSignature(signature, argNameSet); } } private void dumpDataSource(DataSource dataSource, Set<String> argNameSet) throws ServiceException { console.println("# name " + dataSource.getName()); console.println("# type " + dataSource.getType()); Map<String, Object> attrs = dataSource.getAttrs(); dumpAttrs(attrs, argNameSet); console.println(); } private void doGetAccountDataSources(String[] args) throws ServiceException { Account account = lookupAccount(args[1]); Set<String> attrNameSet = getArgNameSet(args, 2); for (DataSource dataSource : prov.getAllDataSources(account)) { dumpDataSource(dataSource, attrNameSet); } } private void doGetDistributionListMembership(Group group) throws ServiceException { String[] members; if (group instanceof DynamicGroup) { members = ((DynamicGroup) group).getAllMembers(true); } else { members = group.getAllMembers(); } int count = members == null ? 0 : members.length; console.println("# distributionList " + group.getName() + " memberCount=" + count); console.println(); console.println("members"); for (String member : members) { console.println(member); } } private void doGetConfig(String[] args) throws ServiceException { String key = args[1]; Set<String> needAttr = new HashSet<String>(); needAttr.add(key); dumpAttrs(prov.getConfig(key).getAttrs(), needAttr); } /** * prov is always LdapProv here */ private void doGetAllAccounts(LdapProv ldapProv, Domain domain, Server server, final boolean verbose, final boolean applyDefault, final Set<String> attrNames) throws ServiceException { NamedEntry.Visitor visitor = new NamedEntry.Visitor() { @Override public void visit(com.zimbra.cs.account.NamedEntry entry) throws ServiceException { if (verbose) { dumpAccount((Account) entry, applyDefault, attrNames); } else { console.println(entry.getName()); } } }; SearchAccountsOptions options = new SearchAccountsOptions(); if (domain != null) { options.setDomain(domain); } options.setIncludeType(IncludeType.ACCOUNTS_ONLY); if (!applyDefault) { options.setMakeObjectOpt(MakeObjectOpt.NO_DEFAULTS); } if (server == null) { options.setFilter(ZLdapFilterFactory.getInstance().allAccountsOnly()); ldapProv.searchDirectory(options, visitor); } else { ldapProv.searchAccountsOnServer(server, options, visitor); } } private void doGetAllAccounts(String[] args) throws ServiceException { LdapProv ldapProv = (LdapProv) prov; boolean verbose = false; boolean applyDefault = true; String d = null; String s = null; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-e")) { applyDefault = false; } else if (arg.equals("-s")) { i++; if (i < args.length) { if (s == null) { s = args[i]; } else { console.println("invalid arg: " + args[i] + ", already specified -s with " + s); usage(); return; } } else { usage(); return; } } else { if (d == null) { d = arg; } else { console.println("invalid arg: " + arg + ", already specified domain: " + d); usage(); return; } } i++; } if (!applyDefault && !verbose) { console.println(ERR_INVALID_ARG_EV); usage(); return; } Server server = null; if (s != null) { server = lookupServer(s); } if (d == null) { doGetAllAccounts(ldapProv, null, server, verbose, applyDefault, null); } else { Domain domain = lookupDomain(d, ldapProv); doGetAllAccounts(ldapProv, domain, server, verbose, applyDefault, null); } } private void doSearchAccounts(String[] args) throws ServiceException, ArgException { boolean verbose = false; int i = 1; if (args[i].equals("-v")) { verbose = true; i++; if (args.length < i - 1) { usage(); return; } } if (args.length < i + 1) { usage(); return; } String query = args[i]; Map<String, Object> attrs = getMap(args, i + 1); String limitStr = (String) attrs.get("limit"); int limit = limitStr == null ? Integer.MAX_VALUE : Integer.parseInt(limitStr); String offsetStr = (String) attrs.get("offset"); int offset = offsetStr == null ? 0 : Integer.parseInt(offsetStr); String sortBy = (String) attrs.get("sortBy"); String sortAscending = (String) attrs.get("sortAscending"); boolean isSortAscending = (sortAscending != null) ? "1".equalsIgnoreCase(sortAscending) : true; String[] attrsToGet = null; String typesStr = (String) attrs.get("types"); if (typesStr == null) { typesStr = SearchDirectoryOptions.ObjectType.accounts.name() + "," + SearchDirectoryOptions.ObjectType.aliases.name() + "," + SearchDirectoryOptions.ObjectType.distributionlists.name() + "," + SearchDirectoryOptions.ObjectType.dynamicgroups.name() + "," + SearchDirectoryOptions.ObjectType.resources.name(); } String domainStr = (String) attrs.get("domain"); SearchDirectoryOptions searchOpts = new SearchDirectoryOptions(attrsToGet); if (domainStr != null) { Domain d = lookupDomain(domainStr, prov); searchOpts.setDomain(d); } searchOpts.setTypes(typesStr); searchOpts.setSortOpt(isSortAscending ? SortOpt.SORT_ASCENDING : SortOpt.SORT_DESCENDING); searchOpts.setSortAttr(sortBy); // if LdapClient is not initialized(the case for SoapProvisioning), FilterId // is not initialized. Use null for SoapProvisioning, it will be set to // FilterId.ADMIN_SEARCH in SearchDirectory soap handler. FilterId filterId = (prov instanceof LdapProv) ? FilterId.ADMIN_SEARCH : null; searchOpts.setFilterString(filterId, query); searchOpts.setConvertIDNToAscii(true); // query must be already RFC 2254 escaped List<NamedEntry> accounts = prov.searchDirectory(searchOpts); for (int j = offset; j < offset + limit && j < accounts.size(); j++) { NamedEntry account = accounts.get(j); if (verbose) { if (account instanceof Account) { dumpAccount((Account) account, true, null); } else if (account instanceof Alias) { dumpAlias((Alias) account); } else if (account instanceof DistributionList) { dumpGroup((DistributionList) account, null); } else if (account instanceof Domain) { dumpDomain((Domain) account, null); } } else { console.println(account.getName()); } } } private void doSyncGal(String[] args) throws ServiceException { String domain = args[1]; String token = args.length == 3 ? args[2] : ""; Domain d = lookupDomain(domain); SearchGalResult result = null; if (prov instanceof LdapProv) { GalContact.Visitor visitor = new GalContact.Visitor() { @Override public void visit(GalContact gc) throws ServiceException { dumpContact(gc); } }; result = prov.syncGal(d, token, visitor); } else { result = ((SoapProvisioning) prov).searchGal(d, "", GalSearchType.all, token, 0, 0, null); for (GalContact contact : result.getMatches()) { dumpContact(contact); } } if (result.getToken() != null) { console.println("\n# token = " + result.getToken() + "\n"); } } private void doSearchGal(String[] args) throws ServiceException, ArgException { if (args.length < 3) { usage(); return; } String domain = args[1]; String query = args[2]; Map<String, Object> attrs = getMap(args, 3); String limitStr = (String) attrs.get("limit"); int limit = limitStr == null ? 0 : Integer.parseInt(limitStr); String offsetStr = (String) attrs.get("offset"); int offset = offsetStr == null ? 0 : Integer.parseInt(offsetStr); String sortBy = (String) attrs.get("sortBy"); Domain d = lookupDomain(domain); SearchGalResult result; if (prov instanceof LdapProv) { if (offsetStr != null) { throw ServiceException.INVALID_REQUEST("offset is not supported with -l", null); } if (sortBy != null) { throw ServiceException.INVALID_REQUEST("sortBy is not supported with -l", null); } GalContact.Visitor visitor = new GalContact.Visitor() { @Override public void visit(GalContact gc) throws ServiceException { dumpContact(gc); } }; result = prov.searchGal(d, query, GalSearchType.all, limit, visitor); } else { result = ((SoapProvisioning) prov).searchGal(d, query, GalSearchType.all, null, limit, offset, sortBy); for (GalContact contact : result.getMatches()) { dumpContact(contact); } } } private void doAutoCompleteGal(String[] args) throws ServiceException { String domain = args[1]; String query = args[2]; int limit = 100; Domain d = lookupDomain(domain); GalContact.Visitor visitor = new GalContact.Visitor() { @Override public void visit(GalContact gc) throws ServiceException { dumpContact(gc); } }; SearchGalResult result = prov.autoCompleteGal(d, query, GalSearchType.all, limit, visitor); } private void doCountAccount(String[] args) throws ServiceException { String domain = args[1]; Domain d = lookupDomain(domain); CountAccountResult result = prov.countAccount(d); String formatHeading = "%-20s %-40s %s\n"; String format = "%-20s %-40s %d\n"; console.printf(formatHeading, "cos name", "cos id", "# of accounts"); console.printf(formatHeading, "--------------------", "----------------------------------------", "--------------------"); for (CountAccountResult.CountAccountByCos c : result.getCountAccountByCos()) { console.printf(format, c.getCosName(), c.getCosId(), c.getCount()); } console.println(); } private void doCountObjects(String[] args) throws ServiceException { CountObjectsType type = CountObjectsType.fromString(args[1]); Domain domain = null; UCService ucService = null; int idx = 2; while (args.length > idx) { String arg = args[idx]; if (arg.equals("-d")) { if (domain != null) { throw ServiceException.INVALID_REQUEST("domain is already specified as:" + domain.getName(), null); } idx++; if (args.length <= idx) { usage(); throw ServiceException.INVALID_REQUEST("expecting domain, not enough args", null); } domain = lookupDomain(args[idx]); } else if (arg.equals("-u")) { if (ucService != null) { throw ServiceException .INVALID_REQUEST("UCService is already specified as:" + ucService.getName(), null); } idx++; if (args.length <= idx) { usage(); throw ServiceException.INVALID_REQUEST("expecting UCService, not enough args", null); } ucService = lookupUCService(args[idx]); } else { usage(); return; } idx++; } long result = prov.countObjects(type, domain, ucService); console.println(result); } private void doGetAllAdminAccounts(String[] args) throws ServiceException { boolean verbose = false; boolean applyDefault = true; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-e")) { applyDefault = false; } else { break; } i++; } if (!applyDefault && !verbose) { console.println(ERR_INVALID_ARG_EV); usage(); return; } List<Account> accounts; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; accounts = soapProv.getAllAdminAccounts(applyDefault); } else { accounts = prov.getAllAdminAccounts(); } Set<String> attrNames = getArgNameSet(args, i); for (Account account : accounts) { if (verbose) { dumpAccount(account, applyDefault, attrNames); } else { console.println(account.getName()); } } } private void doGetAllCos(String[] args) throws ServiceException { boolean verbose = args.length > 1 && args[1].equals("-v"); Set<String> attrNames = getArgNameSet(args, verbose ? 2 : 1); List<Cos> allcos = prov.getAllCos(); for (Cos cos : allcos) { if (verbose) { dumpCos(cos, attrNames); } else { console.println(cos.getName()); } } } private void dumpCos(Cos cos, Set<String> attrNames) throws ServiceException { console.println("# name " + cos.getName()); Map<String, Object> attrs = cos.getAttrs(); dumpAttrs(attrs, attrNames); console.println(); } private void doGetAllDomains(String[] args) throws ServiceException { boolean verbose = false; boolean applyDefault = true; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-e")) { applyDefault = false; } else { break; } i++; } if (!applyDefault && !verbose) { console.println(ERR_INVALID_ARG_EV); usage(); return; } Set<String> attrNames = getArgNameSet(args, i); List<Domain> domains; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; domains = soapProv.getAllDomains(applyDefault); } else { domains = prov.getAllDomains(); } for (Domain domain : domains) { if (verbose) { dumpDomain(domain, attrNames); } else { console.println(domain.getName()); } } } private void dumpDomain(Domain domain, Set<String> attrNames) throws ServiceException { dumpDomain(domain, true, attrNames); } private void dumpDomain(Domain domain, boolean expandConfig, Set<String> attrNames) throws ServiceException { console.println("# name " + domain.getName()); Map<String, Object> attrs = domain.getAttrs(expandConfig); dumpAttrs(attrs, attrNames); console.println(); } private void dumpGroup(Group group, Set<String> attrNames) throws ServiceException { String[] members; if (group instanceof DynamicGroup) { members = ((DynamicGroup) group).getAllMembers(true); } else { members = group.getAllMembers(); } int count = members == null ? 0 : members.length; console.println("# distributionList " + group.getName() + " memberCount=" + count); Map<String, Object> attrs = group.getAttrs(); dumpAttrs(attrs, attrNames); console.println(); console.println("members"); for (String member : members) { console.println(member); } } private void dumpAlias(Alias alias) throws ServiceException { console.println("# alias " + alias.getName()); Map<String, Object> attrs = alias.getAttrs(); dumpAttrs(attrs, null); } private void doGetRight(String[] args) throws ServiceException, ArgException { boolean expandComboRight = false; String right = args[1]; if (args.length > 2) { if (args[2].equals("-e")) { expandComboRight = true; } else { throw new ArgException("invalid arguments"); } } dumpRight(lookupRight(right), expandComboRight); } private void doGetAllRights(String[] args) throws ServiceException, ArgException { boolean verbose = false; String targetType = null; String rightClass = null; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-t")) { i++; if (i == args.length) { throw new ArgException("not enough arguments"); } else { targetType = args[i]; } } else if (arg.equals("-c")) { i++; if (i == args.length) { throw new ArgException("not enough arguments"); } else { rightClass = args[i]; } } else { throw new ArgException("invalid arg: " + arg); } i++; } List<Right> allRights = prov.getAllRights(targetType, false, rightClass); for (Right right : allRights) { if (verbose) { dumpRight(right); } else { console.println(right.getName()); } } } private void dumpRight(Right right) { dumpRight(right, true); } private void dumpRight(Right right, boolean expandComboRight) { String tab = " "; String indent = tab; String indent2 = indent + indent; console.println(); console.println("------------------------------"); console.println(right.getName()); console.println(indent + " description: " + right.getDesc()); console.println(indent + " right type: " + right.getRightType().name()); String targetType = right.getTargetTypeStr(); console.println(indent + " target type(s): " + (targetType == null ? "" : targetType)); String grantTargetType = right.getGrantTargetTypeStr(); console.println(indent + "grant target type: " + (grantTargetType == null ? "(default)" : grantTargetType)); console.println(indent + " right class: " + right.getRightClass().name()); if (right.isAttrRight()) { AttrRight attrRight = (AttrRight) right; console.println(); console.println(indent + "attributes:"); if (attrRight.allAttrs()) { console.println(indent2 + "all attributes"); } else { for (String attrName : attrRight.getAttrs()) { console.println(indent2 + attrName); } } } else if (right.isComboRight()) { ComboRight comboRight = (ComboRight) right; console.println(); console.println(indent + "rights:"); dumpComboRight(comboRight, expandComboRight, indent, new HashSet<String>()); } console.println(); Help help = right.getHelp(); if (help != null) { console.println(help.getDesc()); List<String> helpItems = help.getItems(); for (String helpItem : helpItems) { // console.println(FileGenUtil.wrapComments(helpItem, 70, prefix) + "\n"); console.println("- " + helpItem.trim()); console.println(); } } console.println(); } private void dumpComboRight(ComboRight comboRight, boolean expandComboRight, String indent, Set<String> seen) { // safety check, should not happen, // detect circular combo rights if (seen.contains(comboRight.getName())) { console.println("Circular combo right: " + comboRight.getName() + " !!"); return; } String indent2 = indent + indent; for (Right r : comboRight.getRights()) { String tt = r.getTargetTypeStr(); tt = tt == null ? "" : " (" + tt + ")"; // console.format("%s%10.10s: %s %s\n", indent2, r.getRightType().name(), r.getName(), tt); console.format("%s %s: %s %s\n", indent2, r.getRightType().name(), r.getName(), tt); seen.add(comboRight.getName()); if (r.isComboRight() && expandComboRight) { dumpComboRight((ComboRight) r, expandComboRight, indent2, seen); } seen.clear(); } } private void doGetRightsDoc(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } String[] packages; StringBuilder argsDump = new StringBuilder(); if (args.length > 1) { // args[0] is "grd", starting from args[1] packages = new String[args.length - 1]; for (int i = 1; i < args.length; i++) { packages[i - 1] = args[i]; argsDump.append(" " + args[i]); } } else { packages = new String[] { "com.zimbra.cs.service.admin", "com.zimbra.bp", "com.zimbra.cert", "com.zimbra.cs.network", "com.zimbra.cs.network.license.service", "com.zimbra.cs.service.backup", "com.zimbra.cs.service.hsm", "com.zimbra.xmbxsearch" }; } console.println("#"); console.println("# Generated by: zmprov grd" + argsDump); console.println("#"); console.println("# Date: " + DateFormat.getDateInstance(DateFormat.LONG).format(new Date())); console.println("# "); console.println("# Packages:"); for (String pkg : packages) { console.println("# " + pkg); } console.println("# "); console.println("\n"); Map<String, List<RightsDoc>> allDocs = prov.getRightsDoc(packages); for (Map.Entry<String, List<RightsDoc>> docs : allDocs.entrySet()) { console.println("========================================"); console.println("Package: " + docs.getKey()); console.println("========================================"); console.println(); for (RightsDoc doc : docs.getValue()) { console.println("------------------------------"); console.println(doc.getCmd() + "\n"); console.println(" Related rights:"); for (String r : doc.getRights()) { console.println(" " + r); } console.println(); console.println(" Notes:"); for (String n : doc.getNotes()) { console.println(FileGenUtil.wrapComments(StringUtil.escapeHtml(n), 70, " ") + "\n"); } console.println(); } } } private void doGetAllServers(String[] args) throws ServiceException { boolean verbose = false; boolean applyDefault = true; String service = null; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-e")) { applyDefault = false; } else { if (service == null) { service = arg; } else { console.println("invalid arg: " + arg + ", already specified service: " + service); usage(); return; } } i++; } if (!applyDefault && !verbose) { console.println(ERR_INVALID_ARG_EV); usage(); return; } List<Server> servers; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; servers = soapProv.getAllServers(service, applyDefault); } else { servers = prov.getAllServers(service); } for (Server server : servers) { if (verbose) { dumpServer(server, applyDefault, null); } else { console.println(server.getName()); } } } private void doGetAllAlwaysOnClusters(String[] args) throws ServiceException { boolean verbose = false; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } i++; } List<AlwaysOnCluster> clusters; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; clusters = soapProv.getAllAlwaysOnClusters(); } else { clusters = prov.getAllAlwaysOnClusters(); } for (AlwaysOnCluster cluster : clusters) { if (verbose) { dumpAlwaysOnCluster(cluster, null); } else { console.println(cluster.getName()); } } } private void doGetAllActiveServers(String[] args) throws ServiceException { boolean verbose = false; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } i++; } if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } List<Server> servers = ((SoapProvisioning) prov).getAllActiveServers(); for (Server server : servers) { if (verbose) { dumpServer(server, true, null); } else { console.println(server.getName()); } } } private void doGetAllUCServices(String[] args) throws ServiceException { boolean verbose = args.length > 1 && args[1].equals("-v"); Set<String> attrNames = getArgNameSet(args, verbose ? 2 : 1); List<UCService> allUCServices = prov.getAllUCServices(); for (UCService ucService : allUCServices) { if (verbose) { dumpUCService(ucService, attrNames); } else { console.println(ucService.getName()); } } } private void dumpServer(Server server, boolean expandConfig, Set<String> attrNames) throws ServiceException { console.println("# name " + server.getName()); Map<String, Object> attrs = server.getAttrs(expandConfig); dumpAttrs(attrs, attrNames); console.println(); } private void dumpAlwaysOnCluster(AlwaysOnCluster cluster, Set<String> attrNames) throws ServiceException { console.println("# name " + cluster.getName()); Map<String, Object> attrs = cluster.getAttrs(); dumpAttrs(attrs, attrNames); console.println(); } private void dumpUCService(UCService ucService, Set<String> attrNames) throws ServiceException { console.println("# name " + ucService.getName()); Map<String, Object> attrs = ucService.getAttrs(); dumpAttrs(attrs, attrNames); console.println(); } private void dumpXMPPComponent(XMPPComponent comp, Set<String> attrNames) throws ServiceException { console.println("# name " + comp.getName()); Map<String, Object> attrs = comp.getAttrs(); dumpAttrs(attrs, attrNames); console.println(); } private void doGetAllXMPPComponents() throws ServiceException { List<XMPPComponent> components = prov.getAllXMPPComponents(); for (XMPPComponent comp : components) { dumpXMPPComponent(comp, null); } } private void dumpAccount(Account account, boolean expandCos, Set<String> attrNames) throws ServiceException { console.println("# name " + account.getName()); Map<String, Object> attrs = account.getAttrs(expandCos); dumpAttrs(attrs, attrNames); console.println(); } private void dumpCalendarResource(CalendarResource resource, boolean expandCos, Set<String> attrNames) throws ServiceException { console.println("# name " + resource.getName()); Map<String, Object> attrs = resource.getAttrs(expandCos); dumpAttrs(attrs, attrNames); console.println(); } private void dumpContact(GalContact contact) throws ServiceException { console.println("# name " + contact.getId()); Map<String, Object> attrs = contact.getAttrs(); dumpAttrs(attrs, null); console.println(); } private void dumpIdentity(Identity identity, Set<String> attrNameSet) throws ServiceException { console.println("# name " + identity.getName()); Map<String, Object> attrs = identity.getAttrs(); dumpAttrs(attrs, attrNameSet); console.println(); } private void dumpSignature(Signature signature, Set<String> attrNameSet) throws ServiceException { console.println("# name " + signature.getName()); Map<String, Object> attrs = signature.getAttrs(); dumpAttrs(attrs, attrNameSet); console.println(); } private void dumpAttrs(Map<String, Object> attrsIn, Set<String> specificAttrs) throws ServiceException { TreeMap<String, Object> attrs = new TreeMap<String, Object>(attrsIn); Map<String, Set<String>> specificAttrValues = null; if (specificAttrs != null) { specificAttrValues = new HashMap<String, Set<String>>(); for (String specificAttr : specificAttrs) { int colonAt = specificAttr.indexOf("="); String attrName = null; String attrValue = null; if (colonAt == -1) { attrName = specificAttr; } else { attrName = specificAttr.substring(0, colonAt); attrValue = specificAttr.substring(colonAt + 1); if (attrValue.length() < 1) { throw ServiceException.INVALID_REQUEST("missing value for " + specificAttr, null); } } attrName = attrName.toLowerCase(); Set<String> values = specificAttrValues.get(attrName); if (values == null) { // haven't seen the attr yet values = new HashSet<String>(); } if (attrValue != null) { values.add(attrValue); } specificAttrValues.put(attrName, values); } } AttributeManager attrMgr = AttributeManager.getInstance(); SimpleDateFormat dateFmt = new SimpleDateFormat("yyyyMMddHHmmss"); String timestamp = dateFmt.format(new Date()); for (Map.Entry<String, Object> entry : attrs.entrySet()) { String name = entry.getKey(); boolean isBinary = needsBinaryIO(attrMgr, name); Set<String> specificValues = null; if (specificAttrValues != null) { specificValues = specificAttrValues.get(name.toLowerCase()); } if (specificAttrValues == null || specificAttrValues.keySet().contains(name.toLowerCase())) { Object value = entry.getValue(); if (value instanceof String[]) { String sv[] = (String[]) value; for (int i = 0; i < sv.length; i++) { String aSv = sv[i]; // don't print permission denied attr if (this.forceDisplayAttrValue || aSv.length() > 0 && (specificValues == null || specificValues.isEmpty() || specificValues.contains(aSv))) { printAttr(name, aSv, i, isBinary, timestamp); } } } else if (value instanceof String) { // don't print permission denied attr if (this.forceDisplayAttrValue || ((String) value).length() > 0 && (specificValues == null || specificValues.isEmpty() || specificValues.contains(value))) { printAttr(name, (String) value, null, isBinary, timestamp); } } } } // force display empty value attribute if (this.forceDisplayAttrValue) { for (String attr : specificAttrs) { if (!attrs.containsKey(attr)) { AttributeInfo ai = attrMgr.getAttributeInfo(attr); if (ai != null) { printAttr(attr, "", null, false, timestamp); } } } } } private void doCreateDistributionListsBulk(String[] args) throws ServiceException { if (args.length < 3) { usage(); } else { String domain = args[1]; String nameMask = args[2]; int numAccounts = Integer.parseInt(args[3]); for (int i = 0; i < numAccounts; i++) { String name = nameMask + Integer.toString(i) + "@" + domain; Map<String, Object> attrs = new HashMap<String, Object>(); String displayName = nameMask + " N. " + Integer.toString(i); StringUtil.addToMultiMap(attrs, "displayName", displayName); DistributionList dl = prov.createDistributionList(name, attrs); console.println(dl.getId()); } } } private void doGetAllDistributionLists(String[] args) throws ServiceException { String d = null; boolean verbose = false; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else { if (d == null) { d = arg; } else { console.println("invalid arg: " + arg + ", already specified domain: " + d); usage(); return; } } i++; } if (d == null) { List<Domain> domains = prov.getAllDomains(); for (Domain domain : domains) { Collection<?> dls = prov.getAllGroups(domain); for (Object obj : dls) { Group dl = (Group) obj; if (verbose) { dumpGroup(dl, null); } else { console.println(dl.getName()); } } } } else { Domain domain = lookupDomain(d); Collection<?> dls = prov.getAllGroups(domain); for (Object obj : dls) { Group dl = (Group) obj; if (verbose) { dumpGroup(dl, null); } else { console.println(dl.getName()); } } } } private void doGetAllCalendarResources(String[] args) throws ServiceException { boolean verbose = false; boolean applyDefault = true; String d = null; String s = null; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-v")) { verbose = true; } else if (arg.equals("-e")) { applyDefault = false; } else if (arg.equals("-s")) { i++; if (i < args.length) { if (s == null) { s = args[i]; } else { console.println("invalid arg: " + args[i] + ", already specified -s with " + s); usage(); return; } } else { usage(); return; } } else { if (d == null) { d = arg; } else { console.println("invalid arg: " + arg + ", already specified domain: " + d); usage(); return; } } i++; } if (!applyDefault && !verbose) { console.println(ERR_INVALID_ARG_EV); usage(); return; } // always use LDAP Provisioning prov = Provisioning.getInstance(); Server server = null; if (s != null) { server = lookupServer(s); } if (d == null) { List<Domain> domains = prov.getAllDomains(); for (Domain domain : domains) { doGetAllCalendarResources(prov, domain, server, verbose, applyDefault); } } else { Domain domain = lookupDomain(d, prov); doGetAllCalendarResources(prov, domain, server, verbose, applyDefault); } } private void doGetAllCalendarResources(Provisioning prov, Domain domain, Server server, final boolean verbose, final boolean applyDefault) throws ServiceException { NamedEntry.Visitor visitor = new NamedEntry.Visitor() { @Override public void visit(com.zimbra.cs.account.NamedEntry entry) throws ServiceException { if (verbose) { dumpCalendarResource((CalendarResource) entry, applyDefault, null); } else { console.println(entry.getName()); } } }; prov.getAllCalendarResources(domain, server, visitor); } private void doSearchCalendarResources(String[] args) throws ServiceException { boolean verbose = false; int i = 1; if (args.length < i + 1) { usage(); return; } if (args[i].equals("-v")) { verbose = true; i++; } if (args.length < i + 1) { usage(); return; } Domain d = lookupDomain(args[i++]); if ((args.length - i) % 3 != 0) { usage(); return; } EntrySearchFilter.Multi multi = new EntrySearchFilter.Multi(false, EntrySearchFilter.AndOr.and); for (; i < args.length;) { String attr = args[i++]; String op = args[i++]; String value = args[i++]; try { EntrySearchFilter.Single single = new EntrySearchFilter.Single(false, attr, op, value); multi.add(single); } catch (IllegalArgumentException e) { printError("Bad search op in: " + attr + " " + op + " '" + value + "'"); e.printStackTrace(); usage(); return; } } EntrySearchFilter filter = new EntrySearchFilter(multi); String filterStr = LdapEntrySearchFilter.toLdapCalendarResourcesFilter(filter); SearchDirectoryOptions searchOpts = new SearchDirectoryOptions(); searchOpts.setDomain(d); searchOpts.setTypes(ObjectType.resources); searchOpts.setSortOpt(SortOpt.SORT_ASCENDING); searchOpts.setFilterString(FilterId.ADMIN_SEARCH, filterStr); List<NamedEntry> resources = prov.searchDirectory(searchOpts); // List<NamedEntry> resources = prov.searchCalendarResources(d, filter, null, null, true); for (NamedEntry entry : resources) { CalendarResource resource = (CalendarResource) entry; if (verbose) { dumpCalendarResource(resource, true, null); } else { console.println(resource.getName()); } } } private Account lookupAccount(String key, boolean mustFind, boolean applyDefault) throws ServiceException { Account account; if (applyDefault == true || (prov instanceof LdapProv)) { account = prov.getAccount(key); } else { /* * oops, do not apply default, and we are SoapProvisioning * * This a bit awkward because the applyDefault is controlled at the Entry.getAttrs, not at the provisioning * interface. But for SOAP, this needs to be passed to the get(AccountBy) method so it can set the flag in * SOAP. We do not want to add a provisioning method for this. Instead, we make it a SOAPProvisioning only * method. */ SoapProvisioning soapProv = (SoapProvisioning) prov; account = soapProv.getAccount(key, applyDefault); } if (mustFind && account == null) { throw AccountServiceException.NO_SUCH_ACCOUNT(key); } else { return account; } } private Account lookupAccount(String key) throws ServiceException { return lookupAccount(key, true, true); } private Account lookupAccount(String key, boolean mustFind) throws ServiceException { return lookupAccount(key, mustFind, true); } private CalendarResource lookupCalendarResource(String key) throws ServiceException { CalendarResource res = prov.get(guessCalendarResourceBy(key), key); if (res == null) { throw AccountServiceException.NO_SUCH_CALENDAR_RESOURCE(key); } else { return res; } } private Domain lookupDomain(String key) throws ServiceException { return lookupDomain(key, prov); } private Domain lookupDomain(String key, Provisioning prov) throws ServiceException { return lookupDomain(key, prov, true); } private Domain lookupDomain(String key, Provisioning prov, boolean applyDefault) throws ServiceException { Domain domain; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; domain = soapProv.get(guessDomainBy(key), key, applyDefault); } else { domain = prov.get(guessDomainBy(key), key); } if (domain == null) { throw AccountServiceException.NO_SUCH_DOMAIN(key); } else { return domain; } } private Cos lookupCos(String key) throws ServiceException { Cos cos = prov.get(guessCosBy(key), key); if (cos == null) { throw AccountServiceException.NO_SUCH_COS(key); } else { return cos; } } private Right lookupRight(String rightName) throws ServiceException { return prov.getRight(rightName, false); } private Server lookupServer(String key) throws ServiceException { return lookupServer(key, true); } private Server lookupServer(String key, boolean applyDefault) throws ServiceException { Server server; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; server = soapProv.get(guessServerBy(key), key, applyDefault); } else { server = prov.get(guessServerBy(key), key); } if (server == null) { throw AccountServiceException.NO_SUCH_SERVER(key); } else { return server; } } private AlwaysOnCluster lookupAlwaysOnCluster(String key) throws ServiceException { AlwaysOnCluster cluster; if (prov instanceof SoapProvisioning) { SoapProvisioning soapProv = (SoapProvisioning) prov; cluster = soapProv.get(guessAlwaysOnClusterBy(key), key); } else { cluster = prov.get(guessAlwaysOnClusterBy(key), key); } if (cluster == null) { throw AccountServiceException.NO_SUCH_ALWAYSONCLUSTER(key); } else { return cluster; } } private UCService lookupUCService(String key) throws ServiceException { UCService ucService = prov.get(guessUCServiceBy(key), key); if (ucService == null) { throw AccountServiceException.NO_SUCH_UC_SERVICE(key); } else { return ucService; } } private String lookupDataSourceId(Account account, String key) throws ServiceException { if (Provisioning.isUUID(key)) { return key; } DataSource ds = prov.get(account, Key.DataSourceBy.name, key); if (ds == null) { throw AccountServiceException.NO_SUCH_DATA_SOURCE(key); } else { return ds.getId(); } } private String lookupSignatureId(Account account, String key) throws ServiceException { Signature sig = prov.get(account, guessSignatureBy(key), key); if (sig == null) { throw AccountServiceException.NO_SUCH_SIGNATURE(key); } else { return sig.getId(); } } private DistributionList lookupDistributionList(String key, boolean mustFind) throws ServiceException { DistributionList dl = prov.get(guessDistributionListBy(key), key); if (mustFind && dl == null) { throw AccountServiceException.NO_SUCH_DISTRIBUTION_LIST(key); } else { return dl; } } private DistributionList lookupDistributionList(String key) throws ServiceException { return lookupDistributionList(key, true); } private Group lookupGroup(String key, boolean mustFind) throws ServiceException { Group dl = prov.getGroup(guessDistributionListBy(key), key); if (mustFind && dl == null) { throw AccountServiceException.NO_SUCH_DISTRIBUTION_LIST(key); } else { return dl; } } private Group lookupGroup(String key) throws ServiceException { return lookupGroup(key, true); } private XMPPComponent lookupXMPPComponent(String value) throws ServiceException { if (Provisioning.isUUID(value)) { return prov.get(Key.XMPPComponentBy.id, value); } else { return prov.get(Key.XMPPComponentBy.name, value); } } public static AccountBy guessAccountBy(String value) { if (Provisioning.isUUID(value)) { return AccountBy.id; } return AccountBy.name; } public static Key.CosBy guessCosBy(String value) { if (Provisioning.isUUID(value)) { return Key.CosBy.id; } return Key.CosBy.name; } public static Key.DomainBy guessDomainBy(String value) { if (Provisioning.isUUID(value)) { return Key.DomainBy.id; } return Key.DomainBy.name; } public static Key.ServerBy guessServerBy(String value) { if (Provisioning.isUUID(value)) { return Key.ServerBy.id; } return Key.ServerBy.name; } public static Key.AlwaysOnClusterBy guessAlwaysOnClusterBy(String value) { if (Provisioning.isUUID(value)) { return Key.AlwaysOnClusterBy.id; } return Key.AlwaysOnClusterBy.name; } public static Key.UCServiceBy guessUCServiceBy(String value) { if (Provisioning.isUUID(value)) { return Key.UCServiceBy.id; } return Key.UCServiceBy.name; } public static Key.CalendarResourceBy guessCalendarResourceBy(String value) { if (Provisioning.isUUID(value)) { return Key.CalendarResourceBy.id; } return Key.CalendarResourceBy.name; } public static Key.DistributionListBy guessDistributionListBy(String value) { if (Provisioning.isUUID(value)) { return Key.DistributionListBy.id; } return Key.DistributionListBy.name; } public static Key.SignatureBy guessSignatureBy(String value) { if (Provisioning.isUUID(value)) { return Key.SignatureBy.id; } return Key.SignatureBy.name; } public static TargetBy guessTargetBy(String value) { if (Provisioning.isUUID(value)) { return TargetBy.id; } return TargetBy.name; } public static GranteeBy guessGranteeBy(String value) { if (Provisioning.isUUID(value)) { return GranteeBy.id; } return GranteeBy.name; } private void checkDeprecatedAttrs(Map<String, ? extends Object> attrs) throws ServiceException { AttributeManager am = AttributeManager.getInstance(); boolean hadWarnings = false; for (String attr : attrs.keySet()) { AttributeInfo ai = am.getAttributeInfo(attr); if (ai == null) { continue; } if (ai.isDeprecated()) { hadWarnings = true; console.println( "Warn: attribute " + attr + " has been deprecated since " + ai.getDeprecatedSince()); } } if (hadWarnings) { console.println(); } } private static boolean needsBinaryIO(AttributeManager attrMgr, String attr) { return attrMgr.containsBinaryData(attr); } /** * get map and check/warn deprecated attrs. */ private Map<String, Object> getMapAndCheck(String[] args, int offset, boolean isCreateCmd) throws ArgException, ServiceException { Map<String, Object> attrs = getAttrMap(args, offset, isCreateCmd); checkDeprecatedAttrs(attrs); return attrs; } /** * Convert an array of the form: * * a1 v1 a2 v2 a2 v3 * * to a map of the form: * * a1 -> v1 a2 -> [v2, v3] * * For binary attribute, the argument following an attribute name will be treated as a file path and value for the * attribute will be the base64 encoded string of the content of the file. */ private Map<String, Object> keyValueArrayToMultiMap(String[] args, int offset, boolean isCreateCmd) throws IOException, ServiceException { AttributeManager attrMgr = AttributeManager.getInstance(); Map<String, Object> attrs = new HashMap<String, Object>(); String safeguarded_attrs_prop = LC.get("zmprov_safeguarded_attrs"); Set<String> safeguarded_attrs = safeguarded_attrs_prop == null ? Sets.<String>newHashSet() : Sets.newHashSet(safeguarded_attrs_prop.toLowerCase().split(",")); Multiset<String> multiValAttrsToCheck = HashMultiset.create(); for (int i = offset; i < args.length; i += 2) { String n = args[i]; if (i + 1 >= args.length) { throw new IllegalArgumentException("not enough arguments"); } String v = args[i + 1]; String attrName = n; if (n.charAt(0) == '+' || n.charAt(0) == '-') { attrName = attrName.substring(1); } else if (safeguarded_attrs.contains(attrName.toLowerCase()) && attrMgr.isMultiValued(attrName)) { multiValAttrsToCheck.add(attrName.toLowerCase()); } if (needsBinaryIO(attrMgr, attrName) && v.length() > 0) { File file = new File(v); byte[] bytes = ByteUtil.getContent(file); v = ByteUtil.encodeLDAPBase64(bytes); } StringUtil.addToMultiMap(attrs, n, v); } if (!allowMultiValuedAttrReplacement && !isCreateCmd) { for (Multiset.Entry<String> entry : multiValAttrsToCheck.entrySet()) { if (entry.getCount() == 1) { // If multiple values are being assigned to an attr as part of the same command // then we don't consider it an unsafe replacement printError("error: cannot replace multi-valued attr value unless -r is specified"); System.exit(2); } } } return attrs; } private Map<String, Object> getAttrMap(String[] args, int offset, boolean isCreateCmd) throws ArgException, ServiceException { try { return keyValueArrayToMultiMap(args, offset, isCreateCmd); } catch (IllegalArgumentException iae) { throw new ArgException("not enough arguments"); } catch (IOException ioe) { throw ServiceException.INVALID_REQUEST("unable to process arguments", ioe); } } private Map<String, Object> getMap(String[] args, int offset) throws ArgException { try { return StringUtil.keyValueArrayToMultiMap(args, offset); } catch (IllegalArgumentException iae) { throw new ArgException("not enough arguments"); } } private Set<String> getArgNameSet(String[] args, int offset) { if (offset >= args.length) { return null; } Set<String> result = new HashSet<String>(); for (int i = offset; i < args.length; i++) { result.add(args[i].toLowerCase()); } return result; } private void interactive(BufferedReader in) throws IOException { cliReader = in; interactiveMode = true; while (true) { console.print("prov> "); String line = StringUtil.readLine(in); if (line == null) { break; } if (verboseMode) { console.println(line); } String args[] = StringUtil.parseLine(line); if (args.length == 0) { continue; } try { if (!execute(args)) { console.println("Unknown command. Type: 'help commands' for a list"); } } catch (ServiceException e) { Throwable cause = e.getCause(); errorOccursDuringInteraction = true; String errText = "ERROR: " + e.getCode() + " (" + e.getMessage() + ")" + (cause == null ? "" : " (cause: " + cause.getClass().getName() + " " + cause.getMessage() + ")"); printError(errText); if (verboseMode) { e.printStackTrace(errConsole); } } catch (ArgException e) { usage(); } } } /** * Output binary attribute to file. * * value is written to: {LC.zmprov_tmp_directory}/{attr-name}[_{index-if-multi-valued}]{timestamp} * * e.g. /opt/zimbra/data/tmp/zmprov/zimbraFoo_20110202161621 /opt/zimbra/data/tmp/zmprov/zimbraBar_0_20110202161507 * /opt/zimbra/data/tmp/zmprov/zimbraBar_1_20110202161507 */ private void outputBinaryAttrToFile(String attrName, Integer idx, byte[] value, String timestamp) throws ServiceException { StringBuilder sb = new StringBuilder(LC.zmprov_tmp_directory.value()); sb.append(File.separator).append(attrName); if (idx != null) { sb.append("_" + idx); } sb.append("_" + timestamp); File file = new File(sb.toString()); if (file.exists()) { file.delete(); } try { FileUtil.ensureDirExists(file.getParentFile()); } catch (IOException e) { throw ServiceException.FAILURE("Unable to create directory " + file.getParentFile().getAbsolutePath(), e); } try { ByteUtil.putContent(file.getAbsolutePath(), value); } catch (IOException e) { throw ServiceException.FAILURE("Unable to write to file " + file.getAbsolutePath(), e); } } private void printAttr(String attrName, String value, Integer idx, boolean isBinary, String timestamp) throws ServiceException { if (isBinary) { byte[] binary = ByteUtil.decodeLDAPBase64(value); if (outputBinaryToFile()) { outputBinaryAttrToFile(attrName, idx, binary, timestamp); } else { // print base64 encoded content // follow ldapsearch notion of using two colons when printing base64 encoded data // re-encode into 76 character blocks String based64Chunked = new String(Base64.encodeBase64Chunked(binary)); // strip off the \n at the end if (based64Chunked.charAt(based64Chunked.length() - 1) == '\n') { based64Chunked = based64Chunked.substring(0, based64Chunked.length() - 1); } printOutput(attrName + ":: " + based64Chunked); } } else { printOutput(attrName + ": " + value); } } private static void printError(String text) { PrintStream ps = errConsole; try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ps, Charsets.UTF_8)); writer.write(text + "\n"); writer.flush(); } catch (IOException e) { ps.println(text); } } private static void printOutput(String text) { PrintStream ps = console; try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(ps, Charsets.UTF_8)); writer.write(text + "\n"); writer.flush(); } catch (IOException e) { ps.println(text); } } public static void main(String args[]) throws IOException, ServiceException { CliUtil.setCliSoapHttpTransportTimeout(); ZimbraLog.toolSetupLog4jConsole("INFO", true, false); // send all logs to stderr SocketFactories.registerProtocols(); SoapTransport.setDefaultUserAgent("zmprov", BuildInfo.VERSION); ProvUtil pu = new ProvUtil(); CommandLineParser parser = new PosixParser(); Options options = new Options(); options.addOption("h", "help", false, "display usage"); options.addOption("f", "file", true, "use file as input stream"); options.addOption("s", "server", true, "host[:port] of server to connect to"); options.addOption("l", "ldap", false, "provision via LDAP"); options.addOption("L", "logpropertyfile", true, "log4j property file"); options.addOption("a", "account", true, "account name (not used with --ldap)"); options.addOption("p", "password", true, "password for account"); options.addOption("P", "passfile", true, "filename with password in it"); options.addOption("z", "zadmin", false, "use zimbra admin name/password from localconfig for account/password"); options.addOption("v", "verbose", false, "verbose mode"); options.addOption("d", "debug", false, "debug mode (SOAP request and response payload)"); options.addOption("D", "debughigh", false, "debug mode (SOAP req/resp payload and http headers)"); options.addOption("m", "master", false, "use LDAP master (has to be used with --ldap)"); options.addOption("t", "temp", false, "write binary values to files in temporary directory specified in localconfig key zmprov_tmp_directory"); options.addOption("r", "replace", false, "allow replacement of multi-valued attr value"); options.addOption("fd", "forcedisplay", false, "force display attr value"); options.addOption(SoapCLI.OPT_AUTHTOKEN); options.addOption(SoapCLI.OPT_AUTHTOKENFILE); CommandLine cl = null; boolean err = false; try { cl = parser.parse(options, args, true); } catch (ParseException pe) { printError("error: " + pe.getMessage()); err = true; } if (err || cl.hasOption('h')) { pu.usage(); } if (cl.hasOption('l') && cl.hasOption('s')) { printError("error: cannot specify both -l and -s at the same time"); System.exit(2); } pu.setVerbose(cl.hasOption('v')); if (cl.hasOption('l')) { pu.setUseLdap(true, cl.hasOption('m')); } if (cl.hasOption('L')) { if (cl.hasOption('l')) { ZimbraLog.toolSetupLog4j("INFO", cl.getOptionValue('L')); } else { printError("error: cannot specify -L when -l is not specified"); System.exit(2); } } if (cl.hasOption('z')) { pu.setAccount(LC.zimbra_ldap_user.value()); pu.setPassword(LC.zimbra_ldap_password.value()); } if (cl.hasOption(SoapCLI.O_AUTHTOKEN) && cl.hasOption(SoapCLI.O_AUTHTOKENFILE)) { printError("error: cannot specify " + SoapCLI.O_AUTHTOKEN + " when " + SoapCLI.O_AUTHTOKENFILE + " is specified"); System.exit(2); } if (cl.hasOption(SoapCLI.O_AUTHTOKEN)) { ZAuthToken zat = ZAuthToken.fromJSONString(cl.getOptionValue(SoapCLI.O_AUTHTOKEN)); pu.setAuthToken(zat); } if (cl.hasOption(SoapCLI.O_AUTHTOKENFILE)) { String authToken = StringUtil.readSingleLineFromFile(cl.getOptionValue(SoapCLI.O_AUTHTOKENFILE)); ZAuthToken zat = ZAuthToken.fromJSONString(authToken); pu.setAuthToken(zat); } if (cl.hasOption('s')) { pu.setServer(cl.getOptionValue('s')); } if (cl.hasOption('a')) { pu.setAccount(cl.getOptionValue('a')); } if (cl.hasOption('p')) { pu.setPassword(cl.getOptionValue('p')); } if (cl.hasOption('P')) { pu.setPassword(StringUtil.readSingleLineFromFile(cl.getOptionValue('P'))); } if (cl.hasOption('d') && cl.hasOption('D')) { printError("error: cannot specify both -d and -D at the same time"); System.exit(2); } if (cl.hasOption('D')) { pu.setDebug(SoapDebugLevel.high); } else if (cl.hasOption('d')) { pu.setDebug(SoapDebugLevel.normal); } if (!pu.useLdap() && cl.hasOption('m')) { printError("error: cannot specify -m when -l is not specified"); System.exit(2); } if (cl.hasOption('t')) { pu.setOutputBinaryToFile(true); } if (cl.hasOption('r')) { pu.setAllowMultiValuedAttrReplacement(true); } if (cl.hasOption("fd")) { pu.setForceDisplayAttrValue(true); } args = recombineDecapitatedAttrs(cl.getArgs(), options, args); try { if (args.length < 1) { pu.initProvisioning(); InputStream is = null; if (cl.hasOption('f')) { pu.setBatchMode(true); is = new FileInputStream(cl.getOptionValue('f')); } else { if (LC.command_line_editing_enabled.booleanValue()) { try { CliUtil.enableCommandLineEditing(LC.zimbra_home.value() + "/.zmprov_history"); } catch (IOException e) { errConsole.println("Command line editing will be disabled: " + e); if (pu.verboseMode) { e.printStackTrace(errConsole); } } } // This has to happen last because JLine modifies System.in. is = System.in; } pu.interactive(new BufferedReader(new InputStreamReader(is, "UTF-8"))); } else { Command cmd = pu.lookupCommand(args[0]); if (cmd == null) { pu.usage(); } if (cmd.isDeprecated()) { pu.deprecated(); } if (pu.forceLdapButDontRequireUseLdapOption(cmd)) { pu.setUseLdap(true, false); } if (pu.needProvisioningInstance(cmd)) { pu.initProvisioning(); } try { if (!pu.execute(args)) { pu.usage(); } } catch (ArgException e) { pu.usage(); } } } catch (ServiceException e) { Throwable cause = e.getCause(); String errText = "ERROR: " + e.getCode() + " (" + e.getMessage() + ")" + (cause == null ? "" : " (cause: " + cause.getClass().getName() + " " + cause.getMessage() + ")"); printError(errText); if (pu.verboseMode) { e.printStackTrace(errConsole); } System.exit(2); } } class ArgException extends Exception { ArgException(String msg) { super(msg); } } private static class DescribeArgs { enum Field { type, // attribute type value, // value for enum or regex attributes callback, // class name of AttributeCallback object to invoke on changes to attribute. immutable, // whether this attribute can be modified directly cardinality, // single or multi requiredIn, // comma-seperated list containing classes in which this attribute is required optionalIn, // comma-seperated list containing classes in which this attribute can appear flags, // attribute flags defaults, // default value on global config or default COS(for new install) and all upgraded COS's min, // min value for integers and durations. defaults to Integer.MIN_VALUE" max, // max value for integers and durations, max length for strings/email, defaults to Integer.MAX_VALUE id, // leaf OID of the attribute requiresRestart, // server(s) need be to restarted after changing this attribute since, // version since which the attribute had been introduced deprecatedSince; // version since which the attribute had been deprecaed static String formatDefaults(AttributeInfo ai) { StringBuilder sb = new StringBuilder(); for (String d : ai.getDefaultCosValues()) { sb.append(d + ","); } for (String d : ai.getGlobalConfigValues()) { sb.append(d + ","); } return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); // trim the ending , } static String formatRequiredIn(AttributeInfo ai) { Set<AttributeClass> requiredIn = ai.getRequiredIn(); if (requiredIn == null) { return ""; } StringBuilder sb = new StringBuilder(); for (AttributeClass ac : requiredIn) { sb.append(ac.name() + ","); } return sb.substring(0, sb.length() - 1); // trim the ending , } static String formatOptionalIn(AttributeInfo ai) { Set<AttributeClass> optionalIn = ai.getOptionalIn(); if (optionalIn == null) { return ""; } StringBuilder sb = new StringBuilder(); for (AttributeClass ac : optionalIn) { sb.append(ac.name() + ","); } return sb.substring(0, sb.length() - 1); // trim the ending , } static String formatFlags(AttributeInfo ai) { StringBuilder sb = new StringBuilder(); for (AttributeFlag f : AttributeFlag.values()) { if (ai.hasFlag(f)) { sb.append(f.name() + ","); } } return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); // trim the ending , } static String formatRequiresRestart(AttributeInfo ai) { StringBuilder sb = new StringBuilder(); List<AttributeServerType> requiresRetstart = ai.getRequiresRestart(); if (requiresRetstart != null) { for (AttributeServerType ast : requiresRetstart) { sb.append(ast.name() + ","); } } return sb.length() == 0 ? "" : sb.substring(0, sb.length() - 1); // trim the ending , } static String print(Field field, AttributeInfo ai) { String out = null; switch (field) { case type: out = ai.getType().getName(); break; case value: out = ai.getValue(); break; case callback: AttributeCallback acb = ai.getCallback(); if (acb != null) { out = acb.getClass().getSimpleName(); } break; case immutable: out = Boolean.toString(ai.isImmutable()); break; case cardinality: AttributeCardinality card = ai.getCardinality(); if (card != null) { out = card.name(); } break; case requiredIn: out = formatRequiredIn(ai); break; case optionalIn: out = formatOptionalIn(ai); break; case flags: out = formatFlags(ai); break; case defaults: out = formatDefaults(ai); break; case min: long min = ai.getMin(); if (min != Long.MIN_VALUE && min != Integer.MIN_VALUE) { out = Long.toString(min); } break; case max: long max = ai.getMax(); if (max != Long.MAX_VALUE && max != Integer.MAX_VALUE) { out = Long.toString(max); } break; case id: int id = ai.getId(); if (id != -1) { out = Integer.toString(ai.getId()); } break; case requiresRestart: out = formatRequiresRestart(ai); break; case since: List<Version> since = ai.getSince(); if (since != null) { StringBuilder sb = new StringBuilder(); for (Version version : since) { sb.append(version.toString()).append(","); } sb.setLength(sb.length() - 1); out = sb.toString(); } break; case deprecatedSince: Version depreSince = ai.getDeprecatedSince(); if (depreSince != null) { out = depreSince.toString(); } break; } if (out == null) { out = ""; } return out; } } /* * args when an object class is specified */ boolean mNonInheritedOnly; boolean mOnThisObjectTypeOnly; AttributeClass mAttrClass; boolean mVerbose; /* * args when a specific attribute is specified */ String mAttr; } static String formatLine(int width) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < width; i++) { sb.append("-"); } return sb.toString(); } private String formatAllEntryTypes() { StringBuilder sb = new StringBuilder(); for (AttributeClass ac : AttributeClass.values()) { if (ac.isProvisionable()) { sb.append(ac.name() + ","); } } return sb.substring(0, sb.length() - 1); // trim the ending , } private void descAttrsUsage(Exception e) { console.println(e.getMessage() + "\n"); console.printf("usage: %s(%s) %s\n", command.getName(), command.getAlias(), command.getHelp()); console.println(); console.println("Valid entry types: " + formatAllEntryTypes() + "\n"); console.println("Examples:"); console.println("zmprov desc"); console.println(" print attribute name of all attributes" + "\n"); console.println("zmprov desc -v"); console.println(" print attribute name and description of all attributes" + "\n"); console.println("zmprov desc account"); console.println(" print attribute name of all account attributes" + "\n"); console.println("zmprov desc -ni -v account"); console.println(" print attribute name and description of all non-inherited account attributes, "); console.println(" that is, attributes that are on account but not on cos" + "\n"); console.println("zmprov desc -ni domain"); console.println(" print attribute name of all non-inherited domain attributes, "); console.println(" that is, attributes that are on domain but not on global config" + "\n"); /* * -only is *not* a documented option, we could expose it if we want, handy for engineering tasks, not as useful * for users * * console.println("zmprov desc -only globalConfig"); * console.println(" print attribute name of all attributes that are on global config only" + "\n"); */ console.println("zmprov desc -a zimbraId"); console.println(" print attribute name, description, and all properties of attribute zimbraId\n"); console.println("zmprov desc account -a zimbraId"); console.println(" error: can only specify either an entry type or a specific attribute\n"); usage(); } private DescribeArgs parseDescribeArgs(String[] args) throws ServiceException { DescribeArgs descArgs = new DescribeArgs(); int i = 1; while (i < args.length) { if ("-v".equals(args[i])) { if (descArgs.mAttr != null) { throw ServiceException.INVALID_REQUEST("cannot specify -v when -a is specified", null); } descArgs.mVerbose = true; } else if (args[i].startsWith("-ni")) { if (descArgs.mAttr != null) { throw ServiceException.INVALID_REQUEST("cannot specify -ni when -a is specified", null); } descArgs.mNonInheritedOnly = true; } else if (args[i].startsWith("-only")) { if (descArgs.mAttr != null) { throw ServiceException.INVALID_REQUEST("cannot specify -only when -a is specified", null); } descArgs.mOnThisObjectTypeOnly = true; } else if (args[i].startsWith("-a")) { if (descArgs.mAttrClass != null) { throw ServiceException.INVALID_REQUEST("cannot specify -a when entry type is specified", null); } if (descArgs.mAttr != null) { throw ServiceException.INVALID_REQUEST("attribute is already specified as " + descArgs.mAttr, null); } if (args.length <= i + 1) { throw ServiceException.INVALID_REQUEST("not enough args", null); } i++; descArgs.mAttr = args[i]; } else { if (descArgs.mAttr != null) { throw ServiceException.INVALID_REQUEST("too many args", null); } if (descArgs.mAttrClass != null) { throw ServiceException .INVALID_REQUEST("entry type is already specified as " + descArgs.mAttrClass, null); } AttributeClass ac = AttributeClass.fromString(args[i]); if (ac == null || !ac.isProvisionable()) { throw ServiceException.INVALID_REQUEST("invalid entry type " + ac.name(), null); } descArgs.mAttrClass = ac; } i++; } if ((descArgs.mNonInheritedOnly == true || descArgs.mOnThisObjectTypeOnly == true) && descArgs.mAttrClass == null) { throw ServiceException.INVALID_REQUEST("-ni -only must be specified with an entry type", null); } return descArgs; } private void doDescribe(String[] args) throws ServiceException { DescribeArgs descArgs = null; try { descArgs = parseDescribeArgs(args); } catch (ServiceException e) { descAttrsUsage(e); return; } catch (NumberFormatException e) { descAttrsUsage(e); return; } SortedSet<String> attrs = null; String specificAttr = null; AttributeManager am = AttributeManager.getInstance(); if (descArgs.mAttr != null) { // specific attr specificAttr = descArgs.mAttr; } else if (descArgs.mAttrClass != null) { // attrs in a class attrs = new TreeSet<String>(am.getAllAttrsInClass(descArgs.mAttrClass)); if (descArgs.mNonInheritedOnly) { Set<String> inheritFrom = null; Set<String> netAttrs = null; switch (descArgs.mAttrClass) { case account: netAttrs = new HashSet<String>(attrs); inheritFrom = new HashSet<String>(am.getAllAttrsInClass(AttributeClass.cos)); netAttrs = SetUtil.subtract(netAttrs, inheritFrom); inheritFrom = new HashSet<String>(am.getAllAttrsInClass(AttributeClass.domain)); // for // accountCosDomainInherited netAttrs = SetUtil.subtract(netAttrs, inheritFrom); break; case domain: case server: netAttrs = new HashSet<String>(attrs); inheritFrom = new HashSet<String>(am.getAllAttrsInClass(AttributeClass.globalConfig)); netAttrs = SetUtil.subtract(netAttrs, inheritFrom); break; } if (netAttrs != null) { attrs = new TreeSet<String>(netAttrs); } } if (descArgs.mOnThisObjectTypeOnly) { TreeSet<String> netAttrs = new TreeSet<String>(); for (String attr : attrs) { AttributeInfo ai = am.getAttributeInfo(attr); if (ai == null) { continue; } Set<AttributeClass> requiredIn = ai.getRequiredIn(); Set<AttributeClass> optionalIn = ai.getOptionalIn(); if ((requiredIn == null || requiredIn.size() == 1) && (optionalIn == null || optionalIn.size() == 1)) { netAttrs.add(attr); } } attrs = netAttrs; } } else { // // all attrs // // am.getAllAttrs() only contains attrs with AttributeInfo // not extension attrs // attrs = new TreeSet<String>(am.getAllAttrs()); // attr sets for each AttributeClass contain attrs in the extensions, use them attrs = new TreeSet<String>(); for (AttributeClass ac : AttributeClass.values()) { attrs.addAll(am.getAllAttrsInClass(ac)); } } if (specificAttr != null) { AttributeInfo ai = am.getAttributeInfo(specificAttr); if (ai == null) { console.println("no attribute info for " + specificAttr); } else { console.println(ai.getName()); // description String desc = ai.getDescription(); console.println(FileGenUtil.wrapComments((desc == null ? "" : desc), 70, " ")); console.println(); for (DescribeArgs.Field f : DescribeArgs.Field.values()) { console.format(" %15s : %s\n", f.name(), DescribeArgs.Field.print(f, ai)); } } console.println(); } else { for (String attr : attrs) { AttributeInfo ai = am.getAttributeInfo(attr); if (ai == null) { console.println(attr + " (no attribute info)"); continue; } String attrName = ai.getName(); // camel case name console.println(attrName); if (descArgs.mVerbose) { String desc = ai.getDescription(); console.println(FileGenUtil.wrapComments((desc == null ? "" : desc), 70, " ") + "\n"); } } } } private void doFlushCache(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } boolean allServers = false; int argIdx = 1; if (args[argIdx].equals("-a")) { allServers = true; if (args.length > 2) { argIdx++; } else { usage(); return; } } String type = args[argIdx++]; CacheEntry[] entries = null; if (args.length > argIdx) { entries = new CacheEntry[args.length - argIdx]; for (int i = argIdx; i < args.length; i++) { Key.CacheEntryBy entryBy; if (Provisioning.isUUID(args[i])) { entryBy = Key.CacheEntryBy.id; } else { entryBy = Key.CacheEntryBy.name; } entries[i - argIdx] = new CacheEntry(entryBy, args[i]); } } SoapProvisioning sp = (SoapProvisioning) prov; sp.flushCache(type, entries, allServers); } private void doGenerateDomainPreAuthKey(String[] args) throws ServiceException { String key = null; boolean force = false; if (args.length == 3) { if (args[1].equals("-f")) { force = true; } else { usage(); return; } key = args[2]; } else { key = args[1]; } Domain domain = lookupDomain(key); String curPreAuthKey = domain.getAttr(Provisioning.A_zimbraPreAuthKey); if (curPreAuthKey != null && !force) { throw ServiceException.INVALID_REQUEST("pre auth key exists for domain " + key + ", use command -f option to force overwriting the existing key", null); } String preAuthKey = PreAuthKey.generateRandomPreAuthKey(); HashMap<String, String> attrs = new HashMap<String, String>(); attrs.put(Provisioning.A_zimbraPreAuthKey, preAuthKey); prov.modifyAttrs(domain, attrs); console.printf("preAuthKey: %s\n", preAuthKey); if (curPreAuthKey != null) { console.printf("previous preAuthKey: %s\n", curPreAuthKey); } } private void doGenerateDomainPreAuth(String[] args) throws ServiceException { String key = args[1]; Domain domain = lookupDomain(key); String preAuthKey = domain.getAttr(Provisioning.A_zimbraPreAuthKey, null); if (preAuthKey == null) { throw ServiceException.INVALID_REQUEST("domain not configured for preauth", null); } String name = args[2]; String by = args[3]; long timestamp = Long.parseLong(args[4]); if (timestamp == 0) { timestamp = System.currentTimeMillis(); } long expires = Long.parseLong(args[5]); HashMap<String, String> params = new HashMap<String, String>(); params.put("account", name); params.put("by", by); params.put("timestamp", timestamp + ""); params.put("expires", expires + ""); if (args.length == 7) { params.put("admin", args[6]); } console.printf("account: %s\nby: %s\ntimestamp: %s\nexpires: %s\npreauth: %s\n", name, by, timestamp, expires, PreAuthKey.computePreAuth(params, preAuthKey)); } private void doGetAllMtaAuthURLs() throws ServiceException { List<Server> servers = prov.getAllServers(); for (Server server : servers) { boolean isTarget = server.getBooleanAttr(Provisioning.A_zimbraMtaAuthTarget, false); if (isTarget) { String url = URLUtil.getMtaAuthURL(server) + " "; console.print(url); } } console.println(); } private void doGetAllReverseProxyURLs() throws ServiceException { String REVERSE_PROXY_PROTO = ""; // don't need proto for nginx.conf String REVERSE_PROXY_PATH = ExtensionDispatcherServlet.EXTENSION_PATH + "/nginx-lookup"; List<Server> servers = prov.getAllMailClientServers(); for (Server server : servers) { int port = server.getIntAttr(Provisioning.A_zimbraExtensionBindPort, 7072); boolean isTarget = server.getBooleanAttr(Provisioning.A_zimbraReverseProxyLookupTarget, false); if (isTarget) { String serviceName = server.getAttr(Provisioning.A_zimbraServiceHostname, ""); console.print(REVERSE_PROXY_PROTO + serviceName + ":" + port + REVERSE_PROXY_PATH + " "); } } console.println(); } private void doGetAllReverseProxyBackends() throws ServiceException { List<Server> servers = prov.getAllServers(); boolean atLeastOne = false; for (Server server : servers) { boolean isTarget = server.getBooleanAttr(Provisioning.A_zimbraReverseProxyLookupTarget, false); if (!isTarget) { continue; } // (For now) assume HTTP can be load balanced to... String mode = server.getAttr(Provisioning.A_zimbraMailMode, null); if (mode == null) { continue; } MailMode mailMode = Provisioning.MailMode.fromString(mode); boolean isPlain = (mailMode == Provisioning.MailMode.http || (!LC.zimbra_require_interprocess_security .booleanValue() && (mailMode == Provisioning.MailMode.mixed || mailMode == Provisioning.MailMode.both))); int backendPort; if (isPlain) { backendPort = server.getIntAttr(Provisioning.A_zimbraMailPort, 0); } else { backendPort = server.getIntAttr(Provisioning.A_zimbraMailSSLPort, 0); } String serviceName = server.getAttr(Provisioning.A_zimbraServiceHostname, ""); console.println(" server " + serviceName + ":" + backendPort + ";"); atLeastOne = true; } if (!atLeastOne) { // workaround zmmtaconfig not being able to deal with empty output console.println(" server localhost:8080;"); } } private void doGetAllReverseProxyDomains() throws ServiceException { NamedEntry.Visitor visitor = new NamedEntry.Visitor() { @Override public void visit(NamedEntry entry) throws ServiceException { if (entry.getAttr(Provisioning.A_zimbraVirtualHostname) != null && entry.getAttr(Provisioning.A_zimbraSSLPrivateKey) != null && entry.getAttr(Provisioning.A_zimbraSSLCertificate) != null) { StringBuilder virtualHosts = new StringBuilder(); for (String vh : entry.getMultiAttr(Provisioning.A_zimbraVirtualHostname)) { virtualHosts.append(vh + " "); } console.println(entry.getName() + " " + virtualHosts); } } }; prov.getAllDomains(visitor, new String[] { Provisioning.A_zimbraVirtualHostname, Provisioning.A_zimbraSSLPrivateKey, Provisioning.A_zimbraSSLCertificate }); } private void doGetAllMemcachedServers() throws ServiceException { List<Server> servers = prov.getAllServers(Provisioning.SERVICE_MEMCACHED); for (Server server : servers) { console.print(server.getAttr(Provisioning.A_zimbraServiceHostname, "") + ":" + server.getAttr(Provisioning.A_zimbraMemcachedBindPort, "") + " "); } console.println(); } private List<Pair<String /* hostname */, Integer /* port */>> getMailboxServersFromArgs(String[] args) throws ServiceException { List<Pair<String, Integer>> entries = new ArrayList<Pair<String, Integer>>(); if (args.length == 2 && "all".equalsIgnoreCase(args[1])) { // Get all mailbox servers. List<Server> servers = prov.getAllMailClientServers(); for (Server svr : servers) { String host = svr.getAttr(Provisioning.A_zimbraServiceHostname); int port = (int) svr.getLongAttr(Provisioning.A_zimbraAdminPort, serverPort); Pair<String, Integer> entry = new Pair<String, Integer>(host, port); entries.add(entry); } } else { // Only named servers. for (int i = 1; i < args.length; ++i) { String arg = args[i]; if (serverHostname.equalsIgnoreCase(arg)) { entries.add(new Pair<String, Integer>(serverHostname, serverPort)); } else { Server svr = prov.getServerByServiceHostname(arg); if (svr == null) { throw AccountServiceException.NO_SUCH_SERVER(arg); } // TODO: Verify svr has mailbox service enabled. int port = (int) svr.getLongAttr(Provisioning.A_zimbraAdminPort, serverPort); entries.add(new Pair<String, Integer>(arg, port)); } } } return entries; } private void doReloadMemcachedClientConfig(String[] args) throws ServiceException { List<Pair<String, Integer>> servers = getMailboxServersFromArgs(args); // Send command to each server. for (Pair<String, Integer> server : servers) { String hostname = server.getFirst(); int port = server.getSecond(); if (verboseMode) { console.print("Updating " + hostname + " ... "); } boolean success = false; try { SoapProvisioning sp = new SoapProvisioning(); sp.soapSetURI(LC.zimbra_admin_service_scheme.value() + hostname + ":" + port + AdminConstants.ADMIN_SERVICE_URI); if (debugLevel != SoapDebugLevel.none) { sp.soapSetHttpTransportDebugListener(this); } if (account != null && password != null) { sp.soapAdminAuthenticate(account, password); } else if (authToken != null) { sp.soapAdminAuthenticate(authToken); } else { sp.soapZimbraAdminAuthenticate(); } sp.reloadMemcachedClientConfig(); success = true; } catch (ServiceException e) { if (verboseMode) { console.println("fail"); e.printStackTrace(console); } else { console.println("Error updating " + hostname + ": " + e.getMessage()); } } finally { if (verboseMode && success) { console.println("ok"); } } } } private void doGetMemcachedClientConfig(String[] args) throws ServiceException { List<Pair<String, Integer>> servers = getMailboxServersFromArgs(args); // Send command to each server. int longestHostname = 0; for (Pair<String, Integer> server : servers) { String hostname = server.getFirst(); longestHostname = Math.max(longestHostname, hostname.length()); } String hostnameFormat = String.format("%%-%ds", longestHostname); boolean consistent = true; String prevConf = null; for (Pair<String, Integer> server : servers) { String hostname = server.getFirst(); int port = server.getSecond(); try { SoapProvisioning sp = new SoapProvisioning(); sp.soapSetURI(LC.zimbra_admin_service_scheme.value() + hostname + ":" + port + AdminConstants.ADMIN_SERVICE_URI); if (debugLevel != SoapDebugLevel.none) { sp.soapSetHttpTransportDebugListener(this); } if (account != null && password != null) { sp.soapAdminAuthenticate(account, password); } else if (authToken != null) { sp.soapAdminAuthenticate(authToken); } else { sp.soapZimbraAdminAuthenticate(); } MemcachedClientConfig config = sp.getMemcachedClientConfig(); String serverList = config.serverList != null ? config.serverList : "none"; if (verboseMode) { console.printf(hostnameFormat + " => serverList=[%s], hashAlgo=%s, binaryProto=%s, expiry=%ds, timeout=%dms\n", hostname, serverList, config.hashAlgorithm, config.binaryProtocol, config.defaultExpirySeconds, config.defaultTimeoutMillis); } else if (config.serverList != null) { if (DefaultHashAlgorithm.KETAMA_HASH.toString().equals(config.hashAlgorithm)) { // Don't print the default hash algorithm to keep the output clutter-free. console.printf(hostnameFormat + " => %s\n", hostname, serverList); } else { console.printf(hostnameFormat + " => %s (%S)\n", hostname, serverList, config.hashAlgorithm); } } else { console.printf(hostnameFormat + " => none\n", hostname); } String listAndAlgo = serverList + "/" + config.hashAlgorithm; if (prevConf == null) { prevConf = listAndAlgo; } else if (!prevConf.equals(listAndAlgo)) { consistent = false; } } catch (ServiceException e) { console.printf(hostnameFormat + " => ERROR: unable to get configuration\n", hostname); if (verboseMode) { e.printStackTrace(console); } } } if (!consistent) { console.println("Inconsistency detected!"); } } private void doGetServer(String[] args) throws ServiceException { boolean applyDefault = true; int i = 1; while (i < args.length) { String arg = args[i]; if (arg.equals("-e")) { applyDefault = false; } else { break; } i++; } if (i >= args.length) { usage(); return; } dumpServer(lookupServer(args[i], applyDefault), applyDefault, getArgNameSet(args, i + 1)); } private void doGetAlwaysOnCluster(String[] args) throws ServiceException { dumpAlwaysOnCluster(lookupAlwaysOnCluster(args[1]), getArgNameSet(args, 2)); } private void doPurgeAccountCalendarCache(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } if (args.length > 1) { for (int i = 1; i < args.length; i++) { Account acct = lookupAccount(args[i], true); prov.purgeAccountCalendarCache(acct.getId()); } } } private void doCreateXMPPComponent(String[] args) throws ServiceException, ArgException { // 4 = class // 5 = category // 6 = type Map<String, Object> map = getMapAndCheck(args, 7, true); map.put(Provisioning.A_zimbraXMPPComponentClassName, args[4]); map.put(Provisioning.A_zimbraXMPPComponentCategory, args[5]); map.put(Provisioning.A_zimbraXMPPComponentType, args[6]); Domain d = lookupDomain(args[2]); String routableName = args[1] + "." + d.getName(); console.println(prov.createXMPPComponent(routableName, lookupDomain(args[2]), lookupServer(args[3]), map)); } private void doGetXMPPComponent(String[] args) throws ServiceException { dumpXMPPComponent(lookupXMPPComponent(args[1]), getArgNameSet(args, 2)); } private void doSetServerOffline(String[] args) throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } String key = args[1]; ((SoapProvisioning) prov).setServerOffline(guessServerBy(key), key); } private void doSetLocalServerOnline() throws ServiceException { if (!(prov instanceof SoapProvisioning)) { throwSoapOnly(); } ((SoapProvisioning) prov).setLocalServerOnline(); } static private class RightArgs { String mTargetType; String mTargetIdOrName; String mGranteeType; String mGranteeIdOrName; String mSecret; String mRight; RightModifier mRightModifier; String[] mArgs; int mCurPos = 1; RightArgs(String[] args) { mArgs = args; mCurPos = 1; } String getNextArg() throws ServiceException { if (hasNext()) { return mArgs[mCurPos++]; } else { throw ServiceException.INVALID_REQUEST("not enough arguments", null); } } boolean hasNext() { return (mCurPos < mArgs.length); } } private void getRightArgsTarget(RightArgs ra) throws ServiceException, ArgException { if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } ra.mTargetType = ra.mArgs[ra.mCurPos++]; TargetType tt = TargetType.fromCode(ra.mTargetType); if (tt.needsTargetIdentity()) { if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } ra.mTargetIdOrName = ra.mArgs[ra.mCurPos++]; } else { ra.mTargetIdOrName = null; } } private void getRightArgsGrantee(RightArgs ra, boolean needGranteeType, boolean needSecret) throws ServiceException, ArgException { if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } GranteeType gt = null; if (needGranteeType) { ra.mGranteeType = ra.mArgs[ra.mCurPos++]; gt = GranteeType.fromCode(ra.mGranteeType); } else { ra.mGranteeType = null; } if (gt == GranteeType.GT_AUTHUSER || gt == GranteeType.GT_PUBLIC) { return; } if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } ra.mGranteeIdOrName = ra.mArgs[ra.mCurPos++]; if (needSecret && gt != null) { if (gt.allowSecret()) { if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } ra.mSecret = ra.mArgs[ra.mCurPos++]; } } } private void getRightArgsRight(RightArgs ra) throws ServiceException, ArgException { if (ra.mCurPos >= ra.mArgs.length) { throw new ArgException("not enough arguments"); } ra.mRight = ra.mArgs[ra.mCurPos++]; ra.mRightModifier = RightModifier.fromChar(ra.mRight.charAt(0)); if (ra.mRightModifier != null) { ra.mRight = ra.mRight.substring(1); } } private void getRightArgs(RightArgs ra, boolean needGranteeType, boolean needSecret) throws ServiceException, ArgException { getRightArgsTarget(ra); getRightArgsGrantee(ra, needGranteeType, needSecret); getRightArgsRight(ra); } private void doCheckRight(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); getRightArgs(ra, false, false); // todo, handle secret Map<String, Object> attrs = getMap(args, ra.mCurPos); TargetBy targetBy = (ra.mTargetIdOrName == null) ? null : guessTargetBy(ra.mTargetIdOrName); GranteeBy granteeBy = guessGranteeBy(ra.mGranteeIdOrName); AccessManager.ViaGrant via = new AccessManager.ViaGrant(); boolean allow = prov.checkRight(ra.mTargetType, targetBy, ra.mTargetIdOrName, granteeBy, ra.mGranteeIdOrName, ra.mRight, attrs, via); console.println(allow ? "ALLOWED" : "DENIED"); if (via.available()) { console.println("Via:"); console.println(" target type : " + via.getTargetType()); console.println(" target : " + via.getTargetName()); console.println(" grantee type : " + via.getGranteeType()); console.println(" grantee : " + via.getGranteeName()); console.println(" right : " + (via.isNegativeGrant() ? "DENY " : "") + via.getRight()); console.println(); } } private void doGetAllEffectiveRights(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); if (prov instanceof LdapProv) { // must provide grantee info getRightArgsGrantee(ra, true, false); } else { // has more args, use it for the requested grantee if (ra.mCurPos < args.length) { getRightArgsGrantee(ra, true, false); } } boolean expandSetAttrs = false; boolean expandGetAttrs = false; // if there are more args, see if they are expandSetAttrs/expandGetAttrs for (int i = ra.mCurPos; i < args.length; i++) { if ("expandSetAttrs".equals(args[i])) { expandSetAttrs = true; } else if ("expandGetAttrs".equals(args[i])) { expandGetAttrs = true; } else { throw new ArgException("unrecognized arg: " + args[i]); } } GranteeBy granteeBy = (ra.mGranteeIdOrName == null) ? null : guessGranteeBy(ra.mGranteeIdOrName); RightCommand.AllEffectiveRights allEffRights = prov.getAllEffectiveRights(ra.mGranteeType, granteeBy, ra.mGranteeIdOrName, expandSetAttrs, expandGetAttrs); console.println(allEffRights.granteeType() + " " + allEffRights.granteeName() + "(" + allEffRights.granteeId() + ")" + " has the following rights:"); for (Map.Entry<TargetType, RightCommand.RightsByTargetType> rightsByTargetType : allEffRights .rightsByTargetType().entrySet()) { RightCommand.RightsByTargetType rbtt = rightsByTargetType.getValue(); if (!rbtt.hasNoRight()) { dumpRightsByTargetType(rightsByTargetType.getKey(), rbtt, expandSetAttrs, expandGetAttrs); } } } private void dumpRightsByTargetType(TargetType targetType, RightCommand.RightsByTargetType rbtt, boolean expandSetAttrs, boolean expandGetAttrs) { console.println("------------------------------------------------------------------"); console.println("Target type: " + targetType.getCode()); console.println("------------------------------------------------------------------"); RightCommand.EffectiveRights er = rbtt.all(); if (er != null) { console.println("On all " + targetType.getPrettyName() + " entries"); dumpEffectiveRight(er, expandSetAttrs, expandGetAttrs); } if (rbtt instanceof RightCommand.DomainedRightsByTargetType) { RightCommand.DomainedRightsByTargetType domainedRights = (RightCommand.DomainedRightsByTargetType) rbtt; for (RightCommand.RightAggregation rightsByDomains : domainedRights.domains()) { dumpRightAggregation(targetType, rightsByDomains, true, expandSetAttrs, expandGetAttrs); } } for (RightCommand.RightAggregation rightsByEntries : rbtt.entries()) { dumpRightAggregation(targetType, rightsByEntries, false, expandSetAttrs, expandGetAttrs); } } private void dumpRightAggregation(TargetType targetType, RightCommand.RightAggregation rightAggr, boolean domainScope, boolean expandSetAttrs, boolean expandGetAttrs) { Set<String> entries = rightAggr.entries(); RightCommand.EffectiveRights er = rightAggr.effectiveRights(); for (String entry : entries) { if (domainScope) { console.println("On " + targetType.getCode() + " entries in domain " + entry); } else { console.println("On " + targetType.getCode() + " " + entry); } } dumpEffectiveRight(er, expandSetAttrs, expandGetAttrs); } private void doGetEffectiveRights(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); getRightArgsTarget(ra); if (prov instanceof LdapProv) { // must provide grantee info getRightArgsGrantee(ra, false, false); } else { // has more args, use it for the requested grantee if (ra.mCurPos < args.length) { getRightArgsGrantee(ra, false, false); } } boolean expandSetAttrs = false; boolean expandGetAttrs = false; // if there are more args, see if they are expandSetAttrs/expandGetAttrs for (int i = ra.mCurPos; i < args.length; i++) { if ("expandSetAttrs".equals(args[i])) { expandSetAttrs = true; } else if ("expandGetAttrs".equals(args[i])) { expandGetAttrs = true; } else { throw new ArgException("unrecognized arg: " + args[i]); } } TargetBy targetBy = (ra.mTargetIdOrName == null) ? null : guessTargetBy(ra.mTargetIdOrName); GranteeBy granteeBy = (ra.mGranteeIdOrName == null) ? null : guessGranteeBy(ra.mGranteeIdOrName); RightCommand.EffectiveRights effRights = prov.getEffectiveRights(ra.mTargetType, targetBy, ra.mTargetIdOrName, granteeBy, ra.mGranteeIdOrName, expandSetAttrs, expandGetAttrs); console.println("Account " + effRights.granteeName() + " has the following rights on target " + effRights.targetType() + " " + effRights.targetName()); dumpEffectiveRight(effRights, expandSetAttrs, expandGetAttrs); } private void dumpEffectiveRight(RightCommand.EffectiveRights effRights, boolean expandSetAttrs, boolean expandGetAttrs) { List<String> presetRights = effRights.presetRights(); if (presetRights != null && presetRights.size() > 0) { console.println("================"); console.println("Preset rights"); console.println("================"); for (String r : presetRights) { console.println(" " + r); } } displayAttrs("set", expandSetAttrs, effRights.canSetAllAttrs(), effRights.canSetAttrs()); displayAttrs("get", expandGetAttrs, effRights.canGetAllAttrs(), effRights.canGetAttrs()); console.println(); console.println(); } private void displayAttrs(String op, boolean expandAll, boolean allAttrs, SortedMap<String, RightCommand.EffectiveAttr> attrs) { if (!allAttrs && attrs.isEmpty()) { return; } String format = " %-50s %-30s\n"; console.println(); console.println("========================="); console.println(op + " attributes rights"); console.println("========================="); if (allAttrs) { console.println("Can " + op + " all attributes"); } if (!allAttrs || expandAll) { console.println("Can " + op + " the following attributes"); console.println("--------------------------------"); console.printf(format, "attribute", "default"); console.printf(format, "----------------------------------------", "--------------------"); for (RightCommand.EffectiveAttr ea : attrs.values()) { boolean first = true; if (ea.getDefault().isEmpty()) { console.printf(format, ea.getAttrName(), ""); } else { for (String v : ea.getDefault()) { if (first) { console.printf(format, ea.getAttrName(), v); first = false; } else { console.printf(format, "", v); } } } } } } /** * for testing only, not used in production */ private void doGetCreateObjectAttrs(String[] args) throws ServiceException { String targetType = args[1]; Key.DomainBy domainBy = null; String domain = null; if (!args[2].equals("null")) { domainBy = guessDomainBy(args[2]); domain = args[2]; } Key.CosBy cosBy = null; String cos = null; if (!args[3].equals("null")) { cosBy = guessCosBy(args[3]); cos = args[3]; } GranteeBy granteeBy = null; String grantee = null; // take grantee arg only if LdapProv // for SoapProvisioning, -a {admin account} -p {password} is required with zmprov if (prov instanceof LdapProv) { granteeBy = guessGranteeBy(args[4]); grantee = args[4]; } console.println("Domain: " + domain); console.println("Cos: " + cos); console.println("Grantee: " + grantee); console.println(); RightCommand.EffectiveRights effRights = prov.getCreateObjectAttrs(targetType, domainBy, domain, cosBy, cos, granteeBy, grantee); displayAttrs("set", true, effRights.canSetAllAttrs(), effRights.canSetAttrs()); } private void doGetGrants(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); boolean granteeIncludeGroupsGranteeBelongs = true; while (ra.hasNext()) { String arg = ra.getNextArg(); if ("-t".equals(arg)) { getRightArgsTarget(ra); } else if ("-g".equals(arg)) { getRightArgsGrantee(ra, true, false); if (ra.hasNext()) { String includeGroups = ra.getNextArg(); if ("1".equals(includeGroups)) { granteeIncludeGroupsGranteeBelongs = true; } else if ("0".equals(includeGroups)) { granteeIncludeGroupsGranteeBelongs = false; } else { throw ServiceException .INVALID_REQUEST("invalid value for the include group flag, must be 0 or 1", null); } } } } TargetBy targetBy = (ra.mTargetIdOrName == null) ? null : guessTargetBy(ra.mTargetIdOrName); GranteeBy granteeBy = (ra.mGranteeIdOrName == null) ? null : guessGranteeBy(ra.mGranteeIdOrName); RightCommand.Grants grants = prov.getGrants(ra.mTargetType, targetBy, ra.mTargetIdOrName, ra.mGranteeType, granteeBy, ra.mGranteeIdOrName, granteeIncludeGroupsGranteeBelongs); String format = "%-12.12s %-36.36s %-30.30s %-12.12s %-36.36s %-30.30s %s\n"; console.printf(format, "target type", "target id", "target name", "grantee type", "grantee id", "grantee name", "right"); console.printf(format, "------------", "------------------------------------", "------------------------------", "------------", "------------------------------------", "------------------------------", "--------------------"); for (RightCommand.ACE ace : grants.getACEs()) { // String deny = ace.deny()?"-":""; RightModifier rightModifier = ace.rightModifier(); String rm = (rightModifier == null) ? "" : String.valueOf(rightModifier.getModifier()); console.printf(format, ace.targetType(), ace.targetId(), ace.targetName(), ace.granteeType(), ace.granteeId(), ace.granteeName(), rm + ace.right()); } console.println(); } private void doGrantRight(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); getRightArgs(ra, true, true); TargetBy targetBy = (ra.mTargetIdOrName == null) ? null : guessTargetBy(ra.mTargetIdOrName); GranteeBy granteeBy = (ra.mGranteeIdOrName == null) ? null : guessGranteeBy(ra.mGranteeIdOrName); prov.grantRight(ra.mTargetType, targetBy, ra.mTargetIdOrName, ra.mGranteeType, granteeBy, ra.mGranteeIdOrName, ra.mSecret, ra.mRight, ra.mRightModifier); } private void doRevokeRight(String[] args) throws ServiceException, ArgException { RightArgs ra = new RightArgs(args); getRightArgs(ra, true, false); TargetBy targetBy = (ra.mTargetIdOrName == null) ? null : guessTargetBy(ra.mTargetIdOrName); GranteeBy granteeBy = (ra.mGranteeIdOrName == null) ? null : guessGranteeBy(ra.mGranteeIdOrName); prov.revokeRight(ra.mTargetType, targetBy, ra.mTargetIdOrName, ra.mGranteeType, granteeBy, ra.mGranteeIdOrName, ra.mRight, ra.mRightModifier); } private void doGetAuthTokenInfo(String[] args) { String authToken = args[1]; try { Map attrs = AuthToken.getInfo(authToken); List keys = new ArrayList(attrs.keySet()); Collections.sort(keys); for (Object k : keys) { String key = k.toString(); String value = attrs.get(k).toString(); if ("exp".equals(key)) { long exp = Long.parseLong(value); console.format("%s: %s (%s)\n", key, value, DateUtil.toRFC822Date(new Date(exp))); } else { console.format("%s: %s\n", key, value); } } } catch (AuthTokenException e) { console.println("Unable to parse auth token: " + e.getMessage()); } console.println(); } private void doUpdatePresenceSessionId(String[] args) throws ServiceException { String idOrName = args[1]; String username = args[2]; String password = args[3]; UCService ucService = lookupUCService(idOrName); /* * soap only */ String newSessionId = prov.updatePresenceSessionId(ucService.getId(), username, password); console.println(newSessionId); } private void doGetAllFreeBusyProviders() throws ServiceException, IOException { FbCli fbcli = new FbCli(); for (FbCli.FbProvider fbprov : fbcli.getAllFreeBusyProviders()) { console.println(fbprov.toString()); } } private void doGetFreeBusyQueueInfo(String[] args) throws ServiceException, IOException { FbCli fbcli = new FbCli(); String name = null; if (args.length > 1) { name = args[1]; } for (FbCli.FbQueue fbqueue : fbcli.getFreeBusyQueueInfo(name)) { console.println(fbqueue.toString()); } } private void doPushFreeBusy(String[] args) throws ServiceException, IOException { FbCli fbcli = new FbCli(); Map<String, HashSet<String>> accountMap = new HashMap<String, HashSet<String>>(); for (int i = 1; i < args.length; i++) { String acct = args[i]; Account account = prov.getAccountById(acct); if (account == null) { throw AccountServiceException.NO_SUCH_ACCOUNT(acct); } String host = account.getMailHost(); HashSet<String> accountSet = accountMap.get(host); if (accountSet == null) { accountSet = new HashSet<String>(); accountMap.put(host, accountSet); } accountSet.add(acct); } for (String host : accountMap.keySet()) { console.println("pushing to server " + host); fbcli.setServer(host); fbcli.pushFreeBusyForAccounts(accountMap.get(host)); } } private void doPushFreeBusyForDomain(String[] args) throws ServiceException, IOException { lookupDomain(args[1]); FbCli fbcli = new FbCli(); for (Server server : prov.getAllMailClientServers()) { console.println("pushing to server " + server.getName()); fbcli.setServer(server.getName()); fbcli.pushFreeBusyForDomain(args[1]); } } private void doPurgeFreeBusyQueue(String[] args) throws ServiceException, IOException { String provider = null; if (args.length > 1) { provider = args[1]; } FbCli fbcli = new FbCli(); fbcli.purgeFreeBusyQueue(provider); } private void dumpSMIMEConfigs(Map<String, Map<String, Object>> smimeConfigs) throws ServiceException { for (Map.Entry<String, Map<String, Object>> smimeConfig : smimeConfigs.entrySet()) { String configName = smimeConfig.getKey(); Map<String, Object> configAttrs = smimeConfig.getValue(); console.println("# name " + configName); dumpAttrs(configAttrs, null); console.println(); } } private void doGetConfigSMIMEConfig(String[] args) throws ServiceException { String configName = null; if (args.length > 1) { configName = args[1]; } Map<String, Map<String, Object>> smimeConfigs = prov.getConfigSMIMEConfig(configName); dumpSMIMEConfigs(smimeConfigs); } private void doGetDomainSMIMEConfig(String[] args) throws ServiceException { String domainName = args[1]; Domain domain = lookupDomain(domainName); String configName = null; if (args.length > 2) { configName = args[2]; } Map<String, Map<String, Object>> smimeConfigs = prov.getDomainSMIMEConfig(domain, configName); dumpSMIMEConfigs(smimeConfigs); } private void doModifyConfigSMIMEConfig(String[] args) throws ServiceException, ArgException { String configName = args[1]; prov.modifyConfigSMIMEConfig(configName, getMapAndCheck(args, 2, false)); } private void doModifyDomainSMIMEConfig(String[] args) throws ServiceException, ArgException { String domainName = args[1]; Domain domain = lookupDomain(domainName); String configName = args[2]; prov.modifyDomainSMIMEConfig(domain, configName, getMapAndCheck(args, 3, false)); } private void doRemoveConfigSMIMEConfig(String[] args) throws ServiceException { String configName = null; if (args.length > 1) { configName = args[1]; } prov.removeConfigSMIMEConfig(configName); } private void doRemoveDomainSMIMEConfig(String[] args) throws ServiceException { String domainName = args[1]; Domain domain = lookupDomain(domainName); String configName = null; if (args.length > 2) { configName = args[2]; } prov.removeDomainSMIMEConfig(domain, configName); } private void doHelp(String[] args) { Category cat = null; if (args != null && args.length >= 2) { String s = args[1].toUpperCase(); try { cat = Category.valueOf(s); } catch (IllegalArgumentException e) { for (Category c : Category.values()) { if (c.name().startsWith(s)) { cat = c; break; } } } } if (args == null || args.length == 1 || cat == null) { console.println(" zmprov is used for provisioning. Try:"); console.println(""); for (Category c : Category.values()) { console.printf(" zmprov help %-15s %s\n", c.name().toLowerCase(), c.getDescription()); } } if (cat != null) { console.println(""); for (Command c : Command.values()) { if (!c.hasHelp()) { continue; } if (cat == Category.COMMANDS || cat == c.getCategory()) { Command.Via via = c.getVia(); console.printf(" %s(%s) %s\n", c.getName(), c.getAlias(), c.getHelp()); if (via == Command.Via.ldap) { console.printf(" -- NOTE: %s can only be used with \"zmprov -l/--ldap\"\n", c.getName()); } console.printf("\n"); } } Category.help(cat); } console.println(); } @Override public void receiveSoapMessage(PostMethod postMethod, Element envelope) { console.printf("======== SOAP RECEIVE =========\n"); if (debugLevel == SoapDebugLevel.high) { Header[] headers = postMethod.getResponseHeaders(); for (Header header : headers) { console.println(header.toString().trim()); // trim the ending crlf } console.println(); } long end = System.currentTimeMillis(); console.println(envelope.prettyPrint()); console.printf("=============================== (%d msecs)\n", end - sendStart); } @Override public void sendSoapMessage(PostMethod postMethod, Element envelope, HttpState httpState) { console.println("========== SOAP SEND =========="); if (debugLevel == SoapDebugLevel.high) { try { URI uri = postMethod.getURI(); console.println(uri.toString()); } catch (URIException e) { if (verboseMode) { e.printStackTrace(errConsole); } else { console.println("Unable to get request URL, error=" + e.getMessage()); } } Header[] headers = postMethod.getRequestHeaders(); for (Header header : headers) { console.println(header.toString().trim()); // trim the ending crlf } console.println(); } sendStart = System.currentTimeMillis(); console.println(envelope.prettyPrint()); console.println("==============================="); } private void throwSoapOnly() throws ServiceException { throw ServiceException.INVALID_REQUEST(ERR_VIA_SOAP_ONLY, null); } private void throwLdapOnly() throws ServiceException { throw ServiceException.INVALID_REQUEST(ERR_VIA_LDAP_ONLY, null); } private void loadLdapSchemaExtensionAttrs() { if (prov instanceof LdapProv) { AttributeManager.loadLdapSchemaExtensionAttrs((LdapProv) prov); } } /** * To remove a particular instance of an attribute, the prefix indicator '-' is used before the attribute name. When * the attribute name is started with one of the valid command arguments, such as -z or -a, the parser mistakenly * divides it into two parts instead of treating as one parameter of the '-' and attribute name. * <p> * This method detects such decapitated attribute, and recombines those two into one attribute name with '-'. * * @param parsedArgs * [cmd-args] which are parsed by PosixParser * @param options * set of valid [args] * @param args * @throws ServiceException */ static private String[] recombineDecapitatedAttrs(String[] parsedArgs, Options options, String[] orgArgs) throws ServiceException { List<String> newArgs = new ArrayList<String>(parsedArgs.length); String headStr = null; for (int i = 0; i < parsedArgs.length; i++) { String arg = parsedArgs[i]; if (arg.startsWith("-") && arg.length() == 2 && options.hasOption(arg)) { // Detect legitimate POSIX style parameters even after operation command; // such as "zmprov describe -a <attr>" if (i < parsedArgs.length - 1) { boolean missParsed = false; String tmpParam = arg + parsedArgs[i + 1]; for (String orgArg : orgArgs) { if (orgArg.equals(tmpParam)) { missParsed = true; break; } } if (missParsed) { headStr = arg; } else { newArgs.add(arg); } } else { newArgs.add(arg); } } else if (headStr != null) { newArgs.add(headStr + arg); headStr = null; } else { newArgs.add(arg); } } return (String[]) newArgs.toArray(new String[newArgs.size()]); } }