summaryrefslogtreecommitdiff
path: root/src/terminal/internal/fqterm_telnet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal/internal/fqterm_telnet.cpp')
-rw-r--r--src/terminal/internal/fqterm_telnet.cpp1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/src/terminal/internal/fqterm_telnet.cpp b/src/terminal/internal/fqterm_telnet.cpp
new file mode 100644
index 0000000..2446b8a
--- /dev/null
+++ b/src/terminal/internal/fqterm_telnet.cpp
@@ -0,0 +1,1041 @@
+/***************************************************************************
+ * fqterm, a terminal emulator for both BBS and *nix. *
+ * Copyright (C) 2008 fqterm development group. *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <QAbstractSocket>
+
+#include "fqterm.h"
+#include "fqterm_telnet.h"
+#include "fqterm_socket.h"
+
+#ifndef _NO_SSH_COMPILED
+#include "fqterm_ssh_socket.h"
+#endif
+
+/* TELNET Command Codes: */
+/* Hints: These NVT control characters are sent from client to server, So
+ the client side will not receive these commands */
+#define TCSB (u_char)250 /* Start Subnegotiation */
+#define TCSE (u_char)240 /* End Of Subnegotiation */
+#define TCNOP (u_char)241 /* No Operation */
+#define TCDM (u_char)242 /* Data Mark (for Sync) */
+#define TCBRK (u_char)243 /* NVT Character BRK */
+#define TCIP (u_char)244 /* Interrupt Process */
+#define TCAO (u_char)245 /* Abort Output */
+#define TCAYT (u_char)246 /* "Are You There?" Function */
+#define TCEC (u_char)247 /* Erase Character */
+#define TCEL (u_char)248 /* Erase Line */
+#define TCGA (u_char)249 /* "Go Ahead" Function */
+#define TCWILL (u_char)251 /* Desire/Confirm Will Do Option*/
+#define TCWONT (u_char)252 /* Refusal To Do Option */
+#define TCDO (u_char)253 /* Request To Do Option */
+#define TCDONT (u_char)254 /* Request NOT To Do Option */
+#define TCIAC (u_char)255 /* Interpret As Command Escape */
+
+/* Telnet Option Codes: */
+#define TOTXBINARY (u_char) 0 /* TRANSMIT-BINARY option */
+#define TOECHO (u_char) 1 /* ECHO Option */
+#define TONOGA (u_char) 3 /* Suppress Go-Ahead Option */
+#define TOTERMTYPE (u_char) 24 /* Terminal-Type Option */
+#define TONAWS (u_char) 31 /* Window Size */
+
+/* Network Virtual Printer Special Characters: */
+/* In normal situations, these characters will to translated into local
+ control characters , then pass to upper layer term. But in our situation,
+ we can pass them to term directly */
+
+#define VPLF '\n' /* Line Feed */
+#define VPCR '\r' /* Carriage Return */
+#define VPBEL '\a' /* Bell (attention signal) */
+#define VPBS '\b' /* Back Space */
+#define VPHT '\t' /* Horizontal Tab */
+#define VPVT '\v' /* Vertical Tab */
+#define VPFF '\f' /* Form Feed */
+
+/* Keyboard Command Characters: */
+
+/* Option Subnegotiation Constants: */
+#define TT_IS 0 /* TERMINAL_TYPE option "IS" command */
+#define TT_SEND 1 /* TERMINAL_TYPE option "SEND" command */
+
+/* Telnet Socket-Input FSM States: */
+#define TSDATA 0 /* normal data processing */
+#define TSIAC 1 /* have seen IAC */
+#define TSWOPT 2 /* have seen IAC-{WILL/WONT} */
+#define TSDOPT 3 /* have seen IAC-{DO/DONT} */
+#define TSSUBNEG 4 /* have seen IAC-SB */
+#define TSSUBIAC 5 /* have seen IAC-SB-...-IAC */
+// if any state added here, please update NTSTATES (currently 6).
+
+// Telnet Option Subnegotiation FSM States:
+#define SS_START 0 // initial state
+#define SS_TERMTYPE 1 // TERMINAL_TYPE option subnegotiation
+#define SS_END 2 // state after all legal input
+// if any state added here, please update NSSTATES (currently 3).
+
+#define FSINVALID 0xff // an invalid state number
+#define TCANY (NCHRS+1) // match any character
+
+#define TINVALID 0xff // an invalid transition index
+
+namespace FQTerm {
+
+struct fsm_trans FQTermTelnet::ttstab[] = {
+ /* State Input Next State Action */
+ /* ------ ------ ----------- ------- */
+ {
+ TSDATA, TCIAC, TSIAC, &FQTermTelnet::no_op
+ }, {
+ TSDATA, TCANY, TSDATA, &FQTermTelnet::ttputc
+ }, {
+ TSIAC, TCIAC, TSDATA, &FQTermTelnet::ttputc
+ }, {
+ TSIAC, TCSB, TSSUBNEG, &FQTermTelnet::no_op
+ }, {/* Telnet Commands */
+ TSIAC, TCNOP, TSDATA, &FQTermTelnet::no_op
+ }, {
+ TSIAC, TCDM, TSDATA, &FQTermTelnet::tcdm
+ }, { /* Option Negotiation */
+ TSIAC, TCWILL, TSWOPT, &FQTermTelnet::recopt
+ }, {
+ TSIAC, TCWONT, TSWOPT, &FQTermTelnet::recopt
+ }, {
+ TSIAC, TCDO, TSDOPT, &FQTermTelnet::recopt
+ }, {
+ TSIAC, TCDONT, TSDOPT, &FQTermTelnet::recopt
+ }, {
+ TSIAC, TCANY, TSDATA, &FQTermTelnet::no_op
+ }, { /* Option Subnegotion */
+ TSSUBNEG, TCIAC, TSSUBIAC, &FQTermTelnet::no_op
+ }, {
+ TSSUBNEG, TCANY, TSSUBNEG, &FQTermTelnet::subopt
+ }, {
+ TSSUBIAC, TCSE, TSDATA, &FQTermTelnet::subend
+ }, {
+ TSSUBIAC, TCANY, TSSUBNEG, &FQTermTelnet::subopt
+ }, {
+ TSWOPT, TOECHO, TSDATA, &FQTermTelnet::do_echo
+ }, {
+ TSWOPT, TONOGA, TSDATA, &FQTermTelnet::do_noga
+ }, {
+ TSWOPT, TOTXBINARY, TSDATA, &FQTermTelnet::do_txbinary
+ }, {
+ TSWOPT, TCANY, TSDATA, &FQTermTelnet::do_notsup
+ },{
+ TSDOPT, TONAWS, TSDATA, &FQTermTelnet::will_naws
+ }, {
+ TSDOPT, TOTERMTYPE, TSDATA, &FQTermTelnet::will_termtype
+ }, {
+ TSDOPT, TOTXBINARY, TSDATA, &FQTermTelnet::will_txbinary
+ }, {
+ TSDOPT, TCANY, TSDATA, &FQTermTelnet::will_notsup
+ }, {
+ FSINVALID, TCANY, FSINVALID, &FQTermTelnet::tnabort
+ },
+};
+
+
+struct fsm_trans FQTermTelnet::substab[] = {
+ /* State Input Next State Action */
+ /* ---------------------------------------------------- */
+ {
+ SS_START, TOTERMTYPE, SS_TERMTYPE, &FQTermTelnet::no_op
+ }, {
+ SS_START, TCANY, SS_END, &FQTermTelnet::no_op
+ }, {
+ SS_TERMTYPE, TT_SEND, SS_END, &FQTermTelnet::subtermtype
+ }, {
+ SS_TERMTYPE, TCANY, SS_END, &FQTermTelnet::no_op
+ }, {
+ SS_END, TCANY, SS_END, &FQTermTelnet::no_op
+ }, {
+ FSINVALID, TCANY, FSINVALID, &FQTermTelnet::tnabort
+ },
+};
+
+
+
+/*------------------------------------------------------------------------
+ * Constructor
+ *------------------------------------------------------------------------
+ */
+FQTermTelnet::FQTermTelnet(const QString &strTermType, int rows, int columns,
+ int protocolType, int hostType, const char *sshuser, const char *sshpasswd)
+ : from_socket(),
+ to_ansi(),
+ from_ansi(),
+ to_socket(),
+ hostType_(hostType),
+ protocolType_(protocolType) {
+ term = new char[21];
+ memset(term, 0, sizeof(term));
+ // TODO: clean up, need???
+#ifdef WIN32
+ _snprintf(term, sizeof(term), "%s", strTermType.toLatin1().constData());
+#else
+ strncpy(term,strTermType.toLatin1(),20);
+#endif
+
+ wx = columns;
+ wy = rows;
+ wsize = 0;
+ done_naws = 0;
+ synching = 0;
+ doecho = 0;
+ sndbinary = 0;
+ rcvbinary = 0;
+ noga = 0;
+ termtype = 0;
+ naws = 0;
+ server_sent_do_naws = 0;
+ raw_size = 0;
+
+#ifndef _NO_SSH_COMPILED
+ if (protocolType == 1 || protocolType == 2) {
+ socket = new FQTermSSHSocket(columns, rows, strTermType, sshuser, sshpasswd);
+ FQ_VERIFY(connect(socket, SIGNAL(sshAuthOK()),
+ this, SIGNAL(onSSHAuthOK())));
+ } else if (protocolType == 3) {
+ socket = new FQTermLocalSocket("");
+ } else {
+ socket = new FQTermTelnetSocket();
+ }
+#else
+ socket = new FQTermTelnetSocket();
+#endif
+
+ // connect signal and slots
+ FQ_VERIFY(connect(socket, SIGNAL(connected()), this, SLOT(connected())));
+ FQ_VERIFY(connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead())));
+ FQ_VERIFY(connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(showError(QAbstractSocket::SocketError))));
+ FQ_VERIFY(connect(socket, SIGNAL(hostFound()), this, SLOT(hostFound())));
+ FQ_VERIFY(connect(socket, SIGNAL(delayedCloseFinished()),
+ this, SLOT(delayCloseFinished())));
+ FQ_VERIFY(connect(socket, SIGNAL(connectionClosed()), this, SLOT(closed())));
+ FQ_VERIFY(connect(socket, SIGNAL(socketState(int)), this, SIGNAL(TelnetState(int))));
+
+ FQ_VERIFY(connect(socket, SIGNAL(requestUserPwd(QString *, QString *, bool *)),
+ this, SIGNAL(requestUserPwd(QString *, QString *, bool *))));
+
+ FQ_VERIFY(connect(socket, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))));
+
+ // Init telnet, mainly the FSMs
+ init_telnet();
+}
+
+/*------------------------------------------------------------------------
+ * destructor
+ *------------------------------------------------------------------------
+ */
+FQTermTelnet::~FQTermTelnet() {
+ // delete objects
+ delete [] term;
+ delete socket;
+}
+
+
+
+/*------------------------------------------------------------------------
+ * init_telnet
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::init_telnet() {
+ fsmbuild(); /* setup FSMs */
+}
+
+
+/*------------------------------------------------------------------------
+ * fsmbuild - build the Finite State Machine data structures
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::fsmbuild() {
+ fsminit(ttfsm, ttstab, NTSTATES);
+ ttstate = TSDATA;
+
+ fsminit(subfsm, substab, NSSTATES);
+ substate = SS_START;
+
+}
+
+/*------------------------------------------------------------------------
+ * fsminit - Finite State Machine initializer
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::fsminit(u_char fsm[][NCHRS], struct fsm_trans ttab[], int
+ nstates) {
+ struct fsm_trans *pt;
+ int sn, ti, cn;
+
+ for (cn = 0; cn < NCHRS; ++cn) {
+ for (ti = 0; ti < nstates; ++ti) {
+ fsm[ti][cn] = TINVALID;
+ }
+ }
+
+ for (ti = 0; ttab[ti].ft_state != FSINVALID; ++ti) {
+ pt = &ttab[ti];
+ sn = pt->ft_state;
+ if (pt->ft_char == TCANY) {
+ for (cn = 0; cn < NCHRS; ++cn) {
+ if (fsm[sn][cn] == TINVALID) {
+ fsm[sn][cn] = ti;
+ }
+ }
+ } else {
+ fsm[sn][pt->ft_char] = ti;
+ }
+ }
+
+ /* set all uninitialized indices to an invalid transition */
+ for (cn = 0; cn < NCHRS; ++cn) {
+ for (ti = 0; ti < nstates; ++ti) {
+ if (fsm[ti][cn] == TINVALID) {
+ fsm[ti][cn] = ti;
+ }
+ }
+ }
+
+}
+
+/*------------------------------------------------------------------------
+ * connect to host
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::connectHost(const QString &hostname, quint16 portnumber) {
+ done_naws = 0;
+ synching = 0;
+ doecho = 0;
+ sndbinary = 0;
+ rcvbinary = 0;
+ noga = 0;
+ termtype = 0;
+ naws = 0;
+
+ socket->connectToHost(hostname, portnumber);
+ // host name resolving
+ emit TelnetState(TSRESOLVING);
+}
+
+void FQTermTelnet::windowSizeChanged(int x, int y) {
+ wx = x;
+ wy = y;
+ if (bConnected) {
+ if (hostType_ == 1 && (protocolType_ == 1 || protocolType_ == 2)) {
+ //This is a *nix host, with ssh connection.
+ socket->setTermSize(x, y);
+ return;
+ }
+ naws = 0;
+
+ QByteArray cmd(10, 0);
+ cmd[0] = (char)TCIAC;
+ cmd[1] = (char)TCSB;
+ cmd[2] = (char)TONAWS;
+ cmd[3] = (char)(short(wx) >> 8);
+ cmd[4] = (char)(short(wx) & 0xff);
+ cmd[5] = (char)(short(wy) >> 8);
+ cmd[6] = (char)(short(wy) & 0xff);
+ cmd[7] = (char)TCIAC;
+ cmd[8] = (char)TCSE;
+ socket->writeBlock(cmd);
+
+ }
+}
+
+/*------------------------------------------------------------------------
+ * set proxy
+ *-----------------------------------------------------------------------
+ */
+void FQTermTelnet::setProxy(int nProxyType, bool bAuth, const QString
+ &strProxyHost, quint16 uProxyPort,
+ const QString &strProxyUsr,
+ const QString &strProxyPwd) {
+ socket->setProxy(nProxyType, bAuth, strProxyHost,
+ uProxyPort, strProxyUsr, strProxyPwd);
+}
+
+/*------------------------------------------------------------------------
+ * close connection
+ *-----------------------------------------------------------------------
+ */
+
+void FQTermTelnet::close() {
+ socket->close();
+}
+
+/*------------------------------------------------------------------------
+ * SLOT connected
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::connected() {
+ bConnected = true;
+ emit TelnetState(TSHOSTCONNECTED);
+}
+
+/*------------------------------------------------------------------------
+ * SLOT closed
+ *------------------------------------------------------------------------
+ */
+
+void FQTermTelnet::closed() {
+ bConnected = false;
+ emit TelnetState(TSCLOSED);
+}
+
+/*------------------------------------------------------------------------
+ * SLOT delayCloseFinished
+ *------------------------------------------------------------------------
+ */
+
+void FQTermTelnet::delayCloseFinished() {
+ bConnected = false;
+ emit TelnetState(TSCLOSEFINISH);
+}
+
+/*------------------------------------------------------------------------
+ * SLOT hostFound
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::hostFound() {
+ emit TelnetState(TSHOSTFOUND);
+}
+
+/*------------------------------------------------------------------------
+ * SLOT error
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::showError(QAbstractSocket::SocketError index) {
+
+ switch (index) {
+ case QAbstractSocket::ConnectionRefusedError: emit TelnetState(TSREFUSED);
+ break;
+ case QAbstractSocket::HostNotFoundError: emit TelnetState(TSHOSTNOTFOUND);
+ break;
+ //FIXME: am I right
+ case QAbstractSocket::SocketAccessError: emit TelnetState(TSREADERROR);
+ break;
+ case QAbstractSocket::RemoteHostClosedError: emit TelnetState(TSCLOSED);
+ break;
+ default:
+ emit TelnetState(TSERROR);
+ ;
+ break;
+ }
+
+}
+
+/*------------------------------------------------------------------------
+ * SLOOT socketReadyRead -
+ * when socket has data to upload, it send out readyRead() signal, it
+ * invokes this SLOT to read data in, do telnet decode job, and send out
+ * readyRead() SIGNAL to upper layer
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::socketReadyRead() {
+ int nbytes, nread;
+ // TODO_UTF16: check this function.
+
+ // get the data size
+ nbytes = socket->bytesAvailable();
+ if (nbytes <= 0) {
+ return ;
+ }
+
+ raw_size = nbytes;
+
+ //resize input buffer
+ from_socket.resize(0);
+
+ //read data from socket to from_socket
+ from_socket = socket->readBlock(nbytes);
+ nread = from_socket.size();
+ //do some checks
+ if (nread <= 0) {
+ FQ_TRACE("telnet", 8) << "Failed to read socket: " << nread << " <= 0.";
+ return ;
+ }
+ if (nread > nbytes) {
+ FQ_TRACE("telnet", 0) << "Overflow when reading socket: "
+ << " nread = " << nread
+ << " > nbytes = " << nbytes;
+ return ;
+ }
+
+ //resize output buffer
+ to_ansi.resize(2 *nread);
+ to_socket.resize(4 *nread);
+
+ rsize = 0;
+ wsize = 0;
+
+ // do telnet decode job...
+ struct fsm_trans *pt;
+ int i, ti;
+
+ u_char c;
+ for (i = 0; i < nread; ++i) {
+ c = (u_char)(from_socket[i]);
+ ti = ttfsm[ttstate][c];
+ pt = &ttstab[ti];
+ (this->*(pt->ft_action))((int)c);
+ ttstate = pt->ft_next;
+ }
+
+ // flush the to_socket buffer, it contain response to server
+ if (wsize > 0) {
+ socket->writeBlock(to_socket.left(wsize));
+ socket->flush();
+ }
+
+ /* send SIGNAL readyRead() with the size of data available*/
+ if (rsize > 0 || raw_size > 0) {
+ emit readyRead(rsize, raw_size);
+ }
+
+}
+
+int FQTermTelnet::raw_len() {
+ return raw_size;
+}
+
+/*------------------------------------------------------------------------
+ * actions
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::read_raw(char *data, uint maxlen) {
+ //do some checks
+ if (data == 0) {
+ FQ_TRACE("telnet", 0) << "NULL pointer.";
+ return -1;
+ }
+ if ((long)maxlen < raw_size) {
+ /* we need all data be read out in one read */
+ FQ_TRACE("telnet", 0) << "The upper layer's buffer is too small.";
+ return -1;
+ }
+
+ //do it, memcpy( destination, source, size)
+ memcpy(data, from_socket.data(), raw_size);
+ return raw_size;
+}
+
+
+/*------------------------------------------------------------------------
+ * actions
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::read(char *data, uint maxlen) {
+ //do some checks
+ if (data == 0) {
+ FQ_TRACE("telent", 0) << "NULL pointer.";
+ return -1;
+ }
+ if (maxlen < rsize) {
+ /* we need all data be read out in one read */
+ FQ_TRACE("telnet", 0) << "The upper layer's buffer is too small.";
+ return -1;
+ }
+
+ //do it, memcpy( destination, source, size)
+ memcpy(data, to_ansi.data(), rsize);
+
+ FQ_TRACE("telnet", 8) << "read " << rsize << " bytes:\n"
+ << dumpHexString << std::string(data, rsize);
+
+ return rsize;
+}
+
+/*------------------------------------------------------------------------
+ * actions
+ *------------------------------------------------------------------------
+ */
+
+
+/*------------------------------------------------------------------------
+ * writeBlock
+ * write data from data-> to socket, the length of data is len
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::write(const char *data, uint len) {
+ // TODO: accept data, (This seems can be removed????)
+ from_ansi.resize(len);
+ memcpy(from_ansi.data(), data, len);
+
+ // resize output buffer
+ to_socket.resize(2 *len);
+ wsize = 0;
+
+ // process keyboard input
+ // because we use GUI, there is no need to support a "command mode"
+ // So the keyboard-input FSM isnt' necessary.
+
+ uint i;
+
+ u_char c; // TODO: for gcc's happy :)
+ for (i = 0; i < len; ++i) {
+ c = (u_char)(from_ansi[i]);
+ soputc((int)c);
+ }
+
+ FQ_TRACE("telnet", 2) << "write " << len << " bytes:\n"
+ << dumpHexString << std::string(data, len);
+
+ //flush socket
+ socket->writeBlock(to_socket.left(wsize));
+ socket->flush();
+
+ emit TelnetState(TSWRITED);
+ return 0;
+}
+
+
+/*------------------------------------------------------------------------
+ * Trans functions
+ * All trans functions use from_socket, to_ansi, to_socket buffers, and
+ * rsize, wsize .
+ *------------------------------------------------------------------------
+ */
+
+/*------------------------------------------------------------------------
+ * tcdm - handle the telnet "DATA MARK" command (marks end of SYNCH)
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::tcdm(int) {
+ if (synching > 0) {
+ synching--;
+ }
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * rcvurg - receive urgent data input (indicates a telnet SYNCH)
+ *------------------------------------------------------------------------
+ */
+/*
+ int FQTermTelnet::rcvurg(int sig)
+ {
+ synching++;
+ }
+*/
+
+/*------------------------------------------------------------------------
+ * recopt - record option type
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::recopt(int c) {
+ option_cmd = c;
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * no_op - do nothing
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::no_op(int) {
+ return 0;
+}
+
+
+/*------------------------------------------------------------------------
+ * do_echo - handle TELNET WILL/WON'T ECHO option
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::do_echo(int c) {
+ if (doecho) {
+ if (option_cmd == TCWILL) {
+ return 0;
+ }
+ /* already doing ECHO */
+ } else if (option_cmd == TCWONT) {
+ return 0;
+ }
+ /* already NOT doing ECHO */
+
+ doecho = !doecho;
+
+ putc_down(TCIAC);
+ if (doecho) {
+ putc_down(TCDO);
+ } else {
+ putc_down(TCDONT);
+ }
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * putc_down - put a character in to_socket buffer.
+ * wsize represent the number of bytes in to_socket buffer, and the buffer
+ * is addressed from 0, NOT 1.
+ *------------------------------------------------------------------------
+ */
+void FQTermTelnet::putc_down(u_char c) {
+ // check overflow
+ if ((long)(wsize + 1) > to_socket.size()) {
+ FQ_TRACE("telnet", 0) << "Buffer to_socket overflow.";
+ return ;
+ }
+ // put it in the buffer
+ //to_socket->replace(wsize, 1, (const char *)&c);
+ to_socket[wsize] = c;
+
+ wsize++;
+ return ;
+}
+
+/*------------------------------------------------------------------------
+ * do_notsup - handle an unsupported telnet "will/won't" option
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::do_notsup(int c) {
+ putc_down(TCIAC);
+ putc_down(TCDONT);
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * do_noga - don't do telnet Go-Ahead's
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::do_noga(int c) {
+ if (noga) {
+ if (option_cmd == TCWILL) {
+ return 0;
+ }
+ } else if (option_cmd == TCWONT) {
+ return 0;
+ }
+
+ noga = !noga;
+
+ putc_down(TCIAC);
+ if (noga) {
+ putc_down(TCDO);
+ } else {
+ putc_down(TCDONT);
+ }
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * do_txbinary - handle telnet "will/won't" TRANSMIT-BINARY option
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::do_txbinary(int c) {
+ if (rcvbinary) {
+ if (option_cmd == TCWILL) {
+ return 0;
+ }
+ } else if (option_cmd == TCWONT) {
+ return 0;
+ }
+
+ rcvbinary = !rcvbinary;
+
+ putc_down(TCIAC);
+ if (rcvbinary) {
+ putc_down(TCDO);
+ } else {
+ putc_down(TCDONT);
+ }
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * will_notsup - handle an unsupported telnet "do/don't" option
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::will_notsup(int c) {
+ putc_down(TCIAC);
+ putc_down(TCWONT);
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * will_txbinary - handle telnet "do/don't" TRANSMIT-BINARY option
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::will_txbinary(int c) {
+ if (sndbinary) {
+ if (option_cmd == TCDO) {
+ return 0;
+ }
+ } else if (option_cmd == TCDONT) {
+ return 0;
+ }
+
+ sndbinary = !sndbinary;
+
+ putc_down(TCIAC);
+ if (sndbinary) {
+ putc_down(TCWILL);
+ } else {
+ putc_down(TCWONT);
+ }
+ putc_down((char)c);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * will_termtype - handle telnet "do/don't" TERMINAL-TYPE option
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::will_termtype(int c) {
+ if (termtype) {
+ if (option_cmd == TCDO) {
+ return 0;
+ }
+ } else if (option_cmd == TCDONT) {
+ return 0;
+ }
+
+ termtype = !termtype;
+
+ putc_down(TCIAC);
+
+ if (termtype) {
+ putc_down(TCWILL);
+ } else {
+ putc_down(TCWONT);
+ }
+
+ putc_down((char)c);
+
+ // TODO: Do NOT use this assume! some foolish BBS not response the request
+ /* The client expects that once the remote application receives
+ terminal type information it will send control sequences for
+ the terminal, which cannot be sent using the NVT encoding, So
+ change the transfer mode to binary in both directions */
+ /* set up binary data path; send WILL, DO */
+ /* if (termtype) {
+ option_cmd = TCWILL;
+ do_txbinary(TOTXBINARY);
+ option_cmd = TCDO;
+ will_txbinary(TOTXBINARY);
+ }
+ */
+ return 0;
+}
+
+int FQTermTelnet::will_naws(int c) {
+ if (naws) {
+ if (option_cmd == TCDO)
+ return 0;
+ } else if (option_cmd == TCDONT)
+ return 0;
+
+
+ naws = !naws;
+
+ putc_down(TCIAC);
+ if (naws)
+ putc_down(TCWILL);
+ else
+ putc_down(TCWONT);
+ putc_down((char)c);
+
+ putc_down(TCIAC);
+ putc_down(TCSB);
+ putc_down(TONAWS);
+ putc_down((char)(short(wx) >> 8));
+ putc_down((char)(short(wx)&0xff));
+ putc_down((char)(short(wy) >> 8));
+ putc_down((char)(short(wy)&0xff));
+ putc_down(((char)TCIAC));
+ putc_down((char)TCSE);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * subopt - do option subnegotiation FSM transitions
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::subopt(int c) {
+ struct fsm_trans *pt;
+ int ti;
+
+ ti = subfsm[substate][c];
+ pt = &substab[ti];
+ (this->*(pt->ft_action))(c);
+ substate = pt->ft_next;
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * subtermtype - do terminal type option subnegotation
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::subtermtype(int) {
+ char *i;
+ /* have received IAC.SB.TERMTYPE.SEND */
+
+ putc_down(TCIAC);
+ putc_down(TCSB);
+ putc_down(TOTERMTYPE);
+ putc_down(TT_IS);
+
+ //write term type string
+ //fputs(term, sfp);
+ for (i = term; (*i) != '\000'; i++) {
+ putc_down(*i);
+ }
+
+ putc_down(TCIAC);
+ putc_down(TCSE);
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * subend - end of an option subnegotiation; reset FSM
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::subend(int) {
+ substate = SS_START;
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * soputc - move a character from the keyboard to the socket
+ * convert an character into the NVT encoding and send it
+ * through the socket to the server.
+ *------------------------------------------------------------------------
+ */
+
+int FQTermTelnet::soputc(int c) {
+ if (sndbinary) {
+ if (c == TCIAC) {
+ putc_down(TCIAC);
+ }
+ /* byte-stuff IAC */
+ putc_down(c);
+ return 0;
+ }
+
+ //c &= 0x7f; /* 7-bit ASCII only ???*/
+ // Convert local special characters to NVT characters
+ /* TODO: // BBS don't need control signals
+ if (c == t_intrc || c == t_quitc) { // Interrupt
+ putc_down(TCIAC);
+ putc_down(TCIP);
+ } else if (c == sg_erase) { // Erase Char
+ putc_down(TCIAC);
+ putc_down(TCEC);
+ } else if (c == sg_kill) { // Erase Line
+ putc_down(TCIAC);
+ putc_down(TCEL);
+ } else if (c == t_flushc) { // Abort Output
+ putc_down(TCIAC);
+ putc_down(TCAO);
+ } else */
+ putc_down((char)c);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * xputc - putc to upper layer with optional file scripting
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::xputc_up(char ch) {
+ if ((long)(rsize + 1) > to_ansi.size()) {
+ FQ_TRACE("telnet", 0) << "Buffer to_ansi overflow.";
+ return - 1;
+ }
+ //to_ansi->replace(wsize, 1, &ch);
+ to_ansi[rsize] = u_char(ch);
+ rsize++;
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * xfputs - fputs with optional file scripting
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::xputs_up(char *str) {
+ /*if (scrfp)
+ fputs(str, scrfp);*/
+
+ char *i;
+ for (i = str; (*i) != '\000'; i++) {
+ xputc_up(*i);
+ }
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * ttputc - print a single character on a Network Virtual Terminal
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::ttputc(int c) {
+ if (rcvbinary) {
+ xputc_up((char)c); /* print uninterpretted */
+ return 0;
+ }
+ /* no data, if in SYNCH */
+ /*
+ if (synching)
+ return 0;
+ */
+ /* TODO: FQTermTelnet doesnot interpret NVT code, provide datas to upper
+ layer directly. So, <cr><lf> will not be replaced with <lf>
+ */
+ xputc_up((char)c);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------
+ * invalid state reached, aborted
+ *------------------------------------------------------------------------
+ */
+int FQTermTelnet::tnabort(int) {
+ FQ_VERIFY(false); // "invalid state reached, aborted";
+ // exit(-1);
+ return -1;
+}
+
+bool FQTermTelnet::readyForInput()
+{
+ return socket->readyForInput();
+}
+
+
+
+} // namespace FQTerm
+
+#include "fqterm_telnet.moc"