/** * Copyright (c) 2008-2016, XebiaLabs B.V., All rights reserved. * * * Overthere is licensed under the terms of the GPLv2 * <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most XebiaLabs Libraries. * There are special exceptions to the terms and conditions of the GPLv2 as it is applied to * this software, see the FLOSS License Exception * <http://github.com/xebialabs/overthere/blob/master/LICENSE>. * * 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, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth * Floor, Boston, MA 02110-1301 USA */ package com.xebialabs.overthere; import com.xebialabs.overthere.util.UnixCommandLineArgsSanitizer; import com.xebialabs.overthere.util.WindowsCommandLineArgsSanitizer; import java.io.Serializable; import static com.xebialabs.overthere.OperatingSystemFamily.UNIX; import static com.xebialabs.overthere.util.OverthereUtils.checkNotNull; /** * Represents a single command line argument. */ @SuppressWarnings("serial") public abstract class CmdLineArgument implements Serializable { /** * String used to encode an empty argument as a string. */ private static final String EMPTY_ARGUMENT = "\"\""; /** * Creates a regular argument. * * @param arg the argument string. * @return the created argument. */ public static CmdLineArgument arg(String arg) { checkNotNull(arg, "Cannot create a null argument"); return new Basic(arg); } /** * Creates a password argument. When encoded for execution, a password argument is encoded like a regular argument. * When encoded for logging, a password argument is always encoded as eight stars (********). * * @param arg the argument string. * @return the created argument. */ public static CmdLineArgument password(String arg) { checkNotNull(arg, "Cannot create a null password argument"); return new Password(arg); } /** * Creates a raw argument. When encoded for execution or for logging, a raw argument is left as-is. */ public static CmdLineArgument raw(String arg) { checkNotNull(arg, "Cannot create a null raw argument"); return new Raw(arg); } /** * Creates a nested command line argument. When encoded for execution or for logging, a nested command will be * quoted. Useful for instance when executing su -c '<nestedcommand>' * * @param line the nested command line * @return the created command */ public static CmdLineArgument nested(CmdLine line) { checkNotNull(line, "Cannot create a null nested command"); return new Nested(line); } /** * Returns a string representation of this argument. * * @param os the {@link OperatingSystemFamily operating system} to encode for. * @param forLogging <code>true</code> if this string representation will be used for logging. * @return the string representation of this argument. */ public abstract String toString(OperatingSystemFamily os, boolean forLogging); /** * Builds a string representation of this argument. * * @param os the {@link OperatingSystemFamily operating system} to encode for. * @param forLogging <code>true</code> if this string representation will be used for logging. * @param builder the {@link StringBuilder} to append to. */ public abstract void buildString(OperatingSystemFamily os, boolean forLogging, StringBuilder builder); protected void encodeString(String str, OperatingSystemFamily os, StringBuilder builder) { if (str.length() == 0) { builder.append(EMPTY_ARGUMENT); return; } switch (os) { case WINDOWS: if (!WindowsCommandLineArgsSanitizer.containsAnySpecialChars(str)) { builder.append(str); } else { builder.append(WindowsCommandLineArgsSanitizer.sanitize(str)); } break; case UNIX: case ZOS: if (!UnixCommandLineArgsSanitizer.containsAnySpecialChars(str)) { builder.append(str); } else { builder.append(UnixCommandLineArgsSanitizer.sanitize(str)); } break; default: throw new RuntimeException("Unknown os " + os); } } private abstract static class Single extends CmdLineArgument { protected String arg; private Single(String arg) { this.arg = arg; } @Override public String toString() { return toString(UNIX, true); } } private static class Raw extends Single { public Raw(String arg) { super(arg); } @Override public String toString(OperatingSystemFamily os, boolean forLogging) { return arg; } @Override public void buildString(OperatingSystemFamily os, boolean forLogging, StringBuilder builder) { if (arg.length() == 0) { builder.append(EMPTY_ARGUMENT); } else { builder.append(arg); } } } private static class Basic extends Single { public Basic(String arg) { super(arg); } @Override public String toString(OperatingSystemFamily os, boolean forLogging) { return arg; } @Override public void buildString(OperatingSystemFamily os, boolean forLogging, StringBuilder builder) { String s = arg; encodeString(s, os, builder); } } private static class Password extends Basic { private static final String HIDDEN_PASSWORD = "********"; public Password(String arg) { super(arg); } @Override public String toString(OperatingSystemFamily os, boolean forLogging) { if(forLogging) { return HIDDEN_PASSWORD; } else { return arg; } } @Override public void buildString(OperatingSystemFamily os, boolean forLogging, StringBuilder builder) { if (forLogging) { builder.append(HIDDEN_PASSWORD); } else { super.buildString(os, forLogging, builder); } } } private static class Nested extends CmdLineArgument { private final CmdLine line; public Nested(CmdLine line) { this.line = line; } public String toString(OperatingSystemFamily os, boolean forLogging) { StringBuilder builder = new StringBuilder(); encodeString(line.toCommandLine(os, forLogging), os, builder); return builder.toString(); } @Override public void buildString(OperatingSystemFamily os, boolean forLogging, StringBuilder builder) { encodeString(line.toCommandLine(os, forLogging), os, builder); } @Override public String toString() { return line.toString(); } } }