/* * Copyright (C) 2012 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.cyanogenmod.filemanager.commands.shell; import android.content.res.Resources; import android.content.res.XmlResourceParser; import com.android.internal.util.XmlUtils; import com.cyanogenmod.filemanager.FileManagerApplication; import com.cyanogenmod.filemanager.R; import com.cyanogenmod.filemanager.console.CommandNotFoundException; import com.cyanogenmod.filemanager.console.ExecutionException; import com.cyanogenmod.filemanager.console.InsufficientPermissionsException; import com.cyanogenmod.filemanager.preferences.FileManagerSettings; import com.cyanogenmod.filemanager.preferences.Preferences; import com.cyanogenmod.filemanager.util.ShellHelper; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** * An abstract class that represents a command to be executed * in the underlying operating system. * * @see "command_list.xml" */ public abstract class Command { // Command list XML tags private static final String TAG_COMMAND_LIST = "CommandList"; //$NON-NLS-1$ private static final String TAG_COMMAND = "command"; //$NON-NLS-1$ private static final String TAG_STARTCODE = "startcode"; //$NON-NLS-1$ private static final String TAG_EXITCODE = "exitcode"; //$NON-NLS-1$ private static final String EXPANDED_ARGS = "[@]"; //$NON-NLS-1$ private final String mId; private String mCmd; private String mArgs; // The real arguments private final Object[] mCmdArgs; //The arguments to be formatted private static String sStartCodeCmd; private static String sExitCodeCmd; private boolean mTrace; /** * @Constructor of <code>Command</code> * * @param id The resource identifier of the command * @param args Arguments of the command (will be formatted with the arguments from * the command definition) * @throws InvalidCommandDefinitionException If the command has an invalid definition */ public Command(String id, String... args) throws InvalidCommandDefinitionException { this(id, true, args); } /** * @Constructor of <code>Command</code> * * @param id The resource identifier of the command * @param prepare Indicates if the argument must be prepared * @param args Arguments of the command (will be formatted with the arguments from * the command definition) * @throws InvalidCommandDefinitionException If the command has an invalid definition */ public Command(String id, boolean prepare, String... args) throws InvalidCommandDefinitionException { super(); this.mId = id; //Convert and quote arguments this.mCmdArgs = new Object[args.length]; int cc = args.length; for (int i = 0; i < cc; i++) { //Quote the arguments? if (prepare) { this.mCmdArgs[i] = "\"" + ShellHelper.prepareArgument(args[i]) //$NON-NLS-1$ + "\""; //$NON-NLS-1$ } else { this.mCmdArgs[i] = ShellHelper.prepareArgument(args[i]); } } //Load the command info getCommandInfo(FileManagerApplication.getInstance().getResources()); // Get the current trace value reloadTrace(); } /** * Method that add expended arguments to the arguments. This is defined with a * <code>[@]</code> expression in the <code>commandArgs</code> attribute of the * command xml definition file. * * @param args The expanded arguments * @param prepare Indicates if the argument must be prepared */ protected void addExpandedArguments(String[] args, boolean prepare) { // Don't use of regexp to avoid the need to parse of args to make it compilable. // Only one expanded argument of well known characters int pos = this.mArgs.indexOf(EXPANDED_ARGS); if (pos != -1) { int cc = args.length; StringBuffer sb = new StringBuffer(); for (int i = 0; i < cc; i++) { //Quote the arguments? if (prepare) { sb = sb.append("\"" + //$NON-NLS-1$ ShellHelper.prepareArgument(args[i]) + "\""); //$NON-NLS-1$ sb = sb.append(" "); //$NON-NLS-1$ } else { sb = sb.append(ShellHelper.prepareArgument(args[i])); sb = sb.append(" "); //$NON-NLS-1$ } } // Replace the expanded argument String start = this.mArgs.substring(0, pos); String end = this.mArgs.substring(pos+EXPANDED_ARGS.length()); this.mArgs = start + sb.toString() + end; } } /** * Method that return if the command has to trace his operations * * @return boolean If the command has to trace */ public boolean isTrace() { return this.mTrace; } /** * Method that reload the status of trace setting */ public final void reloadTrace() { this.mTrace = Preferences.getSharedPreferences().getBoolean( FileManagerSettings.SETTINGS_SHOW_TRACES.getId(), ((Boolean)FileManagerSettings.SETTINGS_SHOW_TRACES.getDefaultValue()).booleanValue()); } /** * Method that checks if the result code of the execution was successfully. * * @param exitCode Program exit code * @throws InsufficientPermissionsException If an operation requires elevated permissions * @throws CommandNotFoundException If the command was not found * @throws ExecutionException If the operation returns a invalid exit code * @hide */ public abstract void checkExitCode(int exitCode) throws InsufficientPermissionsException, CommandNotFoundException, ExecutionException; /** * Method that returns the resource identifier of the command. * * @return String The resource identifier of the command */ public String getId() { return this.mId; } /** * This method must returns the name of the full qualified command path.<br /> * <br /> * This method always must returns a full qualified path, and not an * abbreviation to the command to avoid security problems.<br /> * In the same way, a command not must contains any type of arguments. * Arguments must be passed through method {@link #getArguments()} * * @return String The full qualified command path * @see #getArguments() */ public String getCommand() { return this.mCmd; } /** * This method can return the list of arguments to be executed along * with the command. * * @return String A list of individual arguments */ public String getArguments() { return this.mArgs; } /** * Method that loads the resource command list xml and * inflate the internal variables. * * @param resources The application resource manager * @throws InvalidCommandDefinitionException If the command has an invalid definition */ private void getCommandInfo(Resources resources) throws InvalidCommandDefinitionException { //Read the command list xml file XmlResourceParser parser = resources.getXml(R.xml.command_list); try { //Find the root element XmlUtils.beginDocument(parser, TAG_COMMAND_LIST); while (true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) { break; } if (TAG_COMMAND.equals(element)) { CharSequence id = parser.getAttributeValue(R.styleable.Command_commandId); if (id != null && id.toString().compareTo(this.mId) == 0) { CharSequence path = parser.getAttributeValue(R.styleable.Command_commandPath); CharSequence args = parser.getAttributeValue(R.styleable.Command_commandArgs); if (path == null) { throw new InvalidCommandDefinitionException( this.mId + ": path is null"); //$NON-NLS-1$ } if (args == null) { throw new InvalidCommandDefinitionException( this.mId + ": args is null"); //$NON-NLS-1$ } //Save paths this.mCmd = path.toString(); this.mArgs = args.toString(); //Format the arguments of the process with the command arguments if (this.mArgs != null && this.mArgs.length() > 0 && this.mCmdArgs != null && this.mCmdArgs.length > 0) { this.mArgs = String.format(this.mArgs, this.mCmdArgs); } return; } } } } catch (XmlPullParserException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { parser.close(); } //Command not found throw new InvalidCommandDefinitionException(this.mId); } /** * Method that returns the exit code command info. * * @param resources The application resource manager * @return String The exit code command info * @throws InvalidCommandDefinitionException If the command is not present or has an * invalid definition */ public static synchronized String getStartCodeCommandInfo( Resources resources) throws InvalidCommandDefinitionException { //Singleton if (sStartCodeCmd != null) { return new String(sStartCodeCmd); } //Read the command list xml file XmlResourceParser parser = resources.getXml(R.xml.command_list); try { //Find the root element XmlUtils.beginDocument(parser, TAG_COMMAND_LIST); while (true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) { break; } if (TAG_STARTCODE.equals(element)) { CharSequence path = parser.getAttributeValue(R.styleable.Command_commandPath); if (path == null) { throw new InvalidCommandDefinitionException( TAG_STARTCODE + ": path is null"); //$NON-NLS-1$ } //Save paths sStartCodeCmd = path.toString(); return new String(sStartCodeCmd); } } } catch (XmlPullParserException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { parser.close(); } //Command not found throw new InvalidCommandDefinitionException(TAG_STARTCODE); } /** * Method that returns the exit code command info. * * @param resources The application resource manager * @return String The exit code command info * @throws InvalidCommandDefinitionException If the command is not present or has an * invalid definition */ public static synchronized String getExitCodeCommandInfo( Resources resources) throws InvalidCommandDefinitionException { //Singleton if (sExitCodeCmd != null) { return new String(sExitCodeCmd); } //Read the command list xml file XmlResourceParser parser = resources.getXml(R.xml.command_list); try { //Find the root element XmlUtils.beginDocument(parser, TAG_COMMAND_LIST); while (true) { XmlUtils.nextElement(parser); String element = parser.getName(); if (element == null) { break; } if (TAG_EXITCODE.equals(element)) { CharSequence path = parser.getAttributeValue(R.styleable.Command_commandPath); if (path == null) { throw new InvalidCommandDefinitionException( TAG_EXITCODE + ": path is null"); //$NON-NLS-1$ } //Save paths sExitCodeCmd = path.toString(); return new String(sExitCodeCmd); } } } catch (XmlPullParserException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { parser.close(); } //Command not found throw new InvalidCommandDefinitionException(TAG_EXITCODE); } }