blob: 17335834d94cf4ea50f92f03372774fee0e4d6f9 [file] [log] [blame]
/*
* 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.channel;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.MapEntryUtils;
/**
* A enum describing the tty modes according to <a href="https://tools.ietf.org/html/rfc4254#section-8">RFC 4254 -
* section 8</a>.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public enum PtyMode {
/////////////////////////////// Chars ////////////////////////////////////
/**
* Interrupt character; 255 if none. Similarly for the other characters. Not all of these characters are supported
* on all systems.
*/
VINTR(1),
/**
* The quit character (sends SIGQUIT signal on POSIX systems).
*/
VQUIT(2),
/**
* Erase the character to left of the cursor.
*/
VERASE(3),
/**
* Kill the current input line.
*/
VKILL(4),
/**
* End-of-file character (sends EOF from the terminal).
*/
VEOF(5),
/**
* End-of-line character in addition to carriage return and/or line-feed.
*/
VEOL(6),
/**
* Additional end-of-line character.
*/
VEOL2(7),
/**
* Continues paused output (normally control-Q).
*/
VSTART(8),
/**
* Pauses output (normally control-S).
*/
VSTOP(9),
/**
* Suspends the current program.
*/
VSUSP(10),
/**
* Another suspend character.
*/
VDSUSP(11),
/**
* Reprints the current input line.
*/
VREPRINT(12),
/**
* Erases a word left of cursor.
*/
VWERASE(13),
/**
* Enter the next character typed literally, even if it is a special character
*/
VLNEXT(14),
/**
* Character to flush output.
*/
VFLUSH(15),
/**
* Switch to a different shell layer.
*/
VSWTCH(16),
/**
* Prints system status line (load, command, pid, etc).
*/
VSTATUS(17),
/**
* Toggles the flushing of terminal output.
*/
VDISCARD(18),
///////////////////////////////// I-flags ////////////////////////////////
/**
* The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE.
*/
IGNPAR(30),
/**
* Mark parity and framing errors.
*/
PARMRK(31),
/**
* Enable checking of parity errors.
*/
INPCK(32),
/**
* Strip 8th bit off characters.
*/
ISTRIP(33),
/**
* Map NL into CR on input.
*/
INLCR(34),
/**
* Ignore CR on input.
*/
IGNCR(35),
/**
* Map CR to NL on input.
*/
ICRNL(36),
/**
* Translate uppercase characters to lowercase.
*/
IUCLC(37),
/**
* Enable output flow control.
*/
IXON(38),
/**
* Any char will restart after stop.
*/
IXANY(39),
/**
* Enable input flow control.
*/
IXOFF(40),
/**
* Ring bell on input queue full.
*/
IMAXBEL(41),
/**
* @see <A HREF="https://tools.ietf.org/html/rfc8160">IUTF8 Terminal Mode in Secure Shell</A>
*/
IUTF8(42),
/////////////////////////////// L-flags //////////////////////////////////
/**
* Enable signals INTR, QUIT, [D]SUSP.
*/
ISIG(50),
/**
* Canonicalize input lines.
*/
ICANON(51),
/**
* Enable input and output of uppercase characters by preceding their lowercase equivalents with &quot;\&quot;.
*/
XCASE(52),
/**
* Enable echoing.
*/
ECHO(53),
/**
* Visually erase chars.
*/
ECHOE(54),
/**
* Kill character discards current line.
*/
ECHOK(55),
/**
* Echo NL even if ECHO is off.
*/
ECHONL(56),
/**
* Don't flush after interrupt.
*/
NOFLSH(57),
/**
* Stop background jobs from output.
*/
TOSTOP(58),
/**
* Enable extensions.
*/
IEXTEN(59),
/**
* Echo control characters as ^(Char).
*/
ECHOCTL(60),
/**
* Visual erase for line kill.
*/
ECHOKE(61),
/**
* Retype pending input.
*/
PENDIN(62),
/////////////////////////// O-flags //////////////////////////////////////
/**
* Enable output processing.
*/
OPOST(70),
/**
* Convert lowercase to uppercase.
*/
OLCUC(71),
/**
* Map NL to CR-NL.
*/
ONLCR(72),
/**
* Translate carriage return to newline (output).
*/
OCRNL(73),
/**
* Translate newline to carriage return-newline (output).
*/
ONOCR(74),
/**
* Newline performs a carriage return (output).
*/
ONLRET(75),
//////////////////////////////// C-flags /////////////////////////////////
/**
* 7 bit mode.
*/
CS7(90),
/**
* 8 bit mode.
*/
CS8(91),
/**
* Parity enable.
*/
PARENB(92),
/**
* Odd parity, else even.
*/
PARODD(93),
/////////////////////////// Speed(s) /////////////////////////////////////
/**
* Specifies the input baud rate in bits per second.
*/
TTY_OP_ISPEED(128),
/**
* Specifies the output baud rate in bits per second.
*/
TTY_OP_OSPEED(129);
public static final byte TTY_OP_END = 0x00;
// objects that can be used to set {@link PtyMode}s as {@code true} or {@code false}
public static final Integer FALSE_SETTING = 0;
public static final Integer TRUE_SETTING = 1;
/**
* An un-modifiable {@link Set} of all defined {@link PtyMode}s
*/
public static final Set<PtyMode> MODES = Collections.unmodifiableSet(EnumSet.allOf(PtyMode.class));
public static final NavigableMap<Integer, PtyMode> COMMANDS = Collections.unmodifiableNavigableMap(
MapEntryUtils.toSortedMap(MODES, PtyMode::toInt, Function.identity(), Comparator.naturalOrder()));
/**
* A {@code null}-safe {@link ToIntFunction} that returns the {@link PtyMode#toInt()} value and (-1) for
* {@code null}
*/
public static final ToIntFunction<PtyMode> OPCODE_EXTRACTOR = op -> (op == null) ? -1 : op.toInt();
/**
* A {@code null}-safe {@link Comparator} of {@link PtyMode} values according to their {@link PtyMode#toInt()} value
*
* @see #OPCODE_EXTRACTOR
*/
public static final Comparator<PtyMode> BY_OPCODE = (o1, o2) -> {
int v1 = OPCODE_EXTRACTOR.applyAsInt(o1);
int v2 = OPCODE_EXTRACTOR.applyAsInt(o2);
return Integer.compare(v1, v2);
};
private final int v;
PtyMode(int v) {
this.v = v;
}
public int toInt() {
return v;
}
/**
* @param b The numeric value of the option
* @return The matching {@link PtyMode} or {@code null} if no match found
* @see #toInt()
*/
public static PtyMode fromInt(int b) {
return COMMANDS.get(0x00FF & b);
}
public static PtyMode fromName(String name) {
if (GenericUtils.isEmpty(name)) {
return null;
}
for (PtyMode m : MODES) {
if (name.equalsIgnoreCase(m.name())) {
return m;
}
}
return null;
}
/**
* @param options The options to enable - ignored if {@code null}/empty
* @return A {@link Map} where all the specified {@link PtyMode}s have {@link #TRUE_SETTING}
*/
public static Map<PtyMode, Integer> createEnabledOptions(PtyMode... options) {
return createEnabledOptions(GenericUtils.of(options));
}
/**
* @param options The options to enable - ignored if {@code null}/empty
* @return A {@link Map} where all the specified {@link PtyMode}s have {@link #TRUE_SETTING}
*/
public static Map<PtyMode, Integer> createEnabledOptions(Collection<PtyMode> options) {
if (GenericUtils.isEmpty(options)) {
return Collections.emptyMap();
}
Map<PtyMode, Integer> modes = new EnumMap<>(PtyMode.class);
for (PtyMode m : options) {
modes.put(m, TRUE_SETTING);
}
return modes;
}
public static Set<PtyMode> resolveEnabledOptions(Map<PtyMode, ?> modes, PtyMode... options) {
return resolveEnabledOptions(modes, GenericUtils.of(options));
}
/**
* @param modes The PTY settings - ignored if {@code null}/empty
* @param options The options that should be enabled
* @return A {@link Set} of all the {@link PtyMode}s that appeared in the settings and were enabled
* @see #getBooleanSettingValue(Map, PtyMode)
*/
public static Set<PtyMode> resolveEnabledOptions(Map<PtyMode, ?> modes, Collection<PtyMode> options) {
if (MapEntryUtils.isEmpty(modes) || GenericUtils.isEmpty(options)) {
return Collections.emptySet();
}
Set<PtyMode> enabled = EnumSet.noneOf(PtyMode.class);
for (PtyMode m : options) {
if (getBooleanSettingValue(modes, m)) {
enabled.add(m);
}
}
return enabled;
}
/**
* @param modes The current modes {@link Map}-ing
* @param m The required {@link PtyMode}
* @return {@code true} if <U>all</U> of these conditions hold:</BR>
* <UL>
* <LI>Modes map is not {@code null}/empty</LI>
* <LI>Required mode setting is not {@code null}</LI>
* <LI>The setting has a mapped value</LI>
* <LI>The mapped value is a {@link Number}</LI>
* <LI>The {@link Number#intValue()} is non-zero</LI>
* </UL>
* @see #getBooleanSettingValue(Object)
*/
public static boolean getBooleanSettingValue(Map<PtyMode, ?> modes, PtyMode m) {
if ((m == null) || MapEntryUtils.isEmpty(modes)) {
return false;
} else {
return getBooleanSettingValue(modes.get(m));
}
}
/**
* @param modes The {@link Map} of {@link PtyMode}s resolved by the &quot;pty-req&quot; message.
* @param enablers A {@link Collection} of enabler settings to be consulted
* @param defaultValue The default value to be used if no definite setting could be deduced
* @return {@code true} if the CR mode is enabled:</BR>
* <UL>
* <LI>If<tt>modes</tt> or <tt>enablers</tt> are {@code null}/empty then <tt>defaultValue</tt>
* is used</LI>
*
* <LI>If <U>any</U> of the <tt>enablers</tt> modes are enabled then the CR mode is enabled.
* </LI>
*
* <LI>If <U>none</U> of the <tt>enablers</tt> modes were specified then use
* <tt>defaultValue</tt></LI>
*
* <LI>Otherwise (i.e., at least one or more of the <tt>enablers</tt> modes were specified, but
* <U>all</U> of them said {@code no}) then {@code false}.</LI>
* </UL>
*/
public static boolean getBooleanSettingValue(
Map<PtyMode, ?> modes, Collection<PtyMode> enablers, boolean defaultValue) {
if (MapEntryUtils.isEmpty(modes) || GenericUtils.isEmpty(enablers)) {
return defaultValue;
}
int settingsCount = 0;
for (PtyMode m : enablers) {
Object v = modes.get(m);
if (v == null) {
continue;
}
settingsCount++;
// if any setting says yes then use it
if (getBooleanSettingValue(v)) {
return true;
}
}
// ALL (!) settings have said NO
if (settingsCount > 0) {
return false;
} else {
return defaultValue; // none of the settings has been found - assume default
}
}
/**
* @param v The value to be tested
* @return {@code true} if <U>all</U> of these conditions hold:</BR>
* <UL>
* <LI>The mapped value is a {@link Number}</LI>
* <LI>The {@link Number#intValue()} is non-zero</LI>
* </UL>
* @see #getBooleanSettingValue(int)
*/
public static boolean getBooleanSettingValue(Object v) {
return (v instanceof Number) && getBooleanSettingValue(((Number) v).intValue());
}
/**
* @param v The setting value
* @return {@code true} if value is non-zero
*/
public static boolean getBooleanSettingValue(int v) {
return v != 0;
}
/**
* @param m The {@link PtyMode}
* @return {@code true} if not {@code null} and one of the settings that refers to a character value - name
* usually starts with {@code Vxxx}
*/
public static boolean isCharSetting(PtyMode m) {
if (m == null) {
return false;
}
String name = m.name();
char ch = name.charAt(0);
return (ch == 'v') || (ch == 'V');
}
}