/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.hadoop.fs;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.hadoop.fs.FsShell.CmdHandler;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.fs.permission.ChmodParser;


/**
 * This class is the home for file permissions related commands.
 * Moved to this seperate class since FsShell is getting too large.
 */
class FsShellPermissions {
  
  /*========== chmod ==========*/
   
  /* The pattern is alsmost as flexible as mode allowed by 
   * chmod shell command. The main restriction is that we recognize only rwxX.
   * To reduce errors we also enforce 3 digits for octal mode.
   */  
  
  static String CHMOD_USAGE = 
                            "-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...";

  private static  ChmodParser pp;
  
  private static class ChmodHandler extends CmdHandler {

    ChmodHandler(FileSystem fs, String modeStr) throws IOException {
      super("chmod", fs);
      try {
        pp = new ChmodParser(modeStr);
      } catch(IllegalArgumentException iea) {
        patternError(iea.getMessage());
      }
    }

    private void patternError(String mode) throws IOException {
     throw new IOException("chmod : mode '" + mode + 
         "' does not match the expected pattern.");      
    }
    
    @Override
    public void run(FileStatus file, FileSystem srcFs) throws IOException {
      int newperms = pp.applyNewPermission(file);

      if (file.getPermission().toShort() != newperms) {
        try {
          srcFs.setPermission(file.getPath(), 
                                new FsPermission((short)newperms));
        } catch (IOException e) {
          System.err.println(getName() + ": changing permissions of '" + 
                             file.getPath() + "':" + e.getMessage().split("\n")[0]);
        }
      }
    }
  }

  /*========== chown ==========*/
  
  static private String allowedChars = "[-_./@a-zA-Z0-9]";
  ///allows only "allowedChars" above in names for owner and group
  static private Pattern chownPattern = 
         Pattern.compile("^\\s*(" + allowedChars + "+)?" +
                          "([:](" + allowedChars + "*))?\\s*$");
  static private Pattern chgrpPattern = 
         Pattern.compile("^\\s*(" + allowedChars + "+)\\s*$");
  
  static String CHOWN_USAGE = "-chown [-R] [OWNER][:[GROUP]] PATH...";
  static String CHGRP_USAGE = "-chgrp [-R] GROUP PATH...";  

  private static class ChownHandler extends CmdHandler {
    protected String owner = null;
    protected String group = null;

    protected ChownHandler(String cmd, FileSystem fs) { //for chgrp
      super(cmd, fs);
    }

    ChownHandler(FileSystem fs, String ownerStr) throws IOException {
      super("chown", fs);
      Matcher matcher = chownPattern.matcher(ownerStr);
      if (!matcher.matches()) {
        throw new IOException("'" + ownerStr + "' does not match " +
                              "expected pattern for [owner][:group].");
      }
      owner = matcher.group(1);
      group = matcher.group(3);
      if (group != null && group.length() == 0) {
        group = null;
      }
      if (owner == null && group == null) {
        throw new IOException("'" + ownerStr + "' does not specify " +
                              " owner or group.");
      }
    }

    @Override
    public void run(FileStatus file, FileSystem srcFs) throws IOException {
      //Should we do case insensitive match?  
      String newOwner = (owner == null || owner.equals(file.getOwner())) ?
                        null : owner;
      String newGroup = (group == null || group.equals(file.getGroup())) ?
                        null : group;

      if (newOwner != null || newGroup != null) {
        try {
          srcFs.setOwner(file.getPath(), newOwner, newGroup);
        } catch (IOException e) {
          System.err.println(getName() + ": changing ownership of '" + 
                             file.getPath() + "':" + e.getMessage().split("\n")[0]);

        }
      }
    }
  }

  /*========== chgrp ==========*/    
  
  private static class ChgrpHandler extends ChownHandler {
    ChgrpHandler(FileSystem fs, String groupStr) throws IOException {
      super("chgrp", fs);

      Matcher matcher = chgrpPattern.matcher(groupStr);
      if (!matcher.matches()) {
        throw new IOException("'" + groupStr + "' does not match " +
        "expected pattern for group");
      }
      group = matcher.group(1);
    }
  }

  static int changePermissions(FileSystem fs, String cmd, 
                                String argv[], int startIndex, FsShell shell)
                                throws IOException {
    CmdHandler handler = null;
    boolean recursive = false;

    // handle common arguments, currently only "-R" 
    for (; startIndex < argv.length && argv[startIndex].equals("-R"); 
    startIndex++) {
      recursive = true;
    }

    if ( startIndex >= argv.length ) {
      throw new IOException("Not enough arguments for the command");
    }

    if (cmd.equals("-chmod")) {
      handler = new ChmodHandler(fs, argv[startIndex++]);
    } else if (cmd.equals("-chown")) {
      handler = new ChownHandler(fs, argv[startIndex++]);
    } else if (cmd.equals("-chgrp")) {
      handler = new ChgrpHandler(fs, argv[startIndex++]);
    }

    return shell.runCmdHandler(handler, argv, startIndex, recursive);
  } 
}
