Telnet.java
/*
* 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.commons.net.telnet;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.Duration;
import java.util.Arrays;
import org.apache.commons.net.SocketClient;
class Telnet extends SocketClient {
static final boolean debug = /* true; */ false;
static final boolean debugoptions = /* true; */ false;
static final byte[] COMMAND_DO = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DO };
static final byte[] COMMAND_DONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.DONT };
static final byte[] COMMAND_WILL = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WILL };
static final byte[] COMMAND_WONT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.WONT };
static final byte[] COMMAND_SB = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SB };
static final byte[] COMMAND_SE = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.SE };
static final int WILL_MASK = 0x01;
static final int DO_MASK = 0x02;
static final int REQUESTED_WILL_MASK = 0x04;
static final int REQUESTED_DO_MASK = 0x08;
/* public */
static final int DEFAULT_PORT = 23;
/* TERMINAL-TYPE option (start) */
/**
* Terminal type option
*/
protected static final int TERMINAL_TYPE = 24;
/**
* Send (for subnegotiation)
*/
protected static final int TERMINAL_TYPE_SEND = 1;
/**
* Is (for subnegotiation)
*/
protected static final int TERMINAL_TYPE_IS = 0;
/**
* Is sequence (for subnegotiation)
*/
static final byte[] COMMAND_IS = { (byte) TERMINAL_TYPE, (byte) TERMINAL_TYPE_IS };
/* Code Section added for supporting AYT (start) */
/**
* AYT sequence
*/
static final byte[] COMMAND_AYT = { (byte) TelnetCommand.IAC, (byte) TelnetCommand.AYT };
private final int[] doResponse;
private final int[] willResponse;
private final int[] options;
/**
* Terminal type
*/
private String terminalType;
/* TERMINAL-TYPE option (end) */
/* open TelnetOptionHandler functionality (end) */
/* open TelnetOptionHandler functionality (start) */
/**
* Array of option handlers
*/
private final TelnetOptionHandler[] optionHandlers;
/**
* monitor to wait for AYT
*/
private final Object aytMonitor = new Object();
/**
* flag for AYT
*/
private volatile boolean aytFlag = true;
/* Code Section added for supporting AYT (end) */
/**
* The stream on which to spy
*/
private volatile OutputStream spyStream;
/**
* The notification handler
*/
private TelnetNotificationHandler notifhand;
/**
* Empty Constructor
*/
Telnet() {
setDefaultPort(DEFAULT_PORT);
doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
}
/* TERMINAL-TYPE option (start) */
/**
* This constructor lets you specify the terminal type.
*
* @param termtype - terminal type to be negotiated (ej. VT100)
*/
Telnet(final String termtype) {
setDefaultPort(DEFAULT_PORT);
doResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
willResponse = new int[TelnetOption.MAX_OPTION_VALUE + 1];
options = new int[TelnetOption.MAX_OPTION_VALUE + 1];
terminalType = termtype;
optionHandlers = new TelnetOptionHandler[TelnetOption.MAX_OPTION_VALUE + 1];
}
/* TERMINAL-TYPE option (end) */
/**
* Called upon connection.
*
* @throws IOException - Exception in I/O.
*/
@Override
protected void _connectAction_() throws IOException {
/* (start). BUGFIX: clean the option info for each connection */
for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) {
doResponse[ii] = 0;
willResponse[ii] = 0;
options[ii] = 0;
if (optionHandlers[ii] != null) {
optionHandlers[ii].setDo(false);
optionHandlers[ii].setWill(false);
}
}
/* (end). BUGFIX: clean the option info for each connection */
super._connectAction_();
_input_ = new BufferedInputStream(_input_);
_output_ = new BufferedOutputStream(_output_);
/* open TelnetOptionHandler functionality (start) */
for (int ii = 0; ii < TelnetOption.MAX_OPTION_VALUE + 1; ii++) {
if (optionHandlers[ii] != null) {
if (optionHandlers[ii].getInitLocal()) {
requestWill(optionHandlers[ii].getOptionCode());
}
if (optionHandlers[ii].getInitRemote()) {
requestDo(optionHandlers[ii].getOptionCode());
}
}
}
/* open TelnetOptionHandler functionality (end) */
}
/* Code Section added for supporting spystreams (start) */
/**
* Registers an OutputStream for spying what's going on in the Telnet session.
*
* @param spystream - OutputStream on which session activity will be echoed.
*/
void _registerSpyStream(final OutputStream spystream) {
spyStream = spystream;
}
/* Code Section added for supporting AYT (start) */
/**
* Sends an {@code Are You There (AYT)} sequence and waits for the result.
*
* @param timeout - Time to wait for a response.
* @throws IOException - Exception in I/O.
* @throws IllegalArgumentException - Illegal argument
* @throws InterruptedException - Interrupted during wait.
* @return true if AYT received a response, false otherwise
**/
final boolean _sendAYT(final Duration timeout) throws IOException, IllegalArgumentException, InterruptedException {
boolean retValue = false;
synchronized (aytMonitor) {
synchronized (this) {
aytFlag = false;
_output_.write(COMMAND_AYT);
_output_.flush();
}
aytMonitor.wait(timeout.toMillis());
if (!aytFlag) {
aytFlag = true;
} else {
retValue = true;
}
}
return retValue;
}
/* Code Section added for supporting AYT (end) */
/**
* Sends a command, automatically adds IAC prefix and flushes the output.
*
* @param cmd - command data to be sent
* @throws IOException - Exception in I/O.
* @since 3.0
*/
final synchronized void _sendCommand(final byte cmd) throws IOException {
_output_.write(TelnetCommand.IAC);
_output_.write(cmd);
_output_.flush();
}
/* open TelnetOptionHandler functionality (start) */
/**
* Manages subnegotiation for Terminal Type.
*
* @param subn - subnegotiation data to be sent
* @throws IOException - Exception in I/O.
**/
final synchronized void _sendSubnegotiation(final int[] subn) throws IOException {
if (debug) {
System.err.println("SEND SUBNEGOTIATION: ");
if (subn != null) {
System.err.println(Arrays.toString(subn));
}
}
if (subn != null) {
_output_.write(COMMAND_SB);
// Note _output_ is buffered, so might as well simplify by writing single bytes
for (final int element : subn) {
final byte b = (byte) element;
if (b == (byte) TelnetCommand.IAC) { // cast is necessary because IAC is outside the signed byte range
_output_.write(b); // double any IAC bytes
}
_output_.write(b);
}
_output_.write(COMMAND_SE);
/* Code Section added for sending the negotiation ASAP (start) */
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end) */
}
}
/* open TelnetOptionHandler functionality (end) */
/**
* Stops spying this Telnet.
*/
void _stopSpyStream() {
spyStream = null;
}
/**
* Registers a new TelnetOptionHandler for this telnet to use.
*
* @param opthand - option handler to be registered.
* @throws InvalidTelnetOptionException - The option code is invalid.
* @throws IOException on error
**/
void addOptionHandler(final TelnetOptionHandler opthand) throws InvalidTelnetOptionException, IOException {
final int optcode = opthand.getOptionCode();
if (!TelnetOption.isValidOption(optcode)) {
throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
}
if (optionHandlers[optcode] != null) {
throw new InvalidTelnetOptionException("Already registered option", optcode);
}
optionHandlers[optcode] = opthand;
if (isConnected()) {
if (opthand.getInitLocal()) {
requestWill(optcode);
}
if (opthand.getInitRemote()) {
requestDo(optcode);
}
}
}
/**
* Unregisters a TelnetOptionHandler.
*
* @param optcode - Code of the option to be unregistered.
* @throws InvalidTelnetOptionException - The option code is invalid.
* @throws IOException on error
**/
void deleteOptionHandler(final int optcode) throws InvalidTelnetOptionException, IOException {
if (!TelnetOption.isValidOption(optcode)) {
throw new InvalidTelnetOptionException("Invalid Option Code", optcode);
}
if (optionHandlers[optcode] == null) {
throw new InvalidTelnetOptionException("Unregistered option", optcode);
}
final TelnetOptionHandler opthand = optionHandlers[optcode];
optionHandlers[optcode] = null;
if (opthand.getWill()) {
requestWont(optcode);
}
if (opthand.getDo()) {
requestDont(optcode);
}
}
/* open TelnetOptionHandler functionality (end) */
/* Code Section added for supporting AYT (start) */
/**
* Processes the response of an AYT
*/
final synchronized void processAYTResponse() {
if (!aytFlag) {
synchronized (aytMonitor) {
aytFlag = true;
aytMonitor.notifyAll();
}
}
}
/* Code Section added for supporting AYT (end) */
/**
* Processes a COMMAND.
*
* @param command - option code to be set.
**/
void processCommand(final int command) {
if (debugoptions) {
System.err.println("RECEIVED COMMAND: " + command);
}
if (notifhand != null) {
notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_COMMAND, command);
}
}
/**
* Processes a {@code DO} request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void processDo(final int option) throws IOException {
if (debugoptions) {
System.err.println("RECEIVED DO: " + TelnetOption.getOption(option));
}
if (notifhand != null) {
notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DO, option);
}
boolean acceptNewState = false;
/* open TelnetOptionHandler functionality (start) */
if (optionHandlers[option] != null) {
acceptNewState = optionHandlers[option].getAcceptLocal();
} else if (option == TERMINAL_TYPE && terminalType != null && !terminalType.isEmpty()) {
acceptNewState = true;
}
/* TERMINAL-TYPE option (end) */
/* open TelnetOptionHandler functionality (start) */
if (willResponse[option] > 0) {
--willResponse[option];
if (willResponse[option] > 0 && stateIsWill(option)) {
--willResponse[option];
}
}
if (willResponse[option] == 0) {
if (requestedWont(option)) {
switch (option) {
default:
break;
}
if (acceptNewState) {
setWantWill(option);
sendWill(option);
} else {
++willResponse[option];
sendWont(option);
}
} else {
// Other end has acknowledged option.
switch (option) {
default:
break;
}
}
}
setWill(option);
}
/**
* Processes a {@code DONT} request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void processDont(final int option) throws IOException {
if (debugoptions) {
System.err.println("RECEIVED DONT: " + TelnetOption.getOption(option));
}
if (notifhand != null) {
notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_DONT, option);
}
if (willResponse[option] > 0) {
--willResponse[option];
if (willResponse[option] > 0 && stateIsWont(option)) {
--willResponse[option];
}
}
if (willResponse[option] == 0 && requestedWill(option)) {
switch (option) {
default:
break;
}
/* FIX for a BUG in the negotiation (start) */
if (stateIsWill(option) || requestedWill(option)) {
sendWont(option);
}
setWantWont(option);
/* FIX for a BUG in the negotiation (end) */
}
setWont(option);
}
/* TERMINAL-TYPE option (start) */
/**
* Processes a suboption negotiation.
*
* @param suboption - subnegotiation data received
* @param suboptionLength - length of data received
* @throws IOException - Exception in I/O.
**/
void processSuboption(final int[] suboption, final int suboptionLength) throws IOException {
if (debug) {
System.err.println("PROCESS SUBOPTION.");
}
/* open TelnetOptionHandler functionality (start) */
if (suboptionLength > 0) {
if (optionHandlers[suboption[0]] != null) {
final int[] responseSuboption = optionHandlers[suboption[0]].answerSubnegotiation(suboption, suboptionLength);
_sendSubnegotiation(responseSuboption);
} else if (suboptionLength > 1) {
if (debug) {
for (int ii = 0; ii < suboptionLength; ii++) {
System.err.println("SUB[" + ii + "]: " + suboption[ii]);
}
}
if (suboption[0] == TERMINAL_TYPE && suboption[1] == TERMINAL_TYPE_SEND) {
sendTerminalType();
}
}
}
/* open TelnetOptionHandler functionality (end) */
}
/**
* Processes a {@code WILL} request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void processWill(final int option) throws IOException {
if (debugoptions) {
System.err.println("RECEIVED WILL: " + TelnetOption.getOption(option));
}
if (notifhand != null) {
notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WILL, option);
}
boolean acceptNewState = false;
/* open TelnetOptionHandler functionality (start) */
if (optionHandlers[option] != null) {
acceptNewState = optionHandlers[option].getAcceptRemote();
}
/* open TelnetOptionHandler functionality (end) */
if (doResponse[option] > 0) {
--doResponse[option];
if (doResponse[option] > 0 && stateIsDo(option)) {
--doResponse[option];
}
}
if (doResponse[option] == 0 && requestedDont(option)) {
switch (option) {
default:
break;
}
if (acceptNewState) {
setWantDo(option);
sendDo(option);
} else {
++doResponse[option];
sendDont(option);
}
}
setDo(option);
}
/**
* Processes a {@code WONT} request.
*
* @param option - option code to be set.
* @throws IOException - Exception in I/O.
**/
void processWont(final int option) throws IOException {
if (debugoptions) {
System.err.println("RECEIVED WONT: " + TelnetOption.getOption(option));
}
if (notifhand != null) {
notifhand.receivedNegotiation(TelnetNotificationHandler.RECEIVED_WONT, option);
}
if (doResponse[option] > 0) {
--doResponse[option];
if (doResponse[option] > 0 && stateIsDont(option)) {
--doResponse[option];
}
}
if (doResponse[option] == 0 && requestedDo(option)) {
switch (option) {
default:
break;
}
/* FIX for a BUG in the negotiation (start) */
if (stateIsDo(option) || requestedDo(option)) {
sendDont(option);
}
setWantDont(option);
/* FIX for a BUG in the negotiation (end) */
}
setDont(option);
}
/**
* Registers a notification handler to which will be sent notifications of received telnet option negotiation commands.
*
* @param notifhand - TelnetNotificationHandler to be registered
*/
public void registerNotifHandler(final TelnetNotificationHandler notifhand) {
this.notifhand = notifhand;
}
/**
* Requests a DO.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void requestDo(final int option) throws IOException {
if (doResponse[option] == 0 && stateIsDo(option) || requestedDo(option)) {
return;
}
setWantDo(option);
++doResponse[option];
sendDo(option);
}
/**
* Requests a {@code DONT}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void requestDont(final int option) throws IOException {
if (doResponse[option] == 0 && stateIsDont(option) || requestedDont(option)) {
return;
}
setWantDont(option);
++doResponse[option];
sendDont(option);
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code DO} has been requested.
*
* @param option - option code to be looked up.
*/
boolean requestedDo(final int option) {
return (options[option] & REQUESTED_DO_MASK) != 0;
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code DONT} has been requested
*
* @param option - option code to be looked up.
*/
boolean requestedDont(final int option) {
return !requestedDo(option);
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code WILL} has been requested
*
* @param option - option code to be looked up.
*/
boolean requestedWill(final int option) {
return (options[option] & REQUESTED_WILL_MASK) != 0;
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code WONT} has been requested
*
* @param option - option code to be looked up.
*/
boolean requestedWont(final int option) {
return !requestedWill(option);
}
/**
* Requests a {@code WILL}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void requestWill(final int option) throws IOException {
if (willResponse[option] == 0 && stateIsWill(option) || requestedWill(option)) {
return;
}
setWantWill(option);
++doResponse[option];
sendWill(option);
}
/* TERMINAL-TYPE option (end) */
/**
* Requests a {@code WONT}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void requestWont(final int option) throws IOException {
if (willResponse[option] == 0 && stateIsWont(option) || requestedWont(option)) {
return;
}
setWantWont(option);
++doResponse[option];
sendWont(option);
}
/**
* Sends a byte.
*
* @param b - byte to send
* @throws IOException - Exception in I/O.
**/
final synchronized void sendByte(final int b) throws IOException {
_output_.write(b);
/* Code Section added for supporting spystreams (start) */
spyWrite(b);
/* Code Section added for supporting spystreams (end) */
}
/**
* Sends a {@code DO}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void sendDo(final int option) throws IOException {
if (debug || debugoptions) {
System.err.println("DO: " + TelnetOption.getOption(option));
}
_output_.write(COMMAND_DO);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start) */
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end) */
}
/**
* Sends a {@code DONT}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void sendDont(final int option) throws IOException {
if (debug || debugoptions) {
System.err.println("DONT: " + TelnetOption.getOption(option));
}
_output_.write(COMMAND_DONT);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start) */
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end) */
}
/**
* Sends terminal type information.
*
* @throws IOException - Exception in I/O.
*/
final synchronized void sendTerminalType() throws IOException {
if (debug) {
System.err.println("SEND TERMINAL-TYPE: " + terminalType);
}
if (terminalType != null) {
_output_.write(COMMAND_SB);
_output_.write(COMMAND_IS);
_output_.write(terminalType.getBytes(getCharset()));
_output_.write(COMMAND_SE);
_output_.flush();
}
}
/**
* Sends a {@code WILL}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void sendWill(final int option) throws IOException {
if (debug || debugoptions) {
System.err.println("WILL: " + TelnetOption.getOption(option));
}
_output_.write(COMMAND_WILL);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start) */
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end) */
}
/**
* Sends a {@code WONT}.
*
* @param option - Option code.
* @throws IOException - Exception in I/O.
**/
final synchronized void sendWont(final int option) throws IOException {
if (debug || debugoptions) {
System.err.println("WONT: " + TelnetOption.getOption(option));
}
_output_.write(COMMAND_WONT);
_output_.write(option);
/* Code Section added for sending the negotiation ASAP (start) */
_output_.flush();
/* Code Section added for sending the negotiation ASAP (end) */
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
* @throws IOException
*/
void setDo(final int option) throws IOException {
options[option] |= DO_MASK;
/* open TelnetOptionHandler functionality (start) */
if (requestedDo(option) && optionHandlers[option] != null) {
optionHandlers[option].setDo(true);
final int[] subneg = optionHandlers[option].startSubnegotiationRemote();
if (subneg != null) {
_sendSubnegotiation(subneg);
}
}
/* open TelnetOptionHandler functionality (end) */
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setDont(final int option) {
options[option] &= ~DO_MASK;
/* open TelnetOptionHandler functionality (start) */
if (optionHandlers[option] != null) {
optionHandlers[option].setDo(false);
}
/* open TelnetOptionHandler functionality (end) */
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setWantDo(final int option) {
options[option] |= REQUESTED_DO_MASK;
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setWantDont(final int option) {
options[option] &= ~REQUESTED_DO_MASK;
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setWantWill(final int option) {
options[option] |= REQUESTED_WILL_MASK;
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setWantWont(final int option) {
options[option] &= ~REQUESTED_WILL_MASK;
}
/**
* Sets the state of the option.
*
* @param option - option code to be set.
* @throws IOException
*/
void setWill(final int option) throws IOException {
options[option] |= WILL_MASK;
/* open TelnetOptionHandler functionality (start) */
if (requestedWill(option) && optionHandlers[option] != null) {
optionHandlers[option].setWill(true);
final int[] subneg = optionHandlers[option].startSubnegotiationLocal();
if (subneg != null) {
_sendSubnegotiation(subneg);
}
}
/* open TelnetOptionHandler functionality (end) */
}
/* open TelnetOptionHandler functionality (start) */
/**
* Sets the state of the option.
*
* @param option - option code to be set.
*/
void setWont(final int option) {
options[option] &= ~WILL_MASK;
/* open TelnetOptionHandler functionality (start) */
if (optionHandlers[option] != null) {
optionHandlers[option].setWill(false);
}
/* open TelnetOptionHandler functionality (end) */
}
/**
* Sends a read char on the spy stream.
*
* @param ch - character read from the session
*/
void spyRead(final int ch) {
final OutputStream spy = spyStream;
if (spy != null) {
try {
if (ch != '\r') // never write '\r' on its own
{
if (ch == '\n') {
spy.write('\r'); // add '\r' before '\n'
}
spy.write(ch); // write original character
spy.flush();
}
} catch (final IOException e) {
spyStream = null;
}
}
}
/**
* Sends a written char on the spy stream.
*
* @param ch - character written to the session
*/
void spyWrite(final int ch) {
if (!(stateIsDo(TelnetOption.ECHO) && requestedDo(TelnetOption.ECHO))) {
final OutputStream spy = spyStream;
if (spy != null) {
try {
spy.write(ch);
spy.flush();
} catch (final IOException e) {
spyStream = null;
}
}
}
}
/* Code Section added for supporting spystreams (end) */
/**
* Looks for the state of the option.
*
* @return returns true if a {@code DO} has been acknowledged.
*
* @param option - option code to be looked up.
*/
boolean stateIsDo(final int option) {
return (options[option] & DO_MASK) != 0;
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code DONT} has been acknowledged
*
* @param option - option code to be looked up.
*/
boolean stateIsDont(final int option) {
return !stateIsDo(option);
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code WILL} has been acknowledged
*
* @param option - option code to be looked up.
*/
boolean stateIsWill(final int option) {
return (options[option] & WILL_MASK) != 0;
}
/**
* Looks for the state of the option.
*
* @return returns true if a {@code WONT} has been acknowledged
*
* @param option - option code to be looked up.
*/
boolean stateIsWont(final int option) {
return !stateIsWill(option);
}
/**
* Unregisters the current notification handler.
*/
public void unregisterNotifHandler() {
this.notifhand = null;
}
}