/*
* 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.sshd.common.subsystem.sftp.extensions;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import org.apache.sshd.common.subsystem.sftp.SftpConstants;
import org.apache.sshd.common.subsystem.sftp.extensions.AclSupportedParser.AclCapabilities;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.buffer.Buffer;
import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
import org.apache.sshd.common.util.logging.LoggingUtils;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public class AclSupportedParser extends AbstractParser<AclCapabilities> {
/**
* The "acl-supported" information as per
* <A HREF="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-11">DRAFT 11 - section 5.4</A>
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public static class AclCapabilities implements Serializable, Cloneable {
private static final long serialVersionUID = -3118426327336468237L;
private int capabilities;
public AclCapabilities() {
this(0);
}
public AclCapabilities(int capabilities) {
this.capabilities = capabilities;
}
public int getCapabilities() {
return capabilities;
}
public void setCapabilities(int capabilities) {
this.capabilities = capabilities;
}
@Override
public int hashCode() {
return getCapabilities();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (getClass() != obj.getClass()) {
return false;
}
return ((AclCapabilities) obj).getCapabilities() == getCapabilities();
}
@Override
public AclCapabilities clone() {
try {
return getClass().cast(super.clone());
} catch (CloneNotSupportedException e) {
throw new RuntimeException("Failed to clone " + toString() + ": " + e.getMessage(), e);
}
}
@Override
public String toString() {
return Objects.toString(decodeAclCapabilities(getCapabilities()));
}
private static class LazyAclCapabilityNameHolder {
private static final String ACL_CAP_NAME_PREFIX = "SSH_ACL_CAP_";
private static final Map<Integer, String> ACL_VALUES_MAP = LoggingUtils.generateMnemonicMap(SftpConstants.class, ACL_CAP_NAME_PREFIX);
private static final Map<String, Integer> ACL_NAMES_MAP =
Collections.unmodifiableMap(GenericUtils.flipMap(ACL_VALUES_MAP, GenericUtils.caseInsensitiveMap(), false));
}
@SuppressWarnings("synthetic-access")
public static Map<String, Integer> getAclCapabilityNamesMap() {
return LazyAclCapabilityNameHolder.ACL_NAMES_MAP;
}
/**
* @param name The ACL capability name - may be without the "SSH_ACL_CAP_xxx" prefix.
* Ignored if {@code null}/empty
* @return The matching {@link Integer} value - or {@code null} if no match found
*/
public static Integer getAclCapabilityValue(String name) {
if (GenericUtils.isEmpty(name)) {
return null;
}
name = name.toUpperCase();
if (!name.startsWith(LazyAclCapabilityNameHolder.ACL_CAP_NAME_PREFIX)) {
name += LazyAclCapabilityNameHolder.ACL_CAP_NAME_PREFIX;
}
Map<String, Integer> map = getAclCapabilityNamesMap();
return map.get(name);
}
@SuppressWarnings("synthetic-access")
public static Map<Integer, String> getAclCapabilityValuesMap() {
return LazyAclCapabilityNameHolder.ACL_VALUES_MAP;
}
public static String getAclCapabilityName(int aclCapValue) {
Map<Integer, String> map = getAclCapabilityValuesMap();
String name = map.get(aclCapValue);
if (GenericUtils.isEmpty(name)) {
return Integer.toString(aclCapValue);
} else {
return name;
}
}
public static Set<String> decodeAclCapabilities(int mask) {
if (mask == 0) {
return Collections.emptySet();
}
Set<String> caps = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
Map<Integer, String> map = getAclCapabilityValuesMap();
map.forEach((value, name) -> {
if ((mask & value) != 0) {
caps.add(name);
}
});
return caps;
}
public static int constructAclCapabilities(Collection<Integer> maskValues) {
if (GenericUtils.isEmpty(maskValues)) {
return 0;
}
int mask = 0;
for (Integer v : maskValues) {
mask |= v;
}
return mask;
}
public static Set<Integer> deconstructAclCapabilities(int mask) {
if (mask == 0) {
return Collections.emptySet();
}
Map<Integer, String> map = getAclCapabilityValuesMap();
Set<Integer> caps = new HashSet<>(map.size());
for (Integer v : map.keySet()) {
if ((mask & v) != 0) {
caps.add(v);
}
}
return caps;
}
}
public static final AclSupportedParser INSTANCE = new AclSupportedParser();
public AclSupportedParser() {
super(SftpConstants.EXT_ACL_SUPPORTED);
}
@Override
public AclCapabilities parse(byte[] input, int offset, int len) {
return parse(new ByteArrayBuffer(input, offset, len));
}
public AclCapabilities parse(Buffer buffer) {
return new AclCapabilities(buffer.getInt());
}
}