summaryrefslogtreecommitdiff
path: root/src/terminal
diff options
context:
space:
mode:
authoriroul <iroul@iroul-VirtualBox.(none)>2014-04-04 07:35:14 -0700
committeriroul <iroul@iroul-VirtualBox.(none)>2014-04-04 07:35:14 -0700
commitafd34f2893a06a3aecf17e8e83b1df6ed2ae91a2 (patch)
tree851102abc55d91a1b76e63e9e89f9a7733da95b5 /src/terminal
parentc4b028ad53f7b362a864de24828d7cc39ff67b0a (diff)
downloadfqterm-afd34f2893a06a3aecf17e8e83b1df6ed2ae91a2.tar.xz
move to my github.
Diffstat (limited to 'src/terminal')
-rw-r--r--src/terminal/CMakeLists.txt46
-rw-r--r--src/terminal/fqterm_buffer.cpp998
-rw-r--r--src/terminal/fqterm_buffer.h297
-rw-r--r--src/terminal/fqterm_session.cpp1648
-rw-r--r--src/terminal/fqterm_session.h344
-rw-r--r--src/terminal/fqterm_text_line.cpp541
-rw-r--r--src/terminal/fqterm_text_line.h159
-rw-r--r--src/terminal/internal/fqterm_decode.cpp1279
-rw-r--r--src/terminal/internal/fqterm_decode.h194
-rw-r--r--src/terminal/internal/fqterm_telnet.cpp1041
-rw-r--r--src/terminal/internal/fqterm_telnet.h195
-rw-r--r--src/terminal/internal/fqterm_zmodem.cpp3056
-rw-r--r--src/terminal/internal/fqterm_zmodem.h623
13 files changed, 10421 insertions, 0 deletions
diff --git a/src/terminal/CMakeLists.txt b/src/terminal/CMakeLists.txt
new file mode 100644
index 0000000..c3cf3db
--- /dev/null
+++ b/src/terminal/CMakeLists.txt
@@ -0,0 +1,46 @@
+set(export_SRCS
+ fqterm_buffer.h
+ fqterm_session.h
+ fqterm_text_line.h
+ fqterm_session.cpp
+ fqterm_buffer.cpp
+ fqterm_text_line.cpp
+)
+
+set(internal_SRCS
+ internal/fqterm_decode.h
+ internal/fqterm_telnet.h
+ internal/fqterm_zmodem.h
+ internal/fqterm_decode.cpp
+ internal/fqterm_telnet.cpp
+ internal/fqterm_zmodem.cpp
+)
+
+include_directories(
+ ${QT_INCLUDE_DIR}
+ ${QT_QTCORE_INCLUDE_DIR}
+ ${QT_QTGUI_INCLUDE_DIR}
+ ${QT_QTNETWORK_INCLUDE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/internal
+ ${CMAKE_CURRENT_SOURCE_DIR}/../common
+ ${CMAKE_CURRENT_SOURCE_DIR}/../utilities
+ ${CMAKE_CURRENT_SOURCE_DIR}/../protocol
+)
+
+QT4_AUTOMOC(
+ ${export_SRCS}
+ ${internal_SRCS}
+)
+
+add_library(fqterm_terminal
+ ${export_SRCS}
+ ${internal_SRCS}
+)
+
+add_dependencies(fqterm_terminal
+ fqterm_common
+ fqterm_protocol
+ fqterm_utilities
+)
diff --git a/src/terminal/fqterm_buffer.cpp b/src/terminal/fqterm_buffer.cpp
new file mode 100644
index 0000000..c00e079
--- /dev/null
+++ b/src/terminal/fqterm_buffer.cpp
@@ -0,0 +1,998 @@
+/***************************************************************************
+ * 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 <algorithm>
+#include <map>
+#include <vector>
+
+#include <QString>
+#include <QRect>
+#include <QRegExp>
+
+#include "fqterm.h"
+#include "common.h"
+#include "fqterm_buffer.h"
+#include "fqterm_text_line.h"
+
+namespace FQTerm {
+
+typedef std::vector<int> TabStops;
+
+/* staight from linux/drivers/char/consolemap.c, GNU GPL:ed */
+static const UTF16 VT_SPECIAL_GRAPHICS_TABLE[256]={
+ /* VT100 graphics mapped to Unicode */
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
+ 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
+ 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
+ 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
+ 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+ 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+ 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+ 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+ 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+ 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+ 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+ 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+ 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+ 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+ 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+};
+
+FQTermBuffer::FQTermBuffer(int column, int row, int max_hist_line, bool is_bbs) {
+ tab_stops_ = new TabStops;
+ num_rows_ = row;
+ num_columns_ = column;
+ max_num_hist_lines_ = max_hist_line;
+ is_bbs_ = is_bbs;
+
+ num_hist_lines_ = 0;
+
+ while (text_lines_.count() < num_rows_) {
+ text_lines_.append(new FQTermTextLine(num_columns_));
+ }
+
+ top_row_ = 0;
+ bottom_row_ = num_rows_ - 1;
+
+ is_g0_used_ = true;
+
+ is_insert_mode_ = false;
+ is_ansi_mode_ = true;
+ is_newline_mode_ = false;
+ is_cursor_mode_ = false;
+ is_numeric_mode_ = true;
+ is_origin_mode_ = false;
+ is_autowrap_mode_ = false;
+ is_autorepeat_mode_ = true;
+ is_lightbg_mode_ = false;
+
+ selection_start_ = QPoint(-1, -1);
+ selection_end_ = QPoint(-1, -1);
+}
+
+FQTermBuffer::~FQTermBuffer(){
+ foreach(FQTermTextLine *line, text_lines_) {
+ delete line;
+ }
+
+ delete (TabStops *)tab_stops_;
+}
+
+FQTermBuffer::Caret::Caret()
+ : column_(0),
+ row_(0),
+ color_(NO_COLOR),
+ attr_(NO_ATTR),
+ G0_(ASCII_SET),
+ G1_(ASCII_SET) {
+}
+
+void FQTermBuffer::setTermSize(int col, int row) {
+ FQ_TRACE("term", 3) << "Change term size to "
+ << col << "x" << row;
+
+ if (num_columns_ == col && num_rows_ == row) {
+ return;
+ }
+
+ clearSelect();
+
+ if (num_rows_ < row) {
+ for (int i = 0; i < row - num_rows_; i++) {
+ text_lines_.append(new FQTermTextLine(col));
+ }
+ } else if (num_rows_ > row) {
+ for (int i = 0; i < num_rows_ - row; i++) {
+ delete text_lines_.takeLast();
+ }
+ }
+
+ num_columns_ = col;
+ num_rows_ = row;
+
+ top_row_ = 0;
+ bottom_row_ = num_rows_ - 1;
+
+ // TODO: how about saved caret column?
+ // TODO: why make last saved equal to current position?
+ last_saved_caret_.row_ = caret_.row_ = qMin(caret_.row_, row - 1);
+
+ for (int i = 0; i < text_lines_.size(); ++i) {
+ text_lines_.at(i)->setMaxCellCount(num_columns_);
+ }
+
+// this->clearArea(0, 0, num_columns_, num_rows_, 0, 0);
+ // this->moveCaretTo(0, 0);
+
+ moveCaretTo(qMin(caret_.column_, col - 1), qMin(caret_.row_, row - 1));
+
+ emit onSetTermSize(col, row);
+ emit termSizeChanged(num_columns_, num_rows_);
+}
+
+int FQTermBuffer::getNumColumns() const {
+ return num_columns_;
+}
+
+int FQTermBuffer::getNumRows() const {
+ return num_rows_;
+}
+
+int FQTermBuffer::getNumLines() const {
+ return num_rows_ + num_hist_lines_;
+}
+
+const FQTermTextLine *FQTermBuffer::getTextLineInBuffer(int line_index) const {
+ return text_lines_.value(line_index, NULL);
+}
+
+const FQTermTextLine *FQTermBuffer::getTextLineInTerm(int line_index) const {
+ return text_lines_.value(line_index + num_hist_lines_, NULL);
+}
+
+void FQTermBuffer::setCurrentAttr(unsigned char color, unsigned char attr) {
+ caret_.color_ = color;
+ caret_.attr_ = attr;
+}
+
+void FQTermBuffer::writeText(const QString &str, int charstate) {
+
+ QString cstr = str;
+
+ FQ_TRACE("term", 8) << "Add text: \"" << cstr << "\"";
+
+#if 0
+ if ((is_g0_used_ && caret_.G0_ == SPECIAL_GRAPHICS) ||
+ (!is_g0_used_ && caret_.G1_ == SPECIAL_GRAPHICS)) {
+
+ // FIXME: will this break the utf-16 sequence?
+ for (int i = 0; i < cstr.size(); ++i) {
+ if (cstr[i] >= 0137 && cstr[i] <= 0176) {
+ cstr[i] = VT_SPECIAL_GRAPHICS_TABLE[cstr.at(i).unicode()];
+ }
+ }
+ }
+#endif
+
+ // Insert the str into the buffer. Different pieces of text might be
+ // written into different lines.
+ while (!cstr.isEmpty()) {
+ FQTermTextLine *line = text_lines_.value(num_hist_lines_ + caret_.row_, NULL);
+
+ if (line == NULL) {
+ FQ_TRACE("error", 0) << "Error: setBuffer null line";
+ return ;
+ }
+
+ if (charstate & FQTermTextLine::SECONDPART) {
+ moveCaretOffset(-1, 0);
+ }
+
+ if (caret_.column_ >= (int)line->getMaxCellCount()) {
+ // move the caret to the next line.
+ moveCaretTo(0, caret_.row_ + 1, true);
+ continue;
+ }
+
+ if (caret_.column_ > (int)line->getWidth()) {
+ line->appendWhiteSpace(caret_.column_ - line->getWidth());
+ }
+
+ unsigned cell_begin = line->getCellBegin(caret_.column_);
+ int max_width = line->getMaxCellCount() - cell_begin;
+ int element_consumed = 0;
+ int width = get_str_width((const UTF16 *)cstr.data(), cstr.size(),
+ max_width, element_consumed);
+
+ if (width < 0) {
+ break;
+ }
+
+ if (is_insert_mode_) {
+ // FIXEME: How to move cursor if the entire line is wider than
+ // line->getMaxCellCount() after insertion?
+ line->insertText((UTF16 *)cstr.data(), element_consumed, cell_begin,
+ caret_.color_, caret_.attr_, charstate);
+ if ((int)line->getWidth() > num_columns_) {
+ line->deleteText(num_columns_, line->getWidth());
+ }
+ } else {
+ line->replaceText((UTF16 *)cstr.data(), element_consumed,
+ cell_begin, qMin(cell_begin + width, line->getWidth()),
+ caret_.color_, caret_.attr_, charstate);
+ }
+ moveCaretOffset(width, 0);
+
+ if (element_consumed == 0)
+ {
+ element_consumed = 1;
+ }
+
+ if (element_consumed == cstr.size()) {
+ break;
+ }
+
+ cstr.remove(0, element_consumed);
+ }
+}
+
+void FQTermBuffer::lineFeed() {
+ FQ_TRACE("term", 8) << "add a new line";
+ if (caret_.row_ == bottom_row_)
+ scrollLinesInTerm(top_row_, 1);
+ if (is_newline_mode_) {
+ moveCaretOffset(-caret_.column_, 1);
+ } else {
+ moveCaretOffset(0, 1);
+ }
+}
+
+void FQTermBuffer::tab() {
+ FQ_TRACE("term", 8) << "add a tab";
+ int x = getTabStop(caret_.column_);
+
+ moveCaretTo(x, caret_.row_);
+}
+
+int FQTermBuffer::getTabStop(int column) {
+
+ std::vector<int> &tabs = *(TabStops *)tab_stops_;
+
+ if (tabs.size() == 0) {
+ int res = ((caret_.column_) / 8 + 1) * 8;
+ return (res < num_columns_) ? res : num_columns_ - 1;
+ }
+
+ std::vector<int>::iterator it =
+ std::upper_bound(tabs.begin(), tabs.end(), column);
+
+ int res = num_columns_ - 1;
+
+ if (it != tabs.end() && *it < res) {
+ res = *it;
+ }
+
+ if (res > num_columns_ - 1) {
+ res = num_columns_ - 1;
+ }
+
+ return res;
+}
+
+void FQTermBuffer::addTabStop() {
+ // FIXME: what if the caret is located beyond the term temporarily.
+ int x = caret_.column_;
+
+ std::vector<int> &tabs = *(TabStops *)tab_stops_;
+
+ std::vector<int>::iterator it =
+ std::lower_bound(tabs.begin(), tabs.end(), x);
+
+ if (it == tabs.end() || *it != x) {
+ tabs.insert(it, x);
+ }
+}
+
+void FQTermBuffer::clearTabStop(bool clear_all) {
+ // FIXME: what if the caret is located beyond the term temporarily.
+ std::vector<int> &tabs = *(TabStops *)tab_stops_;
+
+ if (clear_all) {
+ tabs.clear();
+ return;
+ }
+
+ int x = caret_.column_;
+
+ std::vector<int>::iterator it =
+ std::lower_bound(tabs.begin(), tabs.end(), x);
+
+ if (it != tabs.end() && *it == x) {
+ tabs.erase(it);
+ }
+}
+
+void FQTermBuffer::setMargins(int top, int bottom) {
+ FQ_TRACE("term", 3) << "Set margins: [" << top << ", " << bottom << "]";
+
+ top_row_ = qMax(top, 0);
+ bottom_row_ = qMin(qMax(bottom, 0), num_rows_ - 1);
+
+ if (is_origin_mode_) {
+ moveCaretTo(0, top_row_);
+ } else {
+ moveCaretTo(0, 0);
+ }
+}
+
+void FQTermBuffer::moveCaretTo(int column, int row, bool scroll_if_necessary) {
+ if (row != caret_.row_)
+ emit caretChangeRow();
+
+ FQ_TRACE("term", 5) << "Move caret to (" << column << ", " << row << ")";
+
+ //If th
+
+ // detect index boundary
+ if (column >= num_columns_) {
+ column = num_columns_;
+ }
+ if (column < 0) {
+ column = 0;
+ }
+
+ int scroll_lines = 0;
+
+ int stop = is_origin_mode_ ? top_row_ : 0;
+ if (row < stop) {
+ scroll_lines = row - stop;
+ row = stop;
+ }
+
+ stop = is_origin_mode_ ? bottom_row_ : num_rows_ - 1;
+ if (row > stop) {
+ scroll_lines = row - stop;
+ row = stop;
+ }
+
+ // Set dirty flag for cells of the last caret position.
+ FQTermTextLine *line = text_lines_.value(num_hist_lines_ + caret_.row_, NULL);
+ if (0 <= caret_.column_ && caret_.column_ < (int)line->getWidth()) {
+ unsigned cell_begin = line->getCellBegin(caret_.column_);
+ unsigned cell_end = line->getCellBegin(caret_.column_ + 1);
+ line->setDirtyFlag(cell_begin, cell_end);
+ }
+
+ if (scroll_if_necessary) {
+ scrollLinesInTerm(top_row_, scroll_lines);
+ }
+
+ caret_.column_ = column;
+ caret_.row_ = row;
+}
+
+void FQTermBuffer::moveCaretOffset(int column_offset, int row_offset, bool scroll_if_necessary) {
+ if (caret_.column_ >= num_columns_) {
+ // it's only allowed that the caret_.column_ >= num_columns_
+ // temporarily when a sequence of normal text is received. if we
+ // found caret_.column_ is out of bounds in other case, we should
+ // correct it first.
+ if (is_bbs_) {
+ // but the BBS (newsmth.net) assumes that the caret could be
+ // located out of the screen :(
+ } else {
+ caret_.column_ = num_columns_ - 1;
+ }
+ }
+
+ if (caret_.column_ + column_offset < 0) {
+ column_offset = -caret_.column_;
+ }
+
+ moveCaretTo(caret_.column_ + column_offset, caret_.row_ + row_offset,
+ scroll_if_necessary);
+}
+
+void FQTermBuffer::changeCaretPosition(int coloumn, int row) {
+ if (is_origin_mode_) {
+ moveCaretTo(coloumn, row + top_row_);
+ } else {
+ moveCaretTo(coloumn, row);
+ }
+}
+
+void FQTermBuffer::saveCaret() {
+ FQ_TRACE("term", 5) << "save the caret.";
+ last_saved_caret_ = caret_;
+}
+
+void FQTermBuffer::restoreCaret() {
+ FQ_TRACE("term", 5) << "restore the caret.";
+ moveCaretTo(last_saved_caret_.column_, last_saved_caret_.row_);
+ caret_ = last_saved_caret_;
+}
+
+void FQTermBuffer::SelectVtCharacterSet(VtCharSet charset, bool G0) {
+ if (G0) {
+ caret_.G0_ = charset;
+ } else {
+ caret_.G1_ = charset;
+ }
+}
+
+void FQTermBuffer::invokeCharset(bool G0) {
+ if (G0) {
+ is_g0_used_ = true;
+ } else {
+ is_g0_used_ = false;
+ }
+}
+
+void FQTermBuffer::carriageReturn() {
+ FQ_TRACE("term", 8) << "carrige return";
+ moveCaretOffset(-caret_.column_, 0);
+}
+
+int FQTermBuffer::getCaretColumn() const {
+ return caret_.column_;
+}
+
+int FQTermBuffer::getCaretRow() const {
+ return caret_.row_;
+}
+
+int FQTermBuffer::getCaretLine() const {
+ return caret_.row_ + num_hist_lines_;
+}
+
+// erase functions
+void FQTermBuffer::eraseText(int cell_count) {
+ FQ_TRACE("term", 8) << "erase " << cell_count << " cell(s) of text";
+
+ const FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_);
+
+ int x = line->getWidth() - caret_.column_;
+
+ clearArea(caret_.column_, caret_.row_,
+ qMin(cell_count, x), 1,
+ caret_.color_, caret_.attr_);
+}
+
+void FQTermBuffer::deleteText(int cell_count) {
+ FQ_TRACE("term", 8) << "delete " << cell_count << " cell(s) of text";
+
+ FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_);
+
+ int x = line->getWidth() - caret_.column_;
+
+ if (cell_count >= x) {
+ line->deleteText(caret_.column_, line->getWidth());
+ } else {
+ line->deleteText(caret_.column_, caret_.column_ + cell_count);
+ }
+}
+
+void FQTermBuffer::fillScreenWith(char c) {
+ FQ_TRACE("term", 5) << "fill screen with '" << c << "'";
+ for (int i = 0; i < num_rows_; i++) {
+ FQTermTextLine *line = text_lines_[i + num_hist_lines_];
+ line->deleteAllText();
+ line->appendWhiteSpace(num_columns_, NO_COLOR, NO_ATTR, c);
+ }
+}
+
+void FQTermBuffer::insertSpaces(int count) {
+ FQ_TRACE("term", 8) << "insert " << count << " white space(s)";
+
+ FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_);
+
+ int x = line->getWidth() - caret_.column_;
+
+ if (count >= x) {
+ clearArea(caret_.column_, caret_.row_, x, caret_.row_,
+ caret_.color_, caret_.attr_);
+ } else {
+ line->insertWhiteSpace(count, caret_.column_, caret_.color_, caret_.attr_);
+ }
+}
+
+void FQTermBuffer::deleteLines(int line_count) {
+ FQ_TRACE("term", 8) << "delete " << line_count << " line(s)";
+
+ int y = bottom_row_ - caret_.row_;
+
+ if (line_count >= y) {
+ clearArea(0, caret_.row_, -1, y, caret_.color_, caret_.attr_);
+ } else {
+ scrollLinesInTerm(caret_.row_, line_count);
+ }
+}
+
+void FQTermBuffer::insertLines(int count) {
+ FQ_TRACE("term", 8) << "insert " << count << " line(s)";
+
+ int y = bottom_row_ - caret_.row_;
+
+ if (count >= y) {
+ clearArea(0, caret_.row_, -1, y, caret_.color_, caret_.attr_);
+ } else {
+ scrollLinesInTerm(caret_.row_, -count);
+ }
+}
+
+void FQTermBuffer::eraseToLineEnd() {
+ FQ_TRACE("term", 8) << "erase to line end";
+
+ clearArea(caret_.column_, caret_.row_, -1, 1, caret_.color_, caret_.attr_);
+}
+
+void FQTermBuffer::eraseToLineBegin() {
+ FQ_TRACE("term", 8) << "erase to line begin";
+ clearArea(0, caret_.row_, caret_.column_ + 1, 1, caret_.color_, caret_.attr_);
+}
+
+void FQTermBuffer::eraseEntireLine() {
+ FQ_TRACE("term", 8) << "erase entire line";
+ clearArea(0, caret_.row_, -1, 1, caret_.color_, caret_.attr_);
+}
+
+// Set a line of text on screen to have been changed from start to end.
+void FQTermBuffer::setLineChanged(int index, int cell_begin, int cell_end) {
+ FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_);
+ text_lines_[index]->setDirtyFlag(cell_begin, cell_end);
+}
+
+void FQTermBuffer::clearLineChanged(int index) {
+ FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_);
+ text_lines_[index]->clearDirtyFlag();
+}
+
+void FQTermBuffer::setLineAllChanged(int index) {
+ FQ_ASSERT(0 <= index && index < num_rows_ + num_hist_lines_);
+ text_lines_[index]->setAllDirty();
+}
+
+void FQTermBuffer::scrollLinesInTerm(int startRow, int numRows) {
+ if (numRows == 0 || startRow > bottom_row_) {
+ return ;
+ }
+ if (startRow < top_row_) {
+ startRow = top_row_;
+ }
+
+ // TODO: performance issue here. Reuse the old text lines.
+ if (numRows > 0) {
+ //We are scrolling the whole screen.
+ if (startRow == 0 && caret_.row_ == num_rows_ - 1) {
+ addHistoryLine(numRows);
+ } else {
+ // delete lines from startRow, insert lines on the bottom_row_.
+ while (numRows) {
+ delete text_lines_.takeAt(num_hist_lines_ + startRow);
+ text_lines_.insert(num_hist_lines_ + bottom_row_, new FQTermTextLine(num_columns_));
+ numRows--;
+ }
+ }
+ }
+
+ // TODO: performance issue here. Reuse the old text lines.
+ if (numRows < 0) {
+ // delete lines from bottom_row_, insert lines in the startRow.
+ while (numRows) {
+ delete text_lines_.takeAt(num_hist_lines_ + bottom_row_);
+ text_lines_.insert(num_hist_lines_ + startRow, new FQTermTextLine(num_columns_));
+ numRows++;
+ }
+ }
+
+ for (int i = num_hist_lines_ + startRow; i <= num_hist_lines_ + bottom_row_; i++) {
+ text_lines_.at(i)->setAllDirty();
+ }
+}
+
+void FQTermBuffer::eraseToTermEnd() {
+ FQ_TRACE("term", 8) << "erase to term end";
+
+ if (caret_.column_ == 0 && caret_.row_ == 0) {
+ eraseEntireTerm();
+ return ;
+ }
+
+ clearArea(caret_.column_, caret_.row_, -1, 1, caret_.color_, caret_.attr_);
+
+ if (caret_.row_ < bottom_row_) {
+ clearArea(0, caret_.row_ + 1,
+ -1, bottom_row_ - caret_.row_,
+ caret_.color_, caret_.attr_);
+ }
+}
+
+void FQTermBuffer::eraseToTermBegin() {
+ FQ_TRACE("term", 8) << "erase to term begin";
+ if (caret_.column_ == num_columns_ - 1 && caret_.row_ == num_rows_ - 1) {
+ eraseEntireTerm();
+ return ;
+ }
+
+ clearArea(0, caret_.row_, caret_.column_ + 1, 1, caret_.color_, caret_.attr_);
+ if (caret_.row_ > top_row_) {
+ clearArea(0, top_row_, -1, caret_.row_, caret_.color_, caret_.attr_);
+ }
+}
+
+void FQTermBuffer::eraseEntireTerm() {
+ FQ_TRACE("term", 8) << "erase entire term";
+ addHistoryLine(num_rows_);
+ clearArea(0, 0, num_columns_, num_rows_, caret_.color_, caret_.attr_);
+}
+
+// width = -1 : clear to end
+void FQTermBuffer::clearArea(int startColumn, int startRow,
+ int width, int height,
+ unsigned char color, unsigned char attr) {
+ QByteArray cstr;
+
+ FQTermTextLine *line;
+
+ if (startRow + height > num_rows_) {
+ height = num_rows_ - startRow;
+ }
+
+ for (int i = startRow; i < height + startRow; i++) {
+ line = text_lines_[i + num_hist_lines_];
+
+ int w = width;
+
+ if (startColumn < num_columns_) {
+ if (w == -1) {
+ w = num_columns_ - startColumn;
+ }
+
+ int endX = startColumn + w;
+ if (endX > (int)line->getWidth()) {
+ endX = line->getWidth();
+ }
+
+ if(startColumn>=endX)
+ {
+ continue;
+ }
+
+ line->replaceWithWhiteSpace(w, startColumn, endX, color, attr);
+ }
+ }
+}
+
+void FQTermBuffer::addHistoryLine(int n) {
+ bool is_full = (num_hist_lines_ == max_num_hist_lines_);
+
+ // TODO: performance issue. Reuse the old lines.
+ while (n) {
+ if (num_hist_lines_ == max_num_hist_lines_) {
+ delete text_lines_.takeFirst();
+ }
+
+ text_lines_.append(new FQTermTextLine(num_columns_));
+ num_hist_lines_ = qMin(num_hist_lines_ + 1, max_num_hist_lines_);
+ n--;
+ }
+
+ for (int i = num_hist_lines_ + 0; i < num_hist_lines_ + bottom_row_; i++) {
+ text_lines_.at(i)->setAllDirty();
+ }
+
+ if (!is_full) {
+ emit bufferSizeChanged();
+ }
+}
+
+void FQTermBuffer::startDecode() {
+ last_saved_caret_.column_ = caret_.column_;
+ last_saved_caret_.row_ = caret_.row_;
+}
+
+void FQTermBuffer::endDecode() {
+ if (last_saved_caret_.row_ < num_rows_) {
+ FQTermTextLine *line = text_lines_[last_saved_caret_.row_ + num_hist_lines_];
+
+ line->safelySetDirtyFlag(last_saved_caret_.column_,
+ last_saved_caret_.column_ + 1);
+ }
+
+ if (caret_.row_ < num_rows_) {
+ FQTermTextLine *line = text_lines_.at(caret_.row_ + num_hist_lines_);
+
+ line->safelySetDirtyFlag(caret_.column_, caret_.column_ + 1);
+ }
+
+ clearSelect();
+}
+
+void FQTermBuffer::setMode(TermMode mode) {
+ FQ_TRACE("term", 8) << "set mode " << mode;
+
+ switch (mode) {
+ case INSERT_MODE:
+ is_insert_mode_ = true;
+ break;
+ case CURSOR_MODE:
+ is_cursor_mode_ = true;
+ break;
+ case ANSI_MODE:
+ is_ansi_mode_ = true;
+ case NUMERIC_MODE:
+ is_numeric_mode_ = true;
+ break;
+ case SMOOTH_MODE:
+ is_smoothscroll_mode_ = true;
+ break;
+ case NEWLINE_MODE:
+ is_newline_mode_ = true;
+ break;
+ case ORIGIN_MODE:
+ is_origin_mode_ = true;
+ this->moveCaretTo(0, top_row_);
+ break;
+ case AUTOWRAP_MODE:
+ is_autowrap_mode_ = true;
+ break;
+ case AUTOREPEAT_MODE:
+ is_autorepeat_mode_ = true;
+ break;
+ case LIGHTBG_MODE:
+ is_lightbg_mode_ = true;
+ break;
+ default:
+ break;
+ }
+}
+
+void FQTermBuffer::resetMode(TermMode mode) {
+ FQ_TRACE("term", 8) << "reset mode " << mode;
+
+ switch (mode) {
+ case INSERT_MODE:
+ is_insert_mode_ = false;
+ break;
+ case CURSOR_MODE:
+ is_cursor_mode_ = false;
+ break;
+ case ANSI_MODE:
+ is_ansi_mode_ = false;
+ case NUMERIC_MODE:
+ is_numeric_mode_ = false;
+ break;
+ case SMOOTH_MODE:
+ is_smoothscroll_mode_ = false;
+ break;
+ case NEWLINE_MODE:
+ is_newline_mode_ = false;
+ break;
+ case ORIGIN_MODE:
+ is_origin_mode_ = false;
+ this->moveCaretTo(0, 0);
+ break;
+ case AUTOWRAP_MODE:
+ is_autowrap_mode_ = false;
+ break;
+ case AUTOREPEAT_MODE:
+ is_autorepeat_mode_ = false;
+ break;
+ case LIGHTBG_MODE:
+ is_lightbg_mode_ = false;
+ break;
+ default:
+ break;
+ }
+}
+
+void FQTermBuffer::setSelect(const QPoint &pt1, const QPoint &pt2) {
+ QPoint ptSelStart, ptSelEnd;
+
+ if (pt1.y() == pt2.y()) {
+ ptSelStart = pt1.x() < pt2.x() ? pt1 : pt2;
+ ptSelEnd = pt1.x() > pt2.x() ? pt1 : pt2;
+ } else {
+ ptSelStart = pt1.y() < pt2.y() ? pt1 : pt2;
+ ptSelEnd = pt1.y() > pt2.y() ? pt1 : pt2;
+ }
+
+ int y1 = ptSelStart.y();
+ int y2 = ptSelEnd.y();
+
+ if (!(selection_start_ == QPoint(-1, -1)
+ && selection_end_ == QPoint(-1, -1))) {
+ y1 = qMin(y1, selection_start_.y());
+ y2 = qMax(y2, selection_end_.y());
+ }
+
+ for (int i = y1; i <= y2; i++) {
+ text_lines_.value(i, NULL)->setAllDirty();
+ }
+
+ selection_start_ = ptSelStart;
+ selection_end_ = ptSelEnd;
+}
+
+void FQTermBuffer::clearSelect() {
+ if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) {
+ return;
+ }
+
+ for (int i = selection_start_.y(); i <= selection_end_.y(); i++) {
+ text_lines_.value(i, NULL)->setAllDirty();
+ }
+
+ selection_start_ = selection_end_ = QPoint(-1, -1);
+}
+
+bool FQTermBuffer::isSelected(int line_index) const {
+ if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) {
+ return false;
+ } else {
+ return line_index >= selection_start_.y() && line_index <= selection_end_.y();
+ }
+}
+
+bool FQTermBuffer::isSelected(const QPoint &cell, bool is_rect_sel) const {
+ if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) {
+ return false;
+ }
+
+ if (cell.y() < 0 || cell.y() >= getNumLines()) {
+ return false;
+ }
+
+ int x1 = selection_start_.x();
+ int y1 = selection_start_.y();
+
+ int x2 = selection_end_.x();
+ int y2 = selection_end_.y();
+
+ if (cell.y() < y1 || cell.y() > y2)
+ return false;
+
+ const FQTermTextLine *line = this->getTextLineInBuffer(cell.y());
+
+ int cell_begin = 0;
+ int cell_end = cell.x() + 1;
+
+ if (is_rect_sel) {
+ int minx = qMin(x1, x2);
+ int maxx = qMax(x1, x2);
+ cell_begin = line->getCellBegin(qMin(minx, (int)line->getWidth()));
+ cell_end = line->getCellEnd(qMin(maxx + 1, (int)line->getWidth()));
+ } else {
+ if (cell.y() == y1) {
+ cell_begin = line->getCellBegin(qMin(x1, (int)line->getWidth()));
+ }
+ if (cell.y() == y2) {
+ cell_end = line->getCellEnd(qMin(x2 + 1, (int)line->getWidth()));
+ }
+ }
+
+ return cell_begin <= cell.x() && cell.x() < cell_end;
+}
+
+static void removeTrailSpace(QString &line) {
+ for (int last_non_space = line.size() - 1;
+ last_non_space >= 0; --last_non_space) {
+ QChar a = line.at(last_non_space);
+ if (!a.isSpace()) {
+ line.resize(last_non_space + 1);
+ break;
+ }
+
+ if (last_non_space == 0) {
+ line.resize(0);
+ }
+ }
+}
+
+
+QString FQTermBuffer::getTextSelected(bool is_rect_sel, bool is_color_copy,
+ const QByteArray &escape) const {
+ QString cstrSelect;
+ QString strTemp;
+
+ if (selection_start_ == QPoint(-1, -1) && selection_end_ == QPoint(-1, -1)) {
+ return cstrSelect;
+ }
+
+ QRect rc;
+
+ for (int i = selection_start_.y(); i <= selection_end_.y(); i++) {
+ strTemp.clear();
+ rc = getSelectRect(i, is_rect_sel);
+
+ FQTermTextLine *line = text_lines_.at(i);
+ unsigned cell_begin = qMax(rc.left(), 0);
+ unsigned cell_end = qMin(rc.right() + 1, (int)line->getWidth());
+ FQ_ASSERT(rc.left() + rc.width() == rc.right() + 1);
+
+ if (cell_begin < cell_end) {
+ cell_begin = line->getCellBegin(cell_begin);
+ cell_end = line->getCellEnd(cell_end);
+
+ if (is_color_copy) {
+ line->getAnsiText(cell_begin, cell_end, strTemp, escape);
+ } else {
+ line->getPlainText(cell_begin, cell_end, strTemp);
+ }
+ }
+
+ removeTrailSpace(strTemp);
+
+ cstrSelect += strTemp;
+
+ // add newline except the last line
+ if (i != selection_end_.y()) {
+ cstrSelect += OS_NEW_LINE;
+ }
+ }
+
+ return cstrSelect;
+}
+
+QRect FQTermBuffer::getSelectRect(int line_index, bool is_rect_sel) const {
+ FQ_ASSERT(isSelected(line_index));
+
+ if (is_rect_sel) {
+ return QRect(qMin(selection_start_.x(), selection_end_.x()), line_index,
+ abs(selection_end_.x() - selection_start_.x()) + 1, 1);
+ } else if (selection_start_.y() == selection_end_.y()) {
+ return QRect(selection_start_.x(), line_index,
+ qMin(selection_end_.x(), num_columns_) - selection_start_.x() + 1, 1);
+ } else if (line_index == selection_start_.y()) {
+ return QRect(selection_start_.x(), line_index,
+ qMax(0, num_columns_ - selection_start_.x()), 1);
+ } else if (line_index == selection_end_.y()) {
+ return QRect(0, line_index, qMin(num_columns_, selection_end_.x() + 1), 1);
+ } else {
+ return QRect(0, line_index, num_columns_, 1);
+ }
+}
+
+void FQTermBuffer::scrollTerm(int numRows) {
+ scrollLinesInTerm(caret_.row_, numRows);
+ moveCaretOffset(0, numRows);
+}
+
+
+} // namespace FQTerm
+
+#include "fqterm_buffer.moc"
diff --git a/src/terminal/fqterm_buffer.h b/src/terminal/fqterm_buffer.h
new file mode 100644
index 0000000..3e0ef6a
--- /dev/null
+++ b/src/terminal/fqterm_buffer.h
@@ -0,0 +1,297 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_BUFFER_H
+#define FQTERM_BUFFER_H
+
+#include <QList>
+#include <QObject>
+#include <QPoint>
+
+#include "fqterm_text_line.h"
+class QString;
+class QByteArray;
+class QRect;
+
+namespace FQTerm {
+
+class FQTermTextLine;
+
+/* Text buffer consists of characters in a matrix of cells.
+ *
+ * Buffer
+ * +--------------------------------+
+ * ||----> |
+ * || x |
+ * || |
+ * |V Y |
+ * | |
+ * . .
+ * . .
+ * . histroical lines .
+ * . .
+ * . .
+ * | |
+ * | |
+ * | |
+ * | |
+ * |+------------------------------+|
+ * |||----> ||
+ * ||| x ||
+ * ||| terminal ||
+ * ||V Y ||
+ * || ||
+ * || ||
+ * || | (caret) ||
+ * || ||
+ * || ||
+ * |+------------------------------+|
+ * +--------------------------------+
+ *
+ * Text buffer, to manage lines of characters with colors and
+ * attributes, caret positions, terminal size/margins, selection
+ * region, historical text lines, buffer write mode (insert or
+ * replace), and etc.
+ *
+ * Note: There are two coordinates, relative to terminal or entire
+ * buffer. When we use (column, row) it indicates terminal coordinate,
+ * while when we use (column, line) it indicates buffer coordinate.
+ * This rule applies both in variable names and function names.
+ **/
+class FQTermBuffer: public QObject {
+ Q_OBJECT;
+ public:
+ enum TermMode {
+ INSERT_MODE, /* otherwise replace mode */
+ ANSI_MODE, /* otherwise VT52 mode */
+ SMOOTH_MODE, /* otherwse jump mode */
+ NEWLINE_MODE, /* otherwise line feed mode */
+ CURSOR_MODE, /* otherwise reset cursor mode */
+ NUMERIC_MODE, /* otherwise application mode */
+ ORIGIN_MODE, /* otherwise absolute mode */
+ AUTOWRAP_MODE, /* otherwise non-autowrap mode */
+ AUTOREPEAT_MODE, /* otherwise non-autorepeat mode */
+ LIGHTBG_MODE, /* otherwise dark background mode */
+ };
+
+ enum VtCharSet {
+ UNITED_KINGDOM_SET,
+ ASCII_SET,
+ SPECIAL_GRAPHICS,
+ ALTERNATE_CHARACTER_ROM_STANDARD_CHARACTER_SET,
+ ALTERNATE_CHARACTER_ROM_SPECIAL_GRAPHICS
+ };
+
+ FQTermBuffer(int column, int row, int max_hist_line, bool is_bbs);
+ ~FQTermBuffer();
+
+ // Get text lines.
+ // Return null if the line_index is out of bound, return NULL.
+ const FQTermTextLine *getTextLineInBuffer(int line_index) const;
+ const FQTermTextLine *getTextLineInTerm(int line_index) const;
+
+ // Get number of columns and rows of the term, or lines of the
+ // entire buffer.
+ int getNumColumns() const;
+ int getNumRows() const;
+ int getNumLines() const;
+
+ // Set the size of the screen.
+ void setTermSize(int col, int row);
+ // set margins in term. (Generally speaking, most operations are
+ // restrictied in [top_row_, bottom_row_] of terminal.)
+ void setMargins(int top_row, int bottom_row);
+
+ // the caret's coordinate in term or buffer.
+ int getCaretColumn() const;
+ int getCaretRow() const;
+ int getCaretLine() const;
+
+ // Set current attribute.
+ void setCurrentAttr(unsigned char color, unsigned char attr);
+
+ // Below are a series of functions to modify the content of buffer.
+ // These functions will work from the current caret position.
+ // All the behaviours of these functions may be influenced by the
+ // mode of this buffer (is_insert_mode_, is_newline_mode_, and
+ // etc.).
+
+ void writeText(const QString &cstr, int charstate = FQTermTextLine::NORMAL);
+
+ void fillScreenWith(char c);
+
+ void insertSpaces(int count);
+ void insertLines(int count);
+
+ void deleteText(int cell_count);
+ void deleteLines(int line_count);
+
+ void eraseText(int cell_count);
+
+ void eraseToLineBegin();
+ void eraseToLineEnd();
+ void eraseEntireLine();
+
+ void eraseToTermBegin();
+ void eraseToTermEnd();
+ void eraseEntireTerm();
+
+ // Set a line of buffer to have been changed from start to end.
+ void setLineChanged(int index, int cell_begin, int cell_end);
+ void clearLineChanged(int index);
+ void setLineAllChanged(int index);
+
+ // Functions about caret.
+ // If the caret is moved, the cells of the position of the last caret will
+ // be marked as changed.
+ void moveCaretOffset(int coloumn_offset, int row_offset,
+ bool scroll_if_necessary = false);
+ void moveCaretTo(int coloumn, int row, bool scroll_if_necessary = false);
+ void changeCaretPosition(int coloumn, int row);
+ void saveCaret();
+ void restoreCaret();
+
+ void SelectVtCharacterSet(VtCharSet charset, bool G0);
+ void invokeCharset(bool G0);
+
+ // non-printing characters
+ void tab();
+ void carriageReturn();
+ void lineFeed();
+
+ void addTabStop();
+ void clearTabStop(bool clear_all);
+
+ // Select Mode (SM) and Reset Mode (RM) functions.
+ // See ANSI X3.64 Mode-Changing Parameters.
+ void setMode(TermMode);
+ void resetMode(TermMode);
+
+ bool isCursorMode() const { return is_cursor_mode_; }
+ bool isAnsiMode() const { return is_ansi_mode_; }
+ bool isNumericMode() const { return is_numeric_mode_; }
+ bool isAutoRepeatMode() const { return is_autorepeat_mode_; }
+ bool isLightBackgroundMode() const { return is_lightbg_mode_; }
+ bool isNewLineMode() const {return is_newline_mode_;}
+
+ // for test
+ void startDecode();
+ void endDecode();
+
+ // Functions about selection in buffer.
+ void setSelect(const QPoint &start_point, const QPoint &end_point);
+ void clearSelect();
+ bool isSelected(const QPoint &cell, bool isRectSel) const;
+ bool isSelected(int line_index) const;
+
+ QString getTextSelected(bool is_rect_sel, bool is_color_copy,
+ const QByteArray &escape) const;
+
+ // Get the rectangle of selected text of a certain line in buffer.
+ // Note: please ensure there exits any cell of the line line_index
+ // is selected.
+ QRect getSelectRect(int line_index, bool is_rect_sel) const;
+
+ //Scroll line && adjust caret pos.
+ //Scroll must occur at the line where caret is
+ void scrollTerm(int numRows);
+
+ signals:
+ void bufferSizeChanged();
+ void termSizeChanged(int column, int row);
+ void onSetTermSize(int col, int row);
+ void caretChangeRow();
+ private:
+ // Scroll lines between startRow and bottom_row_ (see setMargin()).
+ // num > 0 scroll up.
+ // num < 0 scroll down.
+ void scrollLinesInTerm(int startRow, int numRows);
+
+ // Replace a area of cells with spaces.
+ void clearArea(int startColumn, int startRow,
+ int width, int height,
+ unsigned char color, unsigned char attr);
+
+ // Append n empty lines to the buffer and make the first n lines of
+ // term historical.
+ void addHistoryLine(int n);
+
+ int getTabStop(int column);
+
+ struct Caret {
+ int column_, row_;
+ int color_, attr_;
+ VtCharSet G0_, G1_;
+
+ Caret();
+ };
+
+ // terminal size.
+ int num_columns_, num_rows_;
+
+ // term margins. (Generally speaking, most operations are
+ // restrictied in [top_row_, bottom_row_] of terminal.)
+ int top_row_, bottom_row_;
+
+ // Historical data length and max length.
+ int num_hist_lines_, max_num_hist_lines_;
+
+ // All lines of text, including both historical lines and lines in
+ // current terminal.
+ //
+ // Note: the number of historical lines, num_hist_lines, may
+ // increase from 0 to max_num_hist_lines_.
+ QList<FQTermTextLine *> text_lines_;
+
+ void *tab_stops_;
+
+ // The caret in terminal
+ Caret caret_;
+ Caret last_saved_caret_;
+
+ bool is_g0_used_; // is G0 or G1 charset used;
+
+ bool is_insert_mode_; // Is insert or replace mode.
+ bool is_ansi_mode_; // Is ansi or vt52 mode.
+ bool is_smoothscroll_mode_; // Is smooth or jump scrolling mode.
+ bool is_newline_mode_; // Is newline or linefeed mode.
+ bool is_cursor_mode_; // Is cursor key mode.
+ bool is_numeric_mode_; // Is numeric or application mode.
+ bool is_origin_mode_; // Is origin or absolute mode.
+ bool is_autowrap_mode_; // Is auto-wrap mode enabled.
+ bool is_autorepeat_mode_; // Is auto-repeat mode enabled.
+ bool is_lightbg_mode_; // Is ligth background or dark backgrond mode.
+
+ // Selection in buffer. If section start and end are in the same
+ // line, start.x should be equal to or less than end.x. Otherwise
+ // start.y should be less than end.y.
+ //
+ // If selection_start_ = selection_start_ = (-1, -1), the selection
+ // is empty.
+ QPoint selection_start_;
+ QPoint selection_end_;
+
+ // whether this buffer is used for a bbs session.
+ bool is_bbs_;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_BUFFER_H
diff --git a/src/terminal/fqterm_session.cpp b/src/terminal/fqterm_session.cpp
new file mode 100644
index 0000000..6c5b513
--- /dev/null
+++ b/src/terminal/fqterm_session.cpp
@@ -0,0 +1,1648 @@
+/***************************************************************************
+ * 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 "fqterm.h"
+#include "common.h"
+#include "fqterm_trace.h"
+#include "fqterm_session.h"
+#include "fqterm_buffer.h"
+#include "fqterm_text_line.h"
+#include "fqterm_telnet.h"
+#include "fqterm_decode.h"
+#include "fqterm_zmodem.h"
+
+#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif //HAVE_PYTHON
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <windows.h>
+#endif
+
+
+#include <QString>
+#include <QTimer>
+#include <QMutex>
+#include <QRegExp>
+#include <QtAlgorithms>
+#include <QChar>
+#include <QPair>
+#include <QReadLocker>
+#include <QWriteLocker>
+#include <QReadWriteLock>
+
+namespace FQTerm {
+
+const QString FQTermSession::endOfUrl[] = {
+ "ac","ad","ae","af","ag","ai","al","am","an","ao","aq","ar","as","at",
+ "au","aw","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bm","bn","bo","br",
+ "bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm",
+ "cn","co","uk","com","cr","cs","cu","cv","cx","cy","cz","de","dj","dk","dm",
+ "do","dz","ec","edu","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo",
+ "fr","ga","gd","ge","gf","gg","gh","gi","gl","gm","gn","gov","gp","gq","aero",
+ "asia","biz","coop","eu","info","museum","name","pro","travel","gr","gs","gt",
+ "gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","int",
+ "io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp",
+ "kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma",
+ "mc","md","mg","mh","mil","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt",
+ "mu","mv","mw","mx","my","mz","na","nc","ne","net","nf","ng","ni","nl","no",
+ "np","nr","nt","nu","nz","om","org","pa","pe","pf","pg","ph","pk","pl","pm",
+ "pn","pr","ps","pt","pw","py","qa","re","ro","ru","rw","sa","sb","sc","sd","se",
+ "sg","sh","si","sj","sk","sl","sm","sn","so","sr","sv","st","sy","sz","tc","td",
+ "tf","tg","th","tj","tk","tm","tn","to","tp","tr","tt","tv","tw","tz","ua","ug",
+ "uk","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt",
+ "yu","za","zm","zw"
+};
+
+FQTermSession::FQTermSession(FQTermConfig *config, FQTermParam param) {
+ reconnectRetry_ = 0;
+ scriptListener_ = NULL;
+ param_ = param;
+ termBuffer_ = new FQTermBuffer(param_.numColumns_,
+ param_.numRows_,
+ param_.numScrollLines_,
+ param_.hostType_ == 0);
+
+ if (param.protocolType_ == 0) {
+ telnet_ = new FQTermTelnet(param_.virtualTermType_.toLatin1(),
+ param_.numRows_, param_.numColumns_, param.protocolType_, param.hostType_ );
+ } else if (param.protocolType_ == 3) {
+ telnet_ = new FQTermTelnet(param_.virtualTermType_.toLatin1(),
+ param_.numRows_, param_.numColumns_, param.protocolType_, param.hostType_ );
+ } else {
+#if defined(_NO_SSH_COMPILED)
+ QMessageBox::warning(this, "sorry",
+ "SSH support is not compiled, "
+ "FQTerm can only use Telnet!");
+ telnet_ = new FQTermTelnet(param_.virtualTermType_.toUtf8(),
+ param_.numRows_, param_.numColumns_, param.protocolType_, param.hostType_ );
+#else
+ telnet_ = new FQTermTelnet(param_.virtualTermType_.toUtf8(),
+ param_.numRows_, param_.numColumns_, param.protocolType_ , param.hostType_ ,
+ param_.sshUserName_.toUtf8(),
+ param_.sshPassword_.toUtf8());
+#endif
+ }
+
+ zmodem_ = new FQTermZmodem(config, telnet_, param.protocolType_, param.serverEncodingID_);
+ decoder_ = new FQTermDecode(termBuffer_, param.serverEncodingID_);
+ FQ_VERIFY(connect(decoder_, SIGNAL(enqReceived()), this, SLOT(onEnqReceived())));
+ FQ_VERIFY(connect(decoder_, SIGNAL(onTitleSet(const QString&)), this, SIGNAL(onTitleSet(const QString&))));
+
+ isConnected_ = false;
+#ifndef _NO_SSH_COMPILED
+ if (param.protocolType_ != 0) {
+ isSSHLogining_ = true;
+ } else {
+ isSSHLogining_ = false;
+ }
+#else
+ isSSHLogining_ = false;
+#endif
+ isTelnetLogining_ = false;
+ isIdling_ = false;
+ isSendingMessage_ = false;
+ isMouseX11_ = false;
+
+ idleTimer_ = new QTimer;
+ autoReplyTimer_ = new QTimer;
+
+ acThread_ = new ArticleCopyThread(*this, waitCondition_, bufferWriteLock_);
+
+ FQ_VERIFY(connect(decoder_, SIGNAL(mouseMode(bool)),
+ this, SLOT(setMouseMode(bool))));
+ FQ_VERIFY(connect(telnet_, SIGNAL(readyRead(int, int)),
+ this, SLOT(readReady(int, int))));
+ FQ_VERIFY(connect(telnet_, SIGNAL(TelnetState(int)),
+ this, SLOT(changeTelnetState(int))));
+ FQ_VERIFY(connect(telnet_, SIGNAL(errorMessage(QString)),
+ this, SIGNAL(errorMessage(QString))));
+ FQ_VERIFY(connect(telnet_, SIGNAL(requestUserPwd(QString*, QString*, bool*)),
+ this, SIGNAL(requestUserPwd(QString*, QString*, bool*))));
+
+ FQ_VERIFY(connect(telnet_, SIGNAL(onSSHAuthOK()),
+ this, SLOT(onSSHAuthOK())));
+
+
+
+ FQ_VERIFY(connect(termBuffer_, SIGNAL(onSetTermSize(int, int)),
+ telnet_, SLOT(windowSizeChanged(int, int))));
+
+ FQ_VERIFY(connect(zmodem_, SIGNAL(ZmodemState(int, int, const char *)),
+ this, SIGNAL(zmodemStateChanged(int, int, const char *))));
+
+ FQ_VERIFY(connect(idleTimer_, SIGNAL(timeout()), this, SLOT(onIdle())));
+ FQ_VERIFY(connect(autoReplyTimer_, SIGNAL(timeout()),
+ this, SLOT(onAutoReply())));
+
+ FQ_VERIFY(connect(acThread_, SIGNAL(articleCopied(int, const QString)),
+ this, SIGNAL(articleCopied(int, const QString))));
+
+ setAntiIdle(param_.isAntiIdle_);
+}
+
+FQTermSession::~FQTermSession() {
+ delete idleTimer_;
+ delete autoReplyTimer_;
+ delete acThread_;
+ delete termBuffer_;
+ delete telnet_;
+ delete zmodem_;
+ delete decoder_;
+}
+
+const QString FQTermSession::protocolPrefix[] = {
+ "http://", "https://", "mms://", "rstp://", "ftp://", "mailto:", "telnet://"
+};
+
+void FQTermSession::setScreenStart(int nStart) {
+ screenStartLineNumber_ = nStart;
+}
+
+bool FQTermSession::setCursorPos(const QPoint &pt, QRect &rc) {
+ QRect rectOld = getMenuRect();
+
+ cursorPoint_ = pt;
+
+ detectMenuRect();
+ QRect rectNew = getMenuRect();
+
+ rc = rectOld | rectNew;
+
+ return rectOld != rectNew;
+}
+
+QString FQTermSession::getMessage() {
+ const FQTermTextLine *line;
+ LineColorInfo colorInfo;
+ QString message;
+
+ getLineColorInfo(termBuffer_->getTextLineInTerm(0), &colorInfo);
+ if (!colorInfo.uniBackgroundColor) {
+ return message;
+ }
+
+ int i = 1;
+ termBuffer_->getTextLineInTerm(0)->getAllPlainText(message);
+
+ line = termBuffer_->getTextLineInTerm(i);
+ getLineColorInfo(line, &colorInfo);
+ while (colorInfo.uniBackgroundColor && colorInfo.hasBackgroundColor) {
+ message += "\n";
+ line->getAllPlainText(message);
+ i++;
+ line = termBuffer_->getTextLineInTerm(i);
+ getLineColorInfo(line, &colorInfo);
+ }
+ return message;
+}
+
+
+void FQTermSession::detectPageState() {
+ //for smth type bbs.
+ pageState_ = Undefined;
+ if (param_.hostType_ != 0) {
+ return;
+ }
+
+ const FQTermTextLine *line[4];
+ LineColorInfo colorInfo[4];
+ int lineIndex[4] = {0, 1, 2};
+ lineIndex[3] = termBuffer_->getNumRows() - 1;
+ for (int i = 0; i < 4; ++i) {
+ line[i] = termBuffer_->getTextLineInTerm(lineIndex[i]);
+ getLineColorInfo(line[i], colorInfo + i);
+ }
+
+ //TODO: Detect PageState in a clearer way.
+ if (!colorInfo[0].hasBackgroundColor) {
+ if (colorInfo[3].foregroundColorIndex.count() != 0 &&
+ colorInfo[3].hasBackgroundColor){
+ if (colorInfo[3].uniBackgroundColor) {
+ QString text;
+ line[3]->getAllPlainText(text);
+ if (text[0] != L'\x3010') {
+ pageState_ = Read;
+ } else {
+ pageState_ = Edit;
+ }
+ } else if (colorInfo[3].backgroundColorIndex.count() == 2 &&
+ colorInfo[3].backgroundColorIndex.at(0) == 4 &&
+ colorInfo[3].backgroundColorIndex.at(1) == 0){
+ pageState_ = Read;
+ }
+ }
+ return;
+ }
+
+ if (colorInfo[0].backgroundColorIndex.at(0) != 4
+ || !(colorInfo[3].hasBackgroundColor && colorInfo[3].backgroundColorIndex.at(0) == 4)) {
+ if (!colorInfo[3].hasBackgroundColor &&
+ colorInfo[0].foregroundColorIndex.count() == 4 &&
+ colorInfo[0].foregroundColorIndex.at(1) == 4 &&
+ colorInfo[0].foregroundColorIndex.at(2) == 7 &&
+ colorInfo[0].foregroundColorIndex.at(3) == 4){
+ pageState_ = TOP10;
+ }
+ return;
+ }
+
+ if (colorInfo[1].hasBackgroundColor && colorInfo[1].uniBackgroundColor) {
+ pageState_ = Message;
+ return;
+ }
+
+ if (colorInfo[0].uniBackgroundColor) {
+ if (!colorInfo[2].hasBackgroundColor ||
+ colorInfo[2].backgroundColorIndex.at(0) != 4 ||
+ colorInfo[2].backgroundColorIndex.count() > 3) {
+ pageState_ = Menu;
+ return;
+ }
+ }
+
+ if (!colorInfo[0].uniBackgroundColor &&
+ colorInfo[0].backgroundColorIndex.at(0) == 4 &&
+ !colorInfo[3].uniBackgroundColor) {
+ //announce, elite root
+ pageState_ = EliteArticleList;
+ return;
+ }
+
+ if (colorInfo[2].hasBackgroundColor &&
+ !colorInfo[2].uniBackgroundColor &&
+ colorInfo[2].backgroundColorIndex.at(0) == 4 &&
+ !colorInfo[3].uniBackgroundColor) {
+ pageState_ = EliteArticleList;
+ return;
+ }
+
+ if (colorInfo[0].foregroundColorIndex.indexOf(14) != -1) {
+ pageState_ = ArticleList;
+ return;
+ }
+
+ if (colorInfo[2].foregroundColorIndex.indexOf(7) != -1) {
+ QString text;
+ line[2]->getAllPlainText(text);
+ if (text[0] == ' ') {
+ pageState_ = BoardList;
+ } else {
+ pageState_ = MailMenu;
+ }
+ }
+
+}
+
+FQTermSession::CursorType FQTermSession::getCursorType(const QPoint &pt) {
+ if (screenStartLineNumber_ !=
+ (termBuffer_->getNumLines() - termBuffer_->getNumRows())) {
+ return kNormal;
+ }
+
+ QRect rc = getMenuRect();
+ CursorType nCursorType = kNormal;
+ switch (pageState_) {
+ case Undefined: // not recognized
+ if (rc.contains(pt)) {
+ //HAND
+ nCursorType = kRight;
+ } else {
+ nCursorType = kNormal;
+ }
+ break;
+ case Menu:
+ case MailMenu:
+ // menu
+ if (pt.x() < 5) {
+ // LEFT
+ nCursorType = kLeft;
+ } else if (rc.contains(pt)) {
+ // HAND
+ nCursorType = kRight;
+ } else {
+ nCursorType = kNormal;
+ }
+ break;
+ case ArticleList:
+ case BoardList:
+ case EliteArticleList:
+ case FriendMailList:
+ // list
+ if (pt.x() < 12) {
+ // LEFT
+ nCursorType = kLeft;
+ } else if (pt.y() - screenStartLineNumber_ < 3) {
+ // HOME
+ nCursorType = kHome;
+ } else if (pt.y() == termBuffer_->getNumLines() - 1) {
+ // END
+ nCursorType = kEnd;
+ } else if (pt.x() > termBuffer_->getNumColumns() - 16 &&
+ pt.y() - screenStartLineNumber_ <= termBuffer_->getNumRows() / 2) {
+ //PAGEUP
+ nCursorType = kPageUp;
+ } else if (pt.x() > termBuffer_->getNumColumns() - 16
+ && pt.y() - screenStartLineNumber_ >
+ termBuffer_->getNumRows() / 2) {
+ // PAGEDOWN
+ nCursorType = kPageDown;
+ } else if (rc.contains(pt)) {
+ // HAND
+ nCursorType = kRight;
+ } else {
+ nCursorType = kNormal;
+ }
+ break;
+ case Read:
+ // read
+ if (pt.x() < 12) {
+ // LEFT
+ nCursorType = kLeft;
+ } else if (pt.x() > (termBuffer_->getNumColumns() - 16) &&
+ (pt.y() - screenStartLineNumber_)
+ <= termBuffer_->getNumRows() / 2) {
+ // PAGEUP
+ nCursorType = kPageUp;
+ } else if (pt.x() > (termBuffer_->getNumColumns() - 16) &&
+ (pt.y() - screenStartLineNumber_)
+ > termBuffer_->getNumRows() / 2) {
+ // PAGEDOWN
+ nCursorType = kPageDown;
+ } else if (rc.contains(pt)) {
+ //HAND
+ nCursorType = kRight;
+ } else {
+ nCursorType = kNormal;
+ }
+ break;
+ case Edit:
+ // TODO: add action for kEdit state.
+ if (rc.contains(pt)) {
+ //HAND
+ nCursorType = kRight;
+ } else {
+ nCursorType = kNormal;
+ }
+ break;
+ case TOP10:
+ if (pt.x() < 12) {
+ nCursorType = kLeft;
+ } else if (rc.contains(pt)){
+ nCursorType = kRight;
+ }
+ break;
+ default:
+ FQ_TRACE("error", 2) << "Error, wrong PageState.";
+ break;
+ }
+
+ return nCursorType;
+}
+
+bool FQTermSession::isSelectedMenu(int line) {
+ // TODO: possible performance issue
+ QRect rect = getMenuRect();
+
+ // nothing selected
+ if (rect.isNull()) {
+ return false;
+ }
+
+ return line >= rect.bottom() && line <= rect.top();
+}
+
+bool FQTermSession::isSelectedMenu(const QPoint &pt) {
+ // TODO: possible performance issue
+ QRect rect = getMenuRect();
+
+ // nothing selected
+ if (rect.isNull()) {
+ return false;
+ }
+
+ return rect.contains(pt);
+}
+
+FQTermSession::PageState FQTermSession::getPageState() {
+ return pageState_;
+}
+
+char FQTermSession::getMenuChar() {
+ return menuChar_;
+}
+
+void FQTermSession::setMenuRect(int row, int col, int len) {
+ menuRect_ = QRect(col, row, len, 1);
+}
+
+QRect FQTermSession::detectMenuRect() {
+ QRect rect(0, 0, 0, 0);
+ menuRect_ = rect;
+ if (scriptListener_)
+ {
+ bool res = scriptListener_->postScriptCallback(SFN_DETECT_MENU
+#ifdef HAVE_PYTHON
+ ,Py_BuildValue("l", scriptListener_->windowID())
+#endif //HAVE_PYTHON
+ );
+ if (res) return menuRect_;
+ }
+ // current screen scrolled
+ if (screenStartLineNumber_ !=
+ (termBuffer_->getNumLines() - termBuffer_->getNumRows())) {
+ return rect;
+ }
+
+ const FQTermTextLine *line;
+ int menuStartLine = 8;
+ switch (pageState_) {
+ case Undefined: break;
+ case MailMenu:
+ menuStartLine = 7;
+ case Menu:
+ if (cursorPoint_.y() - screenStartLineNumber_ >= menuStartLine &&
+ cursorPoint_.x() > 5) {
+ line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
+ QString cstr;
+ int cell_end = line->getCellEnd(qMin(cursorPoint_.x(),
+ (int)line->getWidth()));
+ line->getPlainText(0, cell_end, cstr);
+
+ int base = cstr.lastIndexOf(" ");
+ if (base == -1) {
+ base = 0;
+ }
+
+ QRegExp reg("[a-zA-Z0-9][).\\]]");
+ char indexChar = cstr.indexOf(reg, base);
+ if (indexChar != -1) {
+ menuChar_ = cstr.at(indexChar).toLatin1();
+
+ QString strTmp = cstr.left(cstr.indexOf(reg, base));
+ int nMenuStart = get_str_width((const UTF16 *)strTmp.data(),
+ strTmp.size());
+ if (cstr[indexChar - 1] == '(' || cstr[indexChar - 1] == '[') {
+ nMenuStart--;
+ }
+
+ cstr.clear();
+ line->getAllPlainText(cstr);
+
+
+ reg = QRegExp("[^ ]");
+
+ int nMenuBaseLength = 20;
+ int cell_begin =
+ line->getCellBegin(
+ qMin(nMenuStart + nMenuBaseLength, (int)line->getWidth() - 1));
+ int char_begin = line->getCharBegin(cell_begin);
+ //last [^ ] until char_begin
+ strTmp = cstr.left(cstr.lastIndexOf(reg, char_begin) + 1);
+
+ int nMenuLength =
+ get_str_width((const UTF16 *)strTmp.data(), strTmp.size())
+ - nMenuStart;
+ if (nMenuLength == nMenuBaseLength + 1) {
+ strTmp = cstr.left(cstr.indexOf(" ", char_begin) + 1);
+ nMenuLength =
+ get_str_width((const UTF16 *)strTmp.data(), strTmp.size())
+ - nMenuStart; //base length is not enough
+ }
+
+ if (cursorPoint_.x() >= nMenuStart && cursorPoint_.x() <=
+ nMenuStart + nMenuLength) {
+ rect.setX(nMenuStart);
+ rect.setY(cursorPoint_.y());
+ rect.setWidth(nMenuLength);
+ rect.setHeight(1);
+ }
+ }
+ }
+ break;
+ case ArticleList:
+ case BoardList:
+ case FriendMailList:
+ case EliteArticleList:
+ if ((cursorPoint_.y() - screenStartLineNumber_) >= 3
+ && (cursorPoint_.y() - screenStartLineNumber_)
+ < termBuffer_->getNumRows() -1
+ && cursorPoint_.x() >= 12
+ && cursorPoint_.x() <= termBuffer_->getNumColumns() - 16) {
+ line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
+ //QString str = line->getText();
+ QString str;
+ line->getAllPlainText(str);
+
+ if (str.count(" ") != (int)str.length()) {
+ rect.setX(0);
+ rect.setY(cursorPoint_.y());
+ rect.setWidth(line->getWidth());
+ rect.setHeight(1);
+ }
+ }
+ break;
+ case Read:
+
+ break;
+ case Edit:
+ break;
+ case TOP10:
+ {
+ int ln = cursorPoint_.y() - screenStartLineNumber_;
+ if (ln >= 3 && (ln & 1) && ln <= 21 &&
+ cursorPoint_.x() >= 12 &&
+ cursorPoint_.x() <= termBuffer_->getNumColumns() - 8){
+ line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
+
+ QString str;
+ line->getAllPlainText(str);
+
+ if (str.count(" ") != (int)str.length()) {
+ if (ln == 21){
+ menuChar_ = '0';
+ } else {
+ menuChar_ = ((ln - 1) >> 1) + '0';
+ }
+
+ rect.setX(12);
+ rect.setY(cursorPoint_.y());
+ rect.setWidth(line->getWidth() - 20);
+ rect.setHeight(1);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ menuRect_ = rect;
+ return rect;
+}
+
+bool FQTermSession::isIllChar(char ch) {
+ static char illChars[] = ";'\"[]<>^";
+ return ch > '~' || ch < '!' || strchr(illChars, ch) != NULL;
+}
+
+bool FQTermSession::isUrl(QRect &rcUrl, QRect &rcOld) {
+ return checkUrl(rcUrl, rcOld, false);
+}
+
+bool FQTermSession::isIP(QRect &rcUrl, QRect &rcOld) {
+ return checkUrl(rcUrl, rcOld, true);
+}
+
+QString FQTermSession::expandUrl(const QPoint& pt, QPair<int, int>& range)
+{
+ // int at = pt.x();
+ range.first = -1;
+ range.second = -2;
+ const FQTermTextLine *textLine = termBuffer_->getTextLineInBuffer(pt.y());
+ if (textLine == NULL || pt.x() > (int)textLine->getWidth()) {
+ return "";
+ }
+
+ QString text;
+ textLine->getAllPlainText(text);
+
+ int cell_begin = textLine->getCellBegin(pt.x());
+ int at = textLine->getCharBegin(cell_begin);
+ if (at >= text.length()) {
+ return "";
+ }
+
+ int start;
+ int end;
+ for (start = at; start >= 0 && !isIllChar(text.at(start).toLatin1());
+ start--) {
+ }
+ start++;
+ for (end = at; end < text.length() && !isIllChar(text.at(end).toLatin1());
+ end++) {
+ }
+ range.first = get_str_width((const UTF16 *)text.data(), start);
+ range.second = get_str_width((const UTF16 *)text.data(), end);
+ return text.mid(start, end - start);
+}
+
+
+bool FQTermSession::checkUrl(QRect &rcUrl, QRect &rcOld, bool checkIP) {
+
+
+
+ QPoint pt = cursorPoint_;
+ int at = pt.x();
+ rcOld = urlRect_;
+
+ if (at > urlRect_.left() && at < urlRect_.right() && urlRect_.y() == pt.y()) {
+ if ( (checkIP && !ip_.isEmpty()) ||
+ (!checkIP && !url_.isEmpty()) ) {
+ rcUrl = urlRect_;
+ return true;
+ }
+
+ }
+
+ QPoint urlStartPoint;
+ QPoint urlEndPoint;
+ urlStartPoint_ = QPoint();
+ urlEndPoint_ = QPoint();
+ urlRect_ = QRect(0, 0, -1, -1);
+ if (!checkIP) {
+ url_.clear();
+ } else {
+ ip_.clear();
+ }
+
+ QString urlText;
+ //phase 1: find all consecutive legal chars
+ QPair<int, int> range;
+ urlText = expandUrl(pt, range);
+ if (range.first < 0 || range.first >= range.second) {
+ return false;
+ }
+
+
+ QRect urlRect = QRect(range.first, pt.y(), range.second - range.first, 1);
+ urlStartPoint = QPoint(range.first, pt.y());
+ urlEndPoint = QPoint(range.second, pt.y());
+
+ if (range.second == termBuffer_->getNumColumns()) {
+ for (int i = pt.y() + 1; i < termBuffer_->getNumLines(); ++i) {
+ QString lineText = expandUrl(QPoint(0, i), range);
+ if (range.first == 0) {
+ urlText += lineText;
+ urlEndPoint.setY(urlEndPoint.y() + 1);
+ urlEndPoint.setX(range.second);
+ if (range.second == termBuffer_->getNumColumns()) {
+ continue;
+ }
+ }
+ break;
+ }
+ }
+
+ //phase 2: find protocol prefix for url
+ //if none, prefix to add is set to "mailto:" if '@' is found
+ //otherwise, the prefix is "http://" by default.
+ QString prefixToAdd;
+ int protocolIndex;
+ for (protocolIndex = 0; protocolIndex < ProtocolSupported; ++protocolIndex) {
+ int begin = urlText.indexOf(protocolPrefix[protocolIndex],
+ Qt::CaseInsensitive);
+ if (begin != -1 && begin <= at) {
+ prefixToAdd = protocolPrefix[protocolIndex];
+ urlText = urlText.mid(begin + protocolPrefix[protocolIndex].length());
+ urlStartPoint.setX(urlStartPoint.x() + begin);
+ break;
+ }
+ }
+ if (protocolIndex == ProtocolSupported) {
+ int begin = urlText.indexOf("://");
+ if (begin != -1) {
+ return false;
+ }
+ if (urlText.indexOf('@') != -1) {
+ prefixToAdd = protocolPrefix[Mailto];
+ } else {
+ prefixToAdd = protocolPrefix[Http];
+ }
+ }
+
+ //no point or duplicated points
+ if (urlText.indexOf("..") != -1 || urlText.indexOf('.') == -1) {
+ return false;
+ }
+
+ //phase 3: (for check ip) if there is a ':' before ip, there must be a '@'
+ int host_begin = qMax(urlText.lastIndexOf('@') + 1, 0);
+ int host_end = urlText.indexOf(':', host_begin);
+ if (host_end == -1) {
+ host_end = urlText.indexOf('/', host_begin);
+ }
+ if (host_end == -1) {
+ host_end = urlText.length();
+ }
+ if (host_begin >= host_end) {
+ return false;
+ }
+ if (checkIP && urlText[host_end - 1] == '*') {
+ urlText[host_end - 1] = '1';
+ }
+
+ for (int index = 0; index < urlText.length() && urlText.at(index) != '/';
+ index++) {
+ QChar cur(urlText.at(index));
+ if (!cur.isLetterOrNumber() && !QString("-_~:@.").contains(cur)) {
+ return false;
+ }
+ }
+
+ QString newIP;
+ newIP = urlText.mid(host_begin, host_end - host_begin);
+ QStringList ipv4 = newIP.split(QLatin1String("."));
+ bool isIPv4 = true;
+ if (ipv4.count() != 4){
+ isIPv4 = false;
+ }
+ foreach(QString domain, ipv4) {
+ bool ok;
+ int num = domain.toInt(&ok);
+ //won't tolerant spaces.
+ if (!ok || num < 0 || num > 255 || domain.length() > 3) {
+ isIPv4 = false;
+ break;
+ }
+ }
+
+ if (!checkIP){
+ QString lastName = ipv4.back();
+
+ int lastCharIndex = lastName.lastIndexOf(QRegExp("[a-zA-Z]"));
+ if (lastCharIndex == -1) {
+ if (!isIPv4) {
+ return false;
+ } else if (urlText == newIP) {
+ return false;
+ }
+ } else {
+ const QString *begin = endOfUrl;
+ const QString *end = endOfUrl + sizeof(endOfUrl) / sizeof(QString *);
+ if (qFind(begin, end, lastName) == end) {
+ return false;
+ }
+ }
+ url_ = prefixToAdd + urlText;
+ } else {
+ if (!isIPv4) {
+ return false;
+ }
+ ip_ = newIP;
+ }
+
+ urlRect_ = urlRect;
+ rcUrl = urlRect;
+ urlStartPoint_ = urlStartPoint;
+ urlEndPoint_ = urlEndPoint;
+ return true;
+}
+
+QString FQTermSession::getUrl() {
+ return url_;
+}
+
+QString FQTermSession::getIP() {
+ return ip_;
+}
+
+bool FQTermSession::isPageComplete() {
+ return termBuffer_->getCaretRow() == (termBuffer_->getNumRows() - 1)
+ && termBuffer_->getCaretColumn() == (termBuffer_->getNumColumns() - 1);
+}
+
+bool FQTermSession::isAntiIdle() {
+ return param_.isAntiIdle_;
+}
+
+void FQTermSession::setAntiIdle(bool antiIdle) {
+ param_.isAntiIdle_ = antiIdle;
+
+ // disabled
+ if (!param_.isAntiIdle_ && idleTimer_->isActive()) {
+ idleTimer_->stop();
+ }
+
+ // enabled
+ if (param_.isAntiIdle_) {
+ if (idleTimer_->isActive()) {
+ idleTimer_->stop();
+ }
+ idleTimer_->start(param_.maxIdleSeconds_ *1000);
+ }
+}
+
+void FQTermSession::setAutoReconnect(bool autoReconnect) {
+ param_.isAutoReconnect_ = autoReconnect;
+ reconnectRetry_ = 0;
+ if (autoReconnect && !isConnected_) {
+ reconnectProcess();
+ }
+}
+
+
+void FQTermSession::autoReplyMessage() {
+ if (autoReplyTimer_->isActive()) {
+ autoReplyTimer_->stop();
+ }
+ if (pageState_ != Message) {
+ return;
+ }
+ if (scriptListener_)
+ {
+ bool res = scriptListener_->postScriptCallback(SFN_AUTO_REPLY
+#ifdef HAVE_PYTHON
+ ,Py_BuildValue("l", scriptListener_->windowID())
+#endif //HAVE_PYTHON
+ );
+ if (res) return;
+ }
+ QByteArray cstrTmp = param_.replyKeyCombination_.toLocal8Bit();
+ QByteArray cstr = parseString(cstrTmp.isEmpty() ? QByteArray("^Z"): cstrTmp);
+ //cstr += m_param.m_strAutoReply.toLocal8Bit();
+ cstr += unicode2bbs(param_.autoReplyMessage_);
+ cstr += '\n';
+ telnet_->write(cstr, cstr.length());
+
+ emit messageAutoReplied();
+}
+
+QByteArray FQTermSession::parseString(const QByteArray &cstr, int *len) {
+ QByteArray parsed = "";
+
+ if (len != 0) {
+ *len = 0;
+ }
+
+ for (uint i = 0; (long)i < cstr.length(); i++) {
+ if (cstr.at(i) == '^') {
+ i++;
+ if ((long)i < cstr.length()) {
+ parsed += FQTERM_CTRL(cstr.at(i));
+ if (len != 0) {
+ *len = *len + 1;
+ }
+ }
+ } else if (cstr.at(i) == '\\') {
+ i++;
+ if ((long)i < cstr.length()) {
+ if (cstr.at(i) == 'n') {
+ parsed += CHAR_CR;
+ } else if (cstr.at(i) == 'r') {
+ parsed += CHAR_LF;
+ } else if (cstr.at(i) == 't') {
+ parsed += CHAR_TAB;
+ }
+ if (len != 0) {
+ *len = *len + 1;
+ }
+ }
+ } else {
+ parsed += cstr.at(i);
+ if (len != 0) {
+ *len = *len + 1;
+ }
+ }
+ }
+
+ return parsed;
+}
+
+void FQTermSession::reconnect() {
+ if (!isConnected_) {
+ telnet_->connectHost(param_.hostAddress_, param_.port_);
+ reconnectRetry_++;
+ }
+}
+
+void FQTermSession::reconnectProcess() {
+ if (!isConnected_ && param_.isAutoReconnect_ && (reconnectRetry_ < param_.retryTimes_ || param_.retryTimes_ == -1)) {
+ int interval = param_.reconnectInterval_ <= 0 ? 0 : param_.reconnectInterval_ * 1000;
+ QTimer::singleShot(interval, this, SLOT(reconnect()));
+ }
+}
+
+void FQTermSession::setMouseMode(bool on) {
+ isMouseX11_ = on;
+}
+
+void FQTermSession::doAutoLogin() {
+ if (!param_.preLoginCommand_.isEmpty()) {
+ QByteArray temp = parseString(param_.preLoginCommand_.toLatin1());
+ telnet_->write((const char*)(temp), temp.length());
+ }
+ if (!param_.userName_.isEmpty()) {
+ QByteArray temp = param_.userName_.toLocal8Bit();
+ telnet_->write((const char*)(temp), temp.length());
+ char ch = CHAR_CR;
+ telnet_->write(&ch, 1);
+ }
+ if (!param_.password_.isEmpty()) {
+ QByteArray temp = param_.password_.toLocal8Bit();
+ telnet_->write((const char*)(temp), temp.length());
+ char ch = CHAR_CR;
+ telnet_->write(&ch, 1);
+ }
+
+ // smth ignore continous input, so sleep 1 sec :)
+#if defined(_OS_WIN32_) || defined(Q_OS_WIN32)
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+
+ if (!param_.postLoginCommand_.isEmpty()) {
+ QByteArray temp = parseString(param_.postLoginCommand_.toLatin1());
+ telnet_->write((const char*)(temp), temp.length());
+ }
+ isTelnetLogining_ = false;
+}
+
+//read slot
+void FQTermSession::readReady(int size, int raw_size) {
+ FQ_ASSERT(size > 0 || raw_size > 0); // maybe raw_size is greater than 0.
+
+ raw_data_.resize(raw_size);
+ telnet_->read_raw(&raw_data_[0], raw_size);
+
+ // read raw buffer
+ int zmodem_consumed;
+ if (param_.enableZmodem_)
+ zmodem_->ZmodemRcv((uchar*)&raw_data_[0], raw_data_.size(), &(zmodem_->info),
+ zmodem_consumed);
+
+ if (zmodem_->transferstate == notransfer) {
+ //decode
+ if (size > 0) {
+ int last_size = telnet_data_.size();
+ telnet_data_.resize(last_size + size);
+ telnet_->read(&telnet_data_[0] + last_size, size);
+ int processed = 0;
+ {
+ while (!bufferWriteLock_.tryLockForWrite()) {}
+ //QWriteLocker locker(&bufferWriteLock_);
+ processed = decoder_->decode(&telnet_data_[0], telnet_data_.size());
+ bufferWriteLock_.unlock();
+ }
+ telnet_data_.erase(telnet_data_.begin(), telnet_data_.begin() + processed);
+ }
+
+ if (isTelnetLogining_) {
+ int n = termBuffer_->getCaretRow();
+ for (int y = n - 5; y < n + 5; y++) {
+ y = qMax(0, y);
+ const FQTermTextLine *pTextLine = termBuffer_->getTextLineInTerm(y);
+ if (pTextLine == NULL) {
+ continue;
+ }
+
+ // QString str = pTextLine->getText();
+ QString str;
+ pTextLine->getAllPlainText(str);
+
+ if (str.indexOf("guest") != -1 /*&& str.indexOf("new") != -1*/) {
+ doAutoLogin();
+ break;
+ }
+ }
+ }
+ // page complete when caret at the right corner
+ // this works for most but not for all
+ const FQTermTextLine *pTextLine =
+ termBuffer_->getTextLineInTerm(termBuffer_->getNumRows() - 1);
+
+ //QString strText = stripWhitespace(pTextLine->getText());
+ // TODO_UTF16: fixme! performance issue!
+ QString strText;
+ pTextLine->getAllPlainText(strText);
+
+ // TODO_UTF16: shall we disable trim?
+ strText = strText.trimmed();
+
+ if (termBuffer_->getCaretRow() == termBuffer_->getNumRows() - 1
+ && termBuffer_->getCaretColumn() >= (strText.length() - 1) / 2) {
+ waitCondition_.wakeAll();
+ }
+
+ //QToolTip::remove(this, screen_->mapToRect(m_rcUrl));
+
+ // message received
+ // 03/19/2003. the caret posion removed as a message judgement
+ // because smth.org changed
+ if (decoder_->bellReceive()) {
+ emit startAlert();
+ emit bellReceived();
+
+ bool bellConsumed = false;
+ if (scriptListener_) {
+ bellConsumed = scriptListener_->postScriptCallback(SFN_ON_BELL
+#ifdef HAVE_PYTHON
+ ,Py_BuildValue("l", scriptListener_->windowID())
+#endif //HAVE_PYTHON
+ );
+ }
+
+ if (isAutoReply() && !bellConsumed) {
+ // TODO: save messages
+ if (isIdling_) {
+ autoReplyMessage();
+ } else {
+ autoReplyTimer_->start(param_.maxIdleSeconds_ *1000 / 2);
+ }
+ }
+ }
+
+ // set page state
+ detectPageState();
+ if (scriptListener_) {
+ scriptListener_->postScriptCallback(SFN_DATA_EVENT
+#ifdef HAVE_PYTHON
+ ,Py_BuildValue("l", scriptListener_->windowID())
+#endif //HAVE_PYTHON
+ );
+ }
+ emit sessionUpdated();
+ }
+
+ if (zmodem_->transferstate == transferstop) {
+ zmodem_->transferstate = notransfer;
+ }
+}
+
+/* 0-left 1-middle 2-right 3-relsase 4/5-wheel
+ *
+ */
+//void FQTermWindow::sendMouseState( int num,
+// Qt::ButtonState btnstate, Qt::ButtonState keystate, const QPoint& pt )
+void FQTermSession::sendMouseState(int num, Qt::KeyboardModifier btnstate,
+ Qt::KeyboardModifier keystate, const QPoint &pt) {
+ /*
+ if(!m_bMouseX11)
+ return;
+
+ QPoint ptc = screen_->mapToChar(pt);
+
+ if(btnstate&Qt::LeftButton)
+ num = num==0?0:num;
+ else if(btnstate&Qt::MidButton)
+ num = num==0?1:num;
+ else if(btnstate&Qt::RightButton)
+ num = num==0?2:num;
+
+ int state = 0;
+ if(keystate&Qt::ShiftModifier)
+ state |= 0x04;
+ if(keystate&Qt::MetaModifier)
+ state |= 0x08;
+ if(keystate&Qt::ControlModifier)
+ state |= 0x10;
+
+ // normal buttons are passed as 0x20 + button,
+ // mouse wheel (buttons 4,5) as 0x5c + button
+ if(num>3) num += 0x3c;
+
+ char mouse[10];
+ sprintf(mouse, "\033[M%c%c%c",
+ num+state+0x20,
+ ptc.x()+1+0x20,
+ ptc.y()+1+0x20);
+ telnet_->write( mouse, strlen(mouse) );
+ */
+}
+
+void FQTermSession::cancelZmodem() {
+ zmodem_->zmodemCancel();
+}
+
+void FQTermSession::disconnect() {
+ telnet_->close();
+ finalizeConnection();
+}
+
+void FQTermSession::finalizeConnection() {
+ if (isConnected_) {
+ QString strMsg = "";
+ strMsg += "\n\n\n\r\n\r";
+ strMsg += "\x1b[17C\x1b[0m===========================================\n\r";
+ strMsg +=
+ "\x1b[17C Connection Closed, Press \x1b[1m\x1b[31;40mEnter\x1b[m\x1b[0m To Connect\n\r";
+ strMsg += "\x1b[17C===========================================\n";
+ decoder_->decode(strMsg.toLatin1(), strMsg.length());
+ }
+ isConnected_ = false;
+
+ if (autoReplyTimer_->isActive()) {
+ autoReplyTimer_->stop();
+ }
+
+ if (idleTimer_->isActive()) {
+ idleTimer_->stop();
+ }
+
+ emit connectionClosed();
+}
+
+// telnet state slot
+void FQTermSession::changeTelnetState(int state) {
+ switch (state) {
+ case TSRESOLVING:
+ break;
+ case TSHOSTFOUND:
+ break;
+ case TSHOSTNOTFOUND:
+ finalizeConnection();
+ break;
+ case TSCONNECTING:
+ break;
+ case TSHOSTCONNECTED:
+ isConnected_ = true;
+ reconnectRetry_ = 0;
+ if (param_.isAutoLogin_ && param_.protocolType_==0) {
+ isTelnetLogining_ = true;
+ }
+
+ break;
+ case TSPROXYCONNECTED:
+ break;
+ case TSPROXYAUTH:
+ break;
+ case TSPROXYFAIL:
+ disconnect();
+ break;
+ case TSREFUSED:
+ finalizeConnection();
+ break;
+ case TSREADERROR:
+ disconnect();
+ break;
+ case TSCLOSED:
+ finalizeConnection();
+ if (param_.isAutoReconnect_) {
+ reconnectProcess();
+ }
+ break;
+ case TSCLOSEFINISH:
+ finalizeConnection();
+ break;
+ case TSCONNECTVIAPROXY:
+ break;
+ case TSEGETHOSTBYNAME:
+ finalizeConnection();
+ break;
+ case TSEINIWINSOCK:
+ finalizeConnection();
+ break;
+ case TSERROR:
+ disconnect();
+ break;
+ case TSPROXYERROR:
+ disconnect();
+ break;
+ case TSWRITED:
+ // restart the idle timer
+ if (idleTimer_->isActive()) {
+ idleTimer_->stop();
+ }
+ if (param_.isAntiIdle_) {
+ idleTimer_->start(param_.maxIdleSeconds_ *1000);
+ }
+ isIdling_ = false;
+ break;
+ default:
+ break;
+ }
+
+ emit telnetStateChanged(state);
+}
+
+void FQTermSession::handleInput(const QString &text) {
+ if (text.length() > 0) {
+ QByteArray cstrTmp = unicode2bbs_smart(text);
+ telnet_->write(cstrTmp, cstrTmp.length());
+ }
+}
+
+QString FQTermSession::bbs2unicode(const QByteArray &text)
+{
+ return encoding2unicode(text, param_.serverEncodingID_);
+}
+
+QByteArray FQTermSession::unicode2bbs(const QString &text) {
+ return unicode2encoding(text, param_.serverEncodingID_);
+}
+
+
+QByteArray FQTermSession::unicode2bbs_smart(const QString &text) {
+ QByteArray strTmp;
+
+ switch(param_.serverEncodingID_)
+ {
+ case FQTERM_ENCODING_GBK:
+ if (FQTermPref::getInstance()->imeEncodingID_ == FQTERM_ENCODING_BIG5)
+ {
+ strTmp = U2B(text);
+ char* tmp = encodingConverter_.B2G(strTmp, strTmp.length());
+ strTmp = tmp;
+ delete []tmp;
+ }
+ else
+ {
+ strTmp = U2G(text);
+ }
+ break;
+ case FQTERM_ENCODING_BIG5:
+ if (FQTermPref::getInstance()->imeEncodingID_ == FQTERM_ENCODING_GBK)
+ {
+ strTmp = U2G(text);
+ char* tmp = encodingConverter_.G2B(strTmp, strTmp.length());
+ strTmp = tmp;
+ delete []tmp;
+ }
+ else
+ {
+ strTmp = U2B(text);
+ }
+ break;
+ case FQTERM_ENCODING_HKSCS:
+ if (FQTermPref::getInstance()->imeEncodingID_ == FQTERM_ENCODING_GBK)
+ {
+ strTmp = U2G(text);
+ char* tmp = encodingConverter_.G2B(strTmp, strTmp.length());
+ strTmp = tmp;
+ delete []tmp;
+ }
+ else
+ {
+ strTmp = U2H(text);
+ }
+ break;
+ case FQTERM_ENCODING_UTF8:
+ strTmp = U2U8(text);
+ break;
+ case FQTERM_ENCODING_UAO:
+ if (FQTermPref::getInstance()->imeEncodingID_ == FQTERM_ENCODING_GBK)
+ {
+ strTmp = U2G(text);
+ char* tmp = encodingConverter_.G2B(strTmp, strTmp.length());
+ strTmp = tmp;
+ delete []tmp;
+ }
+ else
+ {
+ strTmp = U2A(text);
+ }
+ break;
+ }
+
+ return strTmp;
+}
+
+
+void FQTermSession::onIdle() {
+ // do as autoreply when it is enabled
+ if (autoReplyTimer_->isActive() && param_.isAutoReply_) {
+ autoReplyMessage();
+ stopAlert();
+ return ;
+ }
+
+ isIdling_ = true;
+ // system script can handle that
+ if (scriptListener_) {
+ bool res = scriptListener_->postScriptCallback(SFN_ANTI_IDLE
+#ifdef HAVE_PYTHON
+ ,Py_BuildValue("l", scriptListener_->windowID())
+#endif //HAVE_PYTHON
+ );
+ if (res)
+ return;
+ }
+ // the default function
+ int length;
+ QByteArray cstr = parseString(param_.antiIdleMessage_.toLocal8Bit(), &length);
+ telnet_->write(cstr, length);
+}
+
+void FQTermSession::onAutoReply() {
+ // if AutoReply still enabled, then autoreply
+ if (param_.isAutoReply_) {
+ autoReplyMessage();
+ } else {
+ // else just stop the timer
+ autoReplyTimer_->stop();
+ }
+
+ stopAlert();
+}
+bool FQTermSession::isAutoReply() {
+ return param_.isAutoReply_;
+}
+
+void FQTermSession::setAutoReply(bool on) {
+ param_.isAutoReply_ = on;
+ if (!param_.isAutoReply_ && autoReplyTimer_->isActive()) {
+ autoReplyTimer_->stop();
+ }
+}
+
+int FQTermSession::write(const char *data, int len) {
+ return telnet_->write(data, len);
+}
+
+int FQTermSession::writeStr(const char *str) {
+ return write(str, strlen(str));
+}
+
+void FQTermSession::setProxy(int type, bool needAuth,
+ const QString &hostName, quint16 portNumber,
+ const QString &userName, const QString &password) {
+ telnet_->setProxy(type, needAuth, hostName, portNumber, userName, password);
+}
+
+void FQTermSession::connectHost(const QString &hostName, quint16 portNumber) {
+ telnet_->connectHost(hostName, portNumber);
+}
+
+void FQTermSession::close() {
+ telnet_->close();
+ finalizeConnection();
+}
+
+void FQTermSession::clearLineChanged(int index) {
+ termBuffer_->clearLineChanged(index);
+}
+
+void FQTermSession::setLineAllChanged(int index) {
+ termBuffer_->setLineAllChanged(index);
+}
+
+void FQTermSession::setTermSize(int col, int row) {
+ termBuffer_->setTermSize(col, row);
+ param_.numColumns_ = col;
+ param_.numRows_ = row;
+}
+
+FQTermBuffer *FQTermSession::getBuffer() const {
+ return termBuffer_;
+}
+
+void FQTermSession::setSelect(const QPoint &pt1, const QPoint &pt2) {
+ termBuffer_->setSelect(pt1, pt2);
+}
+
+void FQTermSession::clearSelect() {
+ termBuffer_->clearSelect();
+}
+
+void FQTermSession::leaveIdle() {
+ if (autoReplyTimer_->isActive()) {
+ autoReplyTimer_->stop();
+ }
+
+ if (idleTimer_->isActive()) {
+ idleTimer_->stop();
+ }
+
+ if (param_.isAntiIdle_) {
+ idleTimer_->start(param_.maxIdleSeconds_ * 1000);
+ }
+}
+
+void FQTermSession::copyArticle() {
+ if (!acThread_->isRunning()) {
+ acThread_->start();
+ }
+}
+
+void FQTermSession::getLineColorInfo(const FQTermTextLine * line,
+ LineColorInfo * colorInfo)
+{
+ colorInfo->backgroundColorIndex.clear();
+ colorInfo->foregroundColorIndex.clear();
+ colorInfo->hasBackgroundColor = false;
+ colorInfo->uniBackgroundColor = true;
+ colorInfo->uniForegroundColor = true;
+ colorInfo->hasForegroundColor = false;
+
+ if (!line || line->getWidth() == 0) {
+ return;
+ }
+
+ const unsigned char *color = line->getColors();
+ unsigned char background = GETBG(color[0]);
+ unsigned char foreground = GETFG(color[0]);
+ colorInfo->backgroundColorIndex.append(background);
+ colorInfo->foregroundColorIndex.append(foreground);
+ //for (int i = 0; i < color.length() / 2; i++) {
+ // TODO_UTF16: why use color.length()/2 formerly?
+ for (int i = 0; i < (int)line->getWidth(); i++) {
+ if (GETBG(color[i]) != background) {
+ colorInfo->uniBackgroundColor = false;
+ background = GETBG(color[i]);
+ colorInfo->backgroundColorIndex.append(background);
+ }
+ if (GETBG(color[i]) != GETBG(NO_COLOR)) {
+ colorInfo->hasBackgroundColor = true;
+ }
+ if (GETFG(color[i]) != foreground) {
+ colorInfo->uniForegroundColor = false;
+ foreground = GETFG(color[i]);
+ colorInfo->foregroundColorIndex.append(foreground);
+ }
+ if (GETFG(color[i]) != GETFG(NO_COLOR)) {
+ colorInfo->hasForegroundColor = true;
+ }
+ }
+}
+
+bool FQTermSession::readyForInput()
+{
+ return telnet_->readyForInput();
+}
+
+void FQTermSession::onEnqReceived() {
+ if (FQTermPref::getInstance()->replyENQ_)
+ telnet_->write(ANSWERBACK_MESSAGE, sizeof(ANSWERBACK_MESSAGE));
+}
+
+void FQTermSession::onSSHAuthOK() {
+ isSSHLogining_ = false;
+ //setTermSize(80, 24);
+ //setTermSize(param_.numColumns_, param_.numRows_);
+ telnet_->windowSizeChanged(param_.numColumns_, param_.numRows_);
+}
+
+void FQTermSession::updateSetting( const FQTermParam& p ) {
+ bool toggleAutoReconnect = (p.isAutoReconnect_ != param_.isAutoReconnect_);
+ param_ = p;
+ setTermSize(p.numColumns_, p.numRows_);
+ setAntiIdle(isAntiIdle());
+ if (toggleAutoReconnect)
+ setAutoReconnect(param_.isAutoReconnect_);
+}
+
+ArticleCopyThread::ArticleCopyThread(
+ FQTermSession &bbs, QWaitCondition &waitCondition, QReadWriteLock &bufferLock)
+ : session_(bbs),
+ waitCondition_(waitCondition),
+ lock_(bufferLock) {
+ FQ_VERIFY(connect(this, SIGNAL(writeSession(const QString&)),
+ &session_, SLOT(handleInput(const QString&)),
+ Qt::QueuedConnection));
+}
+
+ArticleCopyThread::~ArticleCopyThread() {
+}
+
+static void removeTrailSpace(QString &line) {
+ for (int last_non_space = line.size() - 1;
+ last_non_space >= 0; --last_non_space) {
+ QChar a = line.at(last_non_space);
+ if (!a.isSpace()) {
+ line.resize(last_non_space + 1);
+ break;
+ }
+
+ if (last_non_space == 0) {
+ line.resize(0);
+ }
+ }
+}
+
+
+///////////////////////////////////////
+//analyze last line when copy article
+static std::pair<int, int> ParseLastLine(QString str) {
+ int startLine = -1;
+ int endLine = -1;
+ int infoStart = str.indexOf('(', str.indexOf('(') + 1);
+ int infoEnd = str.indexOf(')', infoStart);
+ int infoSplit = str.indexOf('-', infoStart);
+ if (infoStart == -1 || infoEnd == -1 || infoSplit == -1) {
+ return std::make_pair(-1, -1);
+ }
+ bool ok = true;
+ startLine = str.mid(infoStart + 1, infoSplit - infoStart - 1).toInt(&ok);
+ if (!ok) {
+ return std::make_pair(-1, -1);
+ }
+ endLine = str.mid(infoSplit + 1, infoEnd - infoSplit - 1).toInt(&ok);
+ if (!ok) {
+ return std::make_pair(-1, -1);
+ }
+ return std::make_pair(startLine, endLine);
+}
+///////////////////////////////////////
+
+
+void ArticleCopyThread::run() {
+ QReadLocker locker(&lock_);
+
+ typedef std::vector<QString> Page;
+ const FQTermBuffer *buffer = session_.getBuffer();;
+ Page page;
+
+ QString firstPageLastLine;
+ buffer->getTextLineInTerm(buffer->getNumRows() - 1)->getAllPlainText(firstPageLastLine);
+ bool MultiPage = (firstPageLastLine.indexOf("%") != -1);
+ if (!MultiPage) {
+ emit writeSession("q");
+ emit writeSession("\n");
+ if (!waitCondition_.wait(&lock_, 10000)) {
+ emit articleCopied(DAE_TIMEOUT, "");
+ return;
+ }
+ buffer->getTextLineInTerm(buffer->getNumRows() - 1)->getAllPlainText(firstPageLastLine);
+ MultiPage = (firstPageLastLine.indexOf("%") != -1);
+ }
+
+ if (MultiPage) {
+ //session_.write("e", 1);
+ emit writeSession("e");
+ if (!waitCondition_.wait(&lock_, 10000)) {
+ emit articleCopied(DAE_TIMEOUT, "");
+ return;
+ }
+ QString LastPageLastLine;
+ buffer->getTextLineInTerm(buffer->getNumRows() - 1)->getAllPlainText(LastPageLastLine);
+ std::pair<int, int> range = ParseLastLine(LastPageLastLine);
+ if (range.first == -1 || range.second == -1) {
+ return;
+ }
+ page.resize(range.second);
+ for (int i = range.first; i <= range.second; ++i) {
+ QString strTemp;
+ buffer->getTextLineInTerm(i - range.first)->getAllPlainText(strTemp);
+ page[i - 1] = strTemp;
+ }
+ //session_.write("s", 1);
+ emit writeSession("s");
+ if (!waitCondition_.wait(&lock_, 10000)) {
+ emit articleCopied(DAE_TIMEOUT, "");
+ return;
+ }
+ }
+ else {
+ for (int i = 0; i < buffer->getNumRows() - 1; ++i) {
+ QString strTemp;
+ buffer->getTextLineInTerm(i)->getAllPlainText(strTemp);
+ page.push_back(strTemp);
+ }
+ }
+
+ while (MultiPage) {
+ // the end of article
+ int lineIndex = buffer->getNumRows() - 1;
+ //QByteArray lastLine = buffer->getLineInScreen(lineIndex)->getText();
+ QString lastLine;
+ buffer->getTextLineInTerm(lineIndex)->getAllPlainText(lastLine);
+
+ std::pair<int, int> range = ParseLastLine(lastLine);
+ if (range.first == -1 || range.second == -1) {
+ page.push_back(lastLine);
+ break;
+ }
+
+ // copy a page of lines exclude the last line.
+ for (int i = range.first; i <= range.second; ++i) {
+ QString strTemp;
+ buffer->getTextLineInTerm(i - range.first)->getAllPlainText(strTemp);
+ page[i - 1] = strTemp;
+ }
+
+
+ // wait for next page.
+ //session_.write(" ", 1);
+ emit writeSession(" ");
+ if (!waitCondition_.wait(&lock_, 10000)) {
+ emit articleCopied(DAE_TIMEOUT, "");
+ break;
+ }
+ }
+
+
+
+ QString articleText;
+
+ for (Page::iterator line = page.begin(); line != page.end(); ++line) {
+ removeTrailSpace(*line);
+ articleText += (*line);
+ articleText += (OS_NEW_LINE);
+ }
+
+ //qApp->postEvent(pWin, new QCustomEvent(DAE_FINISH));
+ emit articleCopied(DAE_FINISH, articleText);
+}
+
+} // namespace FQTerm
+
+#include "fqterm_session.moc"
diff --git a/src/terminal/fqterm_session.h b/src/terminal/fqterm_session.h
new file mode 100644
index 0000000..78aaa31
--- /dev/null
+++ b/src/terminal/fqterm_session.h
@@ -0,0 +1,344 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_SESSION_H
+#define FQTERM_SESSION_H
+
+#include <vector>
+
+#include <QPoint>
+#include <QRect>
+#include <QObject>
+#include <QThread>
+#include <QString>
+#include <QWaitCondition>
+#include <QList>
+#include <QtScript/QScriptValue>
+#include <QReadWriteLock>
+#include "fqterm_param.h"
+#include "fqterm_config.h"
+#include "fqterm_convert.h"
+#ifdef HAVE_PYTHON
+#include <Python.h>
+#endif //HAVE_PYTHON
+
+class QRect;
+class QTimer;
+
+namespace FQTerm {
+
+class FQTermScriptEventListener {
+public:
+ bool postScriptCallback(const QString& func,
+#ifdef HAVE_PYTHON
+ PyObject* pArgs,
+#endif //HAVE_PYTHON
+ const QScriptValueList & args = QScriptValueList())
+ {
+ bool res = postQtScriptCallback(func, args);
+#ifdef HAVE_PYTHON
+ res = postPythonCallback(func, pArgs) || res;
+#endif //HAVE_PYTHON
+ return res;
+ }
+ virtual long windowID() = 0;
+ virtual ~FQTermScriptEventListener() {}
+private:
+ virtual bool postQtScriptCallback(const QString& func, const QScriptValueList & args = QScriptValueList()) = 0;
+#ifdef HAVE_PYTHON
+ virtual bool postPythonCallback(const QString& func, PyObject* pArgs) = 0;
+#endif //HAVE_PYTHON
+};
+
+struct LineColorInfo {
+ bool hasBackgroundColor;
+ bool hasForegroundColor;
+ bool uniBackgroundColor;
+ bool uniForegroundColor;
+ QList<unsigned char> backgroundColorIndex;
+ QList<unsigned char> foregroundColorIndex;
+};
+
+class FQTermConfig;
+class FQTermBuffer;
+class FQTermTextLine;
+class FQTermTelnet;
+class FQTermDecode;
+class FQTermZmodem;
+class ArticleCopyThread;
+
+class FQTermSession: public QObject {
+ Q_OBJECT;
+ public:
+ enum CursorType {
+ kHome = 0,
+ kEnd = 1,
+ kPageUp = 2,
+ kPageDown = 3,
+ kUp = 4,
+ kDown = 5,
+ kLeft = 6,
+ kRight = 7,
+ kNormal = 8
+ };
+
+ FQTermSession(FQTermConfig *, FQTermParam);
+ ~FQTermSession();
+
+ enum PageState {
+ Undefined = -1,
+ Menu = 0,
+ MailMenu = 1,
+ ArticleList = 2,
+ EliteArticleList = 3,
+ BoardList = 4,
+ FriendMailList = 5,
+ Message = 6,
+ Read = 7,
+ Edit = 8,
+ TOP10 = 9,
+ };
+
+ enum ProtocolIndex {
+ Http = 0,
+ Https = 1,
+ Mms = 2,
+ Rstp = 3,
+ Ftp = 4,
+ Mailto = 5,
+ Telnet = 6,
+ ProtocolSupported = 7
+ };
+
+ static const QString endOfUrl[];
+
+ static const QString protocolPrefix[];
+ PageState getPageState();
+
+ // Set current cursor postion to pt,
+ // return whether the selection rectangle is changed.
+ // the output parameter rc be a rectangle including both
+ // the new and the old selection region.
+ bool setCursorPos(const QPoint &pt, QRect &rc);
+ CursorType getCursorType(const QPoint &);
+
+ // Get the menu char detected in getSelectRect().
+ char getMenuChar();
+
+ // Get current selection rectangle.
+ // also detect the menu char if in kMenu page state.
+ QRect detectMenuRect();
+ QRect getMenuRect() {return menuRect_;}
+ void setMenuRect(int row, int col, int len);
+ // detect whether the given line or point is contained by current selection rectangle.
+ bool isSelectedMenu(int line);
+ bool isSelectedMenu(const QPoint &);
+ //selection.
+ void setSelect(const QPoint &pt1, const QPoint &pt2);
+ void clearSelect();
+
+ bool isUrl(QRect &, QRect &);
+ bool isIP(QRect &, QRect &);
+ QString getUrl();
+ QString getIP();
+
+
+
+ // Set current screen start line to help detect cursor type
+ // and select rectangle corresponding to current cursor postion.
+ void setScreenStart(int);
+
+
+
+ // Set a line of buffer to have been changed from start to end.
+ void clearLineChanged(int index);
+ void setLineAllChanged(int index);
+
+ bool isAntiIdle();
+ void setAntiIdle(bool antiIdle);
+ void leaveIdle();
+
+ bool isAutoReply();
+ void setAutoReply(bool autoReply);
+ QString getMessage();
+
+ void setAutoReconnect(bool autoReconnect);
+
+
+ bool readyForInput();
+ void setTermSize(int col, int row);
+
+ QWaitCondition& getWaitCondition() {return waitCondition_;};
+ //this function will do
+ //1. convert unicode to bbs encoding
+ //2. if there are some chars express same meaning in simplify/traditional
+ //Chinese, auto covert them by considering ime/bbs encoding.
+ QByteArray unicode2bbs_smart(const QString &);
+ QString bbs2unicode(const QByteArray &text);
+ QByteArray unicode2bbs(const QString &text);
+
+ // Write data raw data
+ int write(const char *data, int len);
+ int writeStr(const char *str);
+
+ // type: 0-no proxy; 1-wingate; 2-sock4; 3-socks5
+ // needAuth: if authentation needed
+ void setProxy(int type, bool needAuth,
+ const QString &hostname, quint16 portNumber,
+ const QString &username, const QString &password);
+ // User close the connection
+ void close();
+
+ bool isConnected() {return isConnected_;}
+ void setSendingMessage(bool sending = true) {isSendingMessage_ = sending;}
+ const QPoint& urlStartPoint() {return urlStartPoint_;}
+ const QPoint& urlEndPoint() {return urlEndPoint_;}
+
+ FQTermParam& param() {return param_;}
+ void updateSetting(const FQTermParam& p);
+ QReadWriteLock& getBufferLock() {return bufferWriteLock_;}
+
+ public:
+
+
+ public slots:
+ FQTermBuffer *getBuffer() const;
+ void connectHost(const QString &hostname, quint16 portnumber);
+ void reconnect();
+ void disconnect();
+
+ void cancelZmodem();
+ void changeTelnetState(int state);
+
+ void handleInput(const QString &text);
+
+ void copyArticle();
+
+ signals:
+ void messageAutoReplied();
+ void articleCopied(int state, const QString content);
+ void sessionUpdated();
+ void connectionClosed();
+ void bellReceived();
+ void onTitleSet(const QString&);
+ void startAlert();
+ void stopAlert();
+
+ void requestUserPwd(QString *user, QString *pwd, bool *isOK);
+
+ void telnetStateChanged(int state);
+ void zmodemStateChanged(int type, int value, const char *status);
+
+ void errorMessage(QString);
+
+private slots:
+ void readReady(int size, int raw_size);
+ void onIdle();
+ void onAutoReply();
+ void onEnqReceived();
+ void onSSHAuthOK();
+ void setMouseMode(bool on);
+
+private:
+ void sendMouseState(int, Qt::KeyboardModifier, Qt::KeyboardModifier, const QPoint &);
+ void getLineColorInfo(const FQTermTextLine * line, LineColorInfo * colorInfo);
+ bool isIllChar(char);
+ void detectPageState();
+ QString expandUrl(const QPoint& pt, QPair<int, int>& range);
+ bool checkUrl(QRect &, QRect &, bool);
+ QByteArray parseString(const QByteArray &cstr, int *len = 0);
+ void finalizeConnection();
+ bool isPageComplete();
+ void autoReplyMessage();
+ void reconnectProcess();
+ void doAutoLogin();
+
+private:
+ //this read-write lock will be locked as a writer's lock if the buffer is being changed
+ //so for a reader who wants to ensure thread-safe, he should lock
+ //this lock as a reader's lock.
+ mutable QReadWriteLock bufferWriteLock_;
+
+ FQTermParam param_;
+ FQTermConvert encodingConverter_;
+ bool isTelnetLogining_;
+ bool isSSHLogining_;
+
+ bool isIdling_;
+ bool isMouseX11_;
+ bool isConnected_;
+ bool isSendingMessage_;
+
+ QRect urlRect_;
+ QPoint urlStartPoint_;
+ QPoint urlEndPoint_;
+ QString url_;
+ QString ip_;
+ char menuChar_;
+ QRect menuRect_;
+ PageState pageState_;
+ QPoint cursorPoint_;
+ int screenStartLineNumber_;
+
+ FQTermZmodem *zmodem_;
+ FQTermDecode *decoder_;
+ FQTermTelnet *telnet_;
+ FQTermBuffer *termBuffer_;
+
+ QTimer *idleTimer_;
+ QTimer *autoReplyTimer_;
+
+ QWaitCondition waitCondition_;
+ ArticleCopyThread *acThread_;
+
+ std::vector<char> telnet_data_;
+ std::vector<char> raw_data_;
+
+ int reconnectRetry_;
+public:
+ void setScriptListener(FQTermScriptEventListener* pythonListener) {
+ scriptListener_ = pythonListener;
+ }
+private:
+ FQTermScriptEventListener* scriptListener_;
+};
+
+class ArticleCopyThread: public QThread {
+ Q_OBJECT;
+ public:
+ ArticleCopyThread(FQTermSession &bbs, QWaitCondition &waitCondition, QReadWriteLock &bufferLock);
+
+ ~ArticleCopyThread();
+
+ signals:
+ void articleCopied(int state, const QString content);
+ void writeSession(const QString&);
+ protected:
+ virtual void run();
+ private:
+ FQTermSession &session_;
+ QWaitCondition &waitCondition_;
+
+ QReadWriteLock &lock_;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_SESSION_H
diff --git a/src/terminal/fqterm_text_line.cpp b/src/terminal/fqterm_text_line.cpp
new file mode 100644
index 0000000..ed6d164
--- /dev/null
+++ b/src/terminal/fqterm_text_line.cpp
@@ -0,0 +1,541 @@
+/***************************************************************************
+ * 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 <QRegExp>
+#include <QString>
+
+#include "fqterm.h"
+#include "fqterm_text_line.h"
+
+namespace FQTerm {
+
+#ifndef NDEBUG
+//#define TEXT_LINE_CHECK
+#else
+//#define TEXT_LINE_CHECK
+#endif
+
+#ifndef TEXT_LINE_CHECK
+void FQTermTextLine::verifyTextLine() const {}
+void FQTermTextLine::verifyCellRange(unsigned, unsigned) const {}
+#else
+void FQTermTextLine::verifyTextLine() const {
+ unsigned last_cell = 0;
+ unsigned cur_cell = last_cell;
+
+ if (cells_.size() == 0
+ || cell_colors_.size() == 0
+ || cell_attrs_.size() == 0
+ || chars_.size() == 0
+ ) {
+ FQ_VERIFY(cells_.size() == 0);
+ FQ_VERIFY(cell_colors_.size() == 0);
+ FQ_VERIFY(cell_attrs_.size() == 0);
+ FQ_VERIFY(chars_.size() == 0);
+ return;
+ }
+
+ FQ_VERIFY(max_cell_count_ < 65536);
+ FQ_VERIFY(chars_.size() < 65536);
+ FQ_VERIFY(cells_.size() <= max_cell_count_);
+ FQ_VERIFY(cells_.size() == cell_colors_.size());
+ FQ_VERIFY(cells_.size() == cell_attrs_.size());
+
+ const UTF16 *start = &chars_[0];
+ const UTF16 *end = start + chars_.size();
+
+ for (const UTF16 *p = start; p < end;) {
+ int w = mk_advace_at_least_one_cell(&p, end);
+
+ FQ_VERIFY(w >= 0); // should be valid unicode string.
+
+ cur_cell += w;
+
+ for (unsigned i = last_cell; i < cur_cell; ++i) {
+ unsigned char_end = p - start;
+ FQ_VERIFY(cells_[i] == char_end);
+ }
+
+ last_cell = cur_cell;
+ }
+
+ FQ_VERIFY(cur_cell == getWidth());
+ FQ_VERIFY(cells_.back() == chars_.size());
+}
+
+void FQTermTextLine::verifyCellRange(unsigned cell_begin, unsigned cell_end) const {
+ FQ_VERIFY(cell_begin <= cell_end);
+ FQ_VERIFY(cell_end <= getWidth());
+
+ unsigned real_cell_begin = getCellBegin(cell_begin);
+ FQ_VERIFY(real_cell_begin == cell_begin);
+
+ unsigned real_cell_end = getCellBegin(cell_end);
+ FQ_VERIFY(real_cell_end == cell_end);
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ FQ_VERIFY(char_begin <= chars_.size());
+
+ unsigned char_end = getCharEnd(cell_end);
+ FQ_VERIFY(char_begin <= char_end);
+ FQ_VERIFY(char_end <= chars_.size());
+
+}
+
+#endif
+
+FQTermTextLine::FQTermTextLine(unsigned max_cell_count)
+ : max_cell_count_(max_cell_count),
+ dirty_cell_begin_(0),
+ dirty_cell_end_(max_cell_count),
+ stored_color_(0),
+ stored_attr_(0){
+ cells_.reserve(max_cell_count_);
+ cell_colors_.reserve(max_cell_count_);
+ cell_attrs_.reserve(max_cell_count_);
+ chars_.reserve(max_cell_count_);
+}
+
+FQTermTextLine::~FQTermTextLine() {
+}
+
+void FQTermTextLine::setMaxCellCount(unsigned max_cell_count) {
+ // FIXME: how about dirty flag?
+ max_cell_count_ = max_cell_count;
+
+ if (cells_.size() > max_cell_count_) {
+ // TODO_UTF16: what if an incomplete utf-16 byte left.
+ cells_.resize(max_cell_count_);
+ cell_colors_.resize(max_cell_count_);
+ cell_attrs_.resize(max_cell_count_);
+ chars_.resize(cells_.back());
+ }
+}
+
+// Whether this line of text contains blink characters.
+bool FQTermTextLine::hasBlink() const {
+ bool blink = false;
+
+ char tempea;
+ for (unsigned i = 0; i < cells_.size(); i++) {
+ tempea = cell_attrs_[i];
+ if (GETBLINK(tempea)) {
+ blink = true;
+ break;
+ }
+ }
+
+ return blink;
+}
+
+void FQTermTextLine::setDirtyFlag(unsigned cell_begin, unsigned cell_end) {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin <= cell_end);
+ FQ_VERIFY(cell_end <= max_cell_count_);
+#endif
+ if (dirty_cell_begin_ > cell_begin) {
+ dirty_cell_begin_ = cell_begin;
+ }
+
+ if (dirty_cell_end_ < cell_end) {
+ dirty_cell_end_ = cell_end;
+ }
+}
+
+void FQTermTextLine::safelySetDirtyFlag(unsigned cell_begin, unsigned cell_end) {
+ if (cell_end > max_cell_count_) cell_end = max_cell_count_;
+
+ if (cell_begin >= cell_end)
+ return;
+
+ setDirtyFlag(cell_begin, cell_end);
+}
+
+void FQTermTextLine::clearDirtyFlag() {
+ dirty_cell_begin_ = 0;
+ dirty_cell_end_ = 0;
+}
+
+bool FQTermTextLine::getDirtyCells(unsigned &cell_begin, unsigned &cell_end) const {
+ cell_begin = dirty_cell_begin_;
+ cell_end = dirty_cell_end_;
+ return dirty_cell_begin_ < dirty_cell_end_;
+}
+
+void FQTermTextLine::getPlainText(unsigned cell_begin, unsigned cell_end,
+ QString &result) const {
+ verifyCellRange(cell_begin, cell_end);
+
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ unsigned char_end = getCharEnd(cell_end);
+
+ result.insert(result.size(),
+ (QChar *) &chars_[char_begin],
+ char_end - char_begin);
+
+ typedef char Verify_QChar_Is_Two_Bytes[
+ (sizeof(QChar) == 2)? 1 : -1];
+}
+
+void FQTermTextLine::getAnsiText(unsigned cell_begin, unsigned cell_end,
+ QString &result,
+ const char *escape) const {
+ verifyCellRange(cell_begin, cell_end);
+
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ unsigned cur_cell = cell_begin;
+ unsigned next_cell = cell_begin;
+
+ while (cur_cell < cell_end) {
+ unsigned char color = cell_colors_[cur_cell];
+ unsigned char attr = cell_attrs_[cur_cell];
+
+ for (next_cell = cur_cell;
+ next_cell < cell_end;
+ ++next_cell) {
+ if (!(cell_colors_[next_cell] == color
+ && cell_attrs_[next_cell] == attr)) {
+ if (next_cell > getCellBegin(next_cell)) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ }
+
+ getAnsiCtrlSeq(color, attr, escape, result);
+ getPlainText(cur_cell, next_cell, result);
+ result += escape;
+ result += "0m";
+
+ cur_cell = next_cell;
+ }
+}
+
+
+void FQTermTextLine::appendWhiteSpace(
+ unsigned count, unsigned char color, unsigned char attr, UTF16 space) {
+ FQ_ASSERT(get_str_width(space) == 1);
+
+ cells_.reserve(getWidth() + count);
+ max_cell_count_ = qMax(max_cell_count_, getWidth() + count);
+
+ cell_colors_.reserve(getWidth() + count);
+ cell_attrs_.reserve(getWidth() + count);
+
+ setDirtyFlag(getWidth(), getWidth() + count);
+
+ chars_.insert(chars_.end(), count, space);
+ cell_colors_.insert(cell_colors_.end(), count, color);
+ cell_attrs_.insert(cell_attrs_.end(), count, attr);
+
+ int c = getCharEnd(getWidth());
+ for (unsigned i = 0; i < count; ++i) {
+ cells_.push_back(c + i + 1);
+ }
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::insertWhiteSpace(unsigned count, unsigned cell_begin,
+ unsigned char color, unsigned char attr,
+ UTF16 space) {
+ FQ_ASSERT(get_str_width(space) == 1);
+
+ breakCell(cell_begin);
+
+ verifyCellRange(cell_begin, getWidth());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+
+ max_cell_count_ = qMax(max_cell_count_, getWidth() + count);
+
+ chars_.insert(chars_.begin() + char_begin, count, space);
+ cell_colors_.insert(cell_colors_.begin() + cell_begin, count, color);
+ cell_attrs_.insert(cell_attrs_.begin() + cell_begin, count, attr);
+
+ cells_.insert(cells_.begin() + cell_begin, count, 0);
+ for (unsigned i = cell_begin; i < cell_begin + count; ++i) {
+ cells_[i] = char_begin + (i - cell_begin) + 1;
+ }
+ for (unsigned i = cell_begin + count; i < getWidth(); ++i) {
+ cells_[i] += count;
+ }
+
+ setDirtyFlag(cell_begin, this->getWidth());
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::replaceWithWhiteSpace(
+ unsigned count, unsigned cell_begin, unsigned cell_end,
+ unsigned char color, unsigned char attr, UTF16 space) {
+ if(cell_end>cells_.size() || cell_begin>=cell_end)
+ return;
+ deleteText(cell_begin, cell_end);
+ insertWhiteSpace(count, cell_begin, color, attr, space);
+}
+
+
+
+void FQTermTextLine::insertText(const UTF16 *str, unsigned count,
+ unsigned cell_begin,
+ unsigned char color, unsigned char attr, int charstate) {
+ if (count == 0) return;
+
+ // Calculate how many cells should be occupied for str.
+ // TODO: avoid compute the width of str twice.
+ int width = get_str_width(str, count);
+
+ if (width == 0) {
+#ifdef TEXT_LINE_CHECK
+ //FQ_VERIFY(false);
+ //TODO: enable utf8 decoding
+#endif
+ return;
+ }
+
+ if (charstate & FQTermTextLine::FIRSTPART) {
+ stored_color_ = color;
+ stored_attr_ = attr;
+ width--;
+ count--;
+ if (width == 0 || count == 0) {
+ return;
+ }
+ }
+
+ breakCell(cell_begin);
+
+ verifyCellRange(cell_begin, getWidth());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+
+ chars_.insert(chars_.begin() + char_begin, str, str + count);
+
+ // Insert colors.
+ cell_colors_.insert(cell_colors_.begin() + cell_begin, width, color);
+
+ // Insert attrs.
+ cell_attrs_.insert(cell_attrs_.begin() + cell_begin, width, attr);
+
+ if (charstate & FQTermTextLine::SECONDPART){
+ cell_colors_[cell_begin] = stored_color_;
+ cell_attrs_[cell_begin] = stored_attr_;
+ }
+
+
+ // Change the cells_ mapping to chars_.
+ cells_.insert(cells_.begin() + cell_begin, width, 0);
+ max_cell_count_ = qMax(max_cell_count_, getWidth());
+
+ unsigned last_cell = cell_begin;
+ unsigned cur_cell = last_cell;
+ for (const UTF16 *p = str; p < str +count;) {
+ int w = mk_advace_at_least_one_cell(&p, str + count);
+
+#ifdef TEXT_LINE_CHECK
+ // TODO_UTF16: FIXME: deal with decoding error.
+ FQ_VERIFY(w >= 0);
+#endif
+
+ cur_cell += w;
+
+ for (unsigned i = last_cell; i < cur_cell; ++i) {
+ cells_[i] = char_begin + (p - str);
+ }
+
+ last_cell = cur_cell;
+ }
+
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cur_cell == cell_begin + width);
+#endif
+
+ for (unsigned i = cell_begin + width; i < cells_.size(); ++i) {
+ cells_[i] += count;
+ }
+
+
+
+ setDirtyFlag(cell_begin, cells_.size());
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::deleteText(unsigned cell_begin, unsigned cell_end) {
+ if (cells_.size() == 0 || cell_begin == cell_end) return;
+
+ breakCell(cell_begin);
+ breakCell(cell_end);
+
+ verifyCellRange(cell_begin, cell_end);
+
+ setDirtyFlag(cell_begin, cells_.size());
+
+ unsigned char_begin = getCharBegin(cell_begin);
+ unsigned char_end = getCharEnd(cell_end);
+
+ chars_.erase(chars_.begin() + char_begin, chars_.begin() + char_end);
+
+ cells_.erase(cells_.begin() + cell_begin, cells_.begin() + cell_end);
+ cell_colors_.erase(cell_colors_.begin() + cell_begin,
+ cell_colors_.begin() + cell_end);
+ cell_attrs_.erase(cell_attrs_.begin() + cell_begin,
+ cell_attrs_.begin() + cell_end);
+
+ for (unsigned i = cell_begin; i < cells_.size(); ++i) {
+ cells_[i] -= (char_end - char_begin);
+ }
+
+ verifyTextLine();
+}
+
+void FQTermTextLine::deleteAllText() {
+ deleteText(0, getWidth());
+}
+
+void FQTermTextLine::replaceText(const UTF16 *str, unsigned count,
+ unsigned cell_begin, unsigned cell_end,
+ unsigned char color, unsigned char attr, int charstate) {
+ if (charstate & FQTermTextLine::FIRSTPART) {
+ stored_color_ = color;
+ stored_attr_ = attr;
+ count--;
+ if (count == 0)
+ {
+ return;
+ }
+ charstate &= ~FQTermTextLine::FIRSTPART;
+ }
+
+ deleteText(cell_begin, cell_end);
+ insertText(str, count, cell_begin, color, attr, charstate);
+
+ setDirtyFlag(cell_begin, cells_.size());
+}
+
+
+unsigned FQTermTextLine::getCellBegin(unsigned cell_begin) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin <= getWidth());
+#endif
+
+ if (cell_begin == cells_.size()) return cell_begin;
+
+ while(cell_begin > 0 && cells_[cell_begin - 1] == cells_[cell_begin]) {
+ --cell_begin;
+ }
+
+ return cell_begin;
+}
+
+unsigned FQTermTextLine::getCellEnd(unsigned cell_end) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_end <= getWidth());
+#endif
+
+ if (cell_end == 0) return cell_end;
+
+ while (cell_end < cells_.size() && cells_[cell_end - 1] == cells_[cell_end]) {
+ ++cell_end;
+ }
+
+ return cell_end;
+}
+
+unsigned FQTermTextLine::getCharBegin(unsigned cell_begin) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_begin == getCellBegin(cell_begin));
+#endif
+
+ if (cell_begin == 0) return 0;
+
+ return cells_[cell_begin - 1];
+}
+
+unsigned FQTermTextLine::getCharEnd(unsigned cell_end) const {
+#ifdef TEXT_LINE_CHECK
+ FQ_VERIFY(cell_end == getCellEnd(cell_end));
+#endif
+
+ if (cell_end == 0) return 0;
+
+ return cells_[cell_end - 1];
+}
+
+bool FQTermTextLine::breakCell(unsigned cell_begin) {
+ unsigned real_cell_begin = getCellBegin(cell_begin);
+ if (real_cell_begin == cell_begin) return false;
+
+ unsigned real_cell_end = getCellEnd(cell_begin);
+
+ verifyCellRange(real_cell_begin, real_cell_end);
+
+ unsigned cell_count = real_cell_end - real_cell_begin;
+
+ replaceWithWhiteSpace(cell_count, real_cell_begin, real_cell_end,
+ cell_colors_[cell_begin], cell_attrs_[cell_begin], URC);
+
+ return true;
+}
+
+void FQTermTextLine::getAnsiCtrlSeq(
+ unsigned char color, unsigned char attr,
+ const char *escape, QString &result) const {
+
+ int fg = GETFG(color) + 30;
+ int bg = GETBG(color) + 40;
+
+ result += escape;
+ result += QString("%1;%2").arg(fg).arg(bg);
+
+ if (GETBRIGHT(attr)) {
+ result += ";1";
+ }
+ if (GETDIM(attr)) {
+ result += ";2";
+ }
+ if (GETUNDERLINE(attr)) {
+ result += ";4";
+ }
+ if (GETBLINK(attr)) {
+ result += ";5";
+ }
+ if (GETRAPIDBLINK(attr)) {
+ result += ";6";
+ }
+ if (GETREVERSE(attr)) {
+ result += ";7";
+ }
+ if (GETINVISIBLE(attr)) {
+ result += ";8";
+ }
+
+ result += "m";
+}
+
+
+} // namespace FQTerm
diff --git a/src/terminal/fqterm_text_line.h b/src/terminal/fqterm_text_line.h
new file mode 100644
index 0000000..72ff4c0
--- /dev/null
+++ b/src/terminal/fqterm_text_line.h
@@ -0,0 +1,159 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_TEXTLINE_H
+#define FQTERM_TEXTLINE_H
+
+#include <vector>
+
+#include "fqwcwidth.h"
+#include "fqterm.h"
+class QString;
+
+namespace FQTerm {
+/*
+ A text line consists of several characters in a sequence of cells. A
+ character may occupy some cells, e.g. each English letter occupies
+ one cell, while a CJK character needs two.
+
+ This class is used to manipolate a text line, delete/replace
+ characters in a given cell range, insert characters before a cell,
+ and retrieve characters/colors/attributs of a gaven cell range.
+*/
+class FQTermTextLine {
+ public:
+ FQTermTextLine(unsigned max_cell_count);
+ ~FQTermTextLine();
+
+ enum CHARSTATE{NORMAL = 0x00, FIRSTPART = 0x01, SECONDPART = 0x02};
+ // Get the number of used cells.
+ unsigned getWidth() const {return cells_.size();}
+
+ void setMaxCellCount(unsigned max_cell_count);
+ unsigned getMaxCellCount() const { return max_cell_count_; }
+
+ // Whether this line of text contains blink characters.
+ bool hasBlink() const;
+
+ // Set/get the dirty flag, that indicates which cells have been
+ // modified.
+ void setDirtyFlag(unsigned cell_begin, unsigned cell_end);
+ void safelySetDirtyFlag(unsigned cell_begin, unsigned cell_end);
+ bool getDirtyCells(unsigned &cell_begin, unsigned &cell_end) const;
+ void clearDirtyFlag();
+ void setAllDirty() {setDirtyFlag(0, max_cell_count_);}
+
+ const unsigned char *getColors() const {
+ return cells_.size() == 0 ? NULL : &cell_colors_[0];
+ }
+
+ const unsigned char *getAttributes() const {
+ return cells_.size() == 0 ? NULL : &cell_attrs_[0];
+ }
+
+ // Get plain text in [cell_begin, cell_end).
+ void getPlainText(unsigned cell_begin, unsigned cell_end,
+ QString &result) const;
+
+ void getAllPlainText(QString &result) const {
+ getPlainText(0, getWidth(), result);
+ }
+
+ // Get text in cells [cell_begin, cell_end), with color and
+ // attribute in ANSI format.
+ void getAnsiText(unsigned cell_begin, unsigned cell_end,
+ QString &result,
+ const char *escape = "\x1b\x1b") const;
+
+ void getAllAnsiText(QString &result, const char* escape = "\x1b\x1b") const {
+ getAnsiText(0, getWidth(), result, escape);
+ }
+
+ void appendWhiteSpace(unsigned count,
+ unsigned char color = NO_COLOR,
+ unsigned char attr = NO_ATTR,
+ UTF16 space = ' ');
+
+ void insertWhiteSpace(unsigned count, unsigned cell_begin,
+ unsigned char color = NO_COLOR,
+ unsigned char attr = NO_ATTR,
+ UTF16 space = ' ');
+
+ void replaceWithWhiteSpace(unsigned count,
+ unsigned cell_begin, unsigned cell_end,
+ unsigned char color = NO_COLOR,
+ unsigned char attr = NO_ATTR,
+ UTF16 space = ' ');
+
+ // insert string at specified cell,
+ void insertText(const UTF16 *str, unsigned count,
+ unsigned cell_begin,
+ unsigned char color, unsigned char attr, int charstate = FQTermTextLine::NORMAL);
+ void replaceText(const UTF16 *str, unsigned count,
+ unsigned cell_begin, unsigned cell_end,
+ unsigned char color, unsigned char attr, int charstate = FQTermTextLine::NORMAL);
+ void deleteText(unsigned cell_begin, unsigned cell_end);
+ void deleteAllText();
+
+ // If cell_begin is a begining of a readable character, return
+ // cell_begin itself, otherwise return the previous cell which starts a
+ // readable character.
+ unsigned getCellBegin(unsigned cell_begin) const;
+
+ // If cell_end is a ending of a readable character, return cell_end
+ // itself, otherwise return the next cell which finishes a
+ // readable character.
+ unsigned getCellEnd(unsigned cell_end) const;
+
+ unsigned getCharBegin(unsigned cell_begin) const;
+ unsigned getCharEnd(unsigned cell_end) const;
+
+ private:
+ unsigned max_cell_count_; // max number of cells allowed.
+
+ std::vector<uint16_t> cells_; // store the index of character in this->chars_ for each cell.
+ std::vector<unsigned char> cell_colors_; // store the color for each cell.
+ std::vector<unsigned char> cell_attrs_; // store the attribute for each cell.
+ std::vector<UTF16> chars_; // store all the character.
+
+ unsigned char stored_color_;
+ unsigned char stored_attr_;
+
+ // range of changed text.
+ unsigned dirty_cell_begin_, dirty_cell_end_;
+
+ // if the given cell isn't a beginning of characters,
+ // i.e is in the middle of a wide character,
+ // break the character into several unicode replacement characters (URC)
+ // to make the given cell be a beginning of character.
+ bool breakCell(unsigned cell_begin);
+
+ // calculate ANSI escape sequence with given color, attribute and escape strings.
+ void getAnsiCtrlSeq(unsigned char color, unsigned char attr,
+ const char *escape, QString &result) const;
+
+ // Two functions for debugging/testing.
+ void verifyTextLine() const;
+ void verifyCellRange(unsigned cell_begin, unsigned cell_end) const;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_TEXTLINE_H
diff --git a/src/terminal/internal/fqterm_decode.cpp b/src/terminal/internal/fqterm_decode.cpp
new file mode 100644
index 0000000..cb7bc9a
--- /dev/null
+++ b/src/terminal/internal/fqterm_decode.cpp
@@ -0,0 +1,1279 @@
+/***************************************************************************
+ * 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 <QByteArray>
+
+#include "fqterm.h"
+#include "fqterm_telnet.h"
+#include "fqterm_decode.h"
+#include "fqterm_buffer.h"
+#include "fqwcwidth.h"
+
+namespace FQTerm {
+
+#define MODE_MouseX11 0
+
+/************************************************************************/
+// state for FSM
+// please read ANSI decoding
+StateOption FQTermDecode::VT100StateMachine::normal_state_[] = {
+ {
+ CHAR_CR, &FQTermDecode::cr, normal_state_
+ }, {
+ CHAR_LF, &FQTermDecode::lf, normal_state_
+ }, {
+ CHAR_VT, &FQTermDecode::lf, normal_state_
+ }, {
+ CHAR_ESC, 0, esc_state_
+ }, {
+ CHAR_FF, &FQTermDecode::ff, normal_state_
+ }, {
+ CHAR_TAB, &FQTermDecode::tab, normal_state_
+ }, {
+ CHAR_BS, &FQTermDecode::bs, normal_state_
+ }, {
+ CHAR_BELL, &FQTermDecode::bell, normal_state_
+ }, {
+ CHAR_SO, &FQTermDecode::g1, normal_state_
+ }, {
+ CHAR_SI, &FQTermDecode::g0, normal_state_
+ }, {
+ CHAR_CAN, 0, normal_state_ // should be ignored
+ }, {
+ CHAR_DEL, &FQTermDecode::del, normal_state_
+ }, {
+ CHAR_NUL, 0, normal_state_ // should be ignored
+ }, {
+ CHAR_ENQ, &FQTermDecode::enq, normal_state_
+ }, {
+ CHAR_NORMAL, &FQTermDecode::normalInput, normal_state_
+ }
+};
+
+// state after a ESC_CHAR
+StateOption FQTermDecode::VT100StateMachine::esc_state_[] = {
+ {
+ '7', &FQTermDecode::saveCursor, normal_state_
+ }, {
+ '8', &FQTermDecode::restoreCursor, normal_state_
+ }, {
+ '[', &FQTermDecode::clearParam, bracket_state_
+ }, {
+ ']', &FQTermDecode::clearParam, title_state_
+ }, {
+ '(', 0, select_g0_charset_state_
+ }, {
+ ')', 0, select_g1_charset_state_
+ }, {
+ 'D', &FQTermDecode::moveCursorDown, normal_state_
+ }, {
+ 'E', &FQTermDecode::nextLine, normal_state_
+ }, {
+ 'H', &FQTermDecode::addTabStop, normal_state_
+ }, {
+ 'M', &FQTermDecode::moveCursorUp, normal_state_
+ }, {
+ '>', &FQTermDecode::setNumericKeypad, normal_state_
+ }, {
+ '=', &FQTermDecode::setAppKeypad, normal_state_
+ }, {
+ '<', &FQTermDecode::test, normal_state_
+ }, {
+ '#', 0, sharp_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC [
+StateOption FQTermDecode::VT100StateMachine::bracket_state_[] = {
+ {
+ '0', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '1', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '2', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '3', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '4', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '5', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '6', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '7', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '8', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ '9', &FQTermDecode::paramDigit, bracket_state_
+ }, {
+ ';', &FQTermDecode::nextParam, bracket_state_
+ }, {
+ '?', &FQTermDecode::clearParam, private_state_
+ }, {
+ 'A', &FQTermDecode::cursorUp, normal_state_
+ }, {
+ 'B', &FQTermDecode::cursorDown, normal_state_
+ }, {
+ 'C', &FQTermDecode::cursorRight, normal_state_
+ }, {
+ 'D', &FQTermDecode::cursorLeft, normal_state_
+ }, {
+ 'H', &FQTermDecode::cursorPosition, normal_state_
+ }, {
+ 'J', &FQTermDecode::eraseScreen, normal_state_
+ }, {
+ 'K', &FQTermDecode::eraseLine, normal_state_
+ }, {
+ 'L', &FQTermDecode::insertLine, normal_state_
+ }, {
+ 'M', &FQTermDecode::deleteLine, normal_state_
+ }, {
+ 'P', &FQTermDecode::deleteStr, normal_state_
+ }, {
+ 'X', &FQTermDecode::eraseStr, normal_state_
+ }, {
+ 'f', &FQTermDecode::cursorPosition, normal_state_
+ }, {
+ 'g', &FQTermDecode::clearTabStop, normal_state_
+ }, {
+ 'h', &FQTermDecode::setMode, normal_state_
+ }, {
+ 'l', &FQTermDecode::resetMode, normal_state_
+ }, {
+ 'm', &FQTermDecode::setAttr, normal_state_
+ }, {
+ 'q', 0, normal_state_ // ignore LED-related commands.
+ }, {
+ 'r', &FQTermDecode::setMargins, normal_state_
+ }, {
+ 's', &FQTermDecode::saveCursor, normal_state_
+ }, {
+ 'u', &FQTermDecode::restoreCursor, normal_state_
+ }, {
+ '@', &FQTermDecode::insertStr, normal_state_
+ }, {
+ CHAR_CR, &FQTermDecode::cr, bracket_state_
+ }, {
+ CHAR_LF, &FQTermDecode::lf, bracket_state_
+ }, {
+ CHAR_VT, &FQTermDecode::lf, bracket_state_
+ }, {
+ CHAR_FF, &FQTermDecode::ff, bracket_state_
+ }, {
+ CHAR_TAB, &FQTermDecode::tab, bracket_state_
+ }, {
+ CHAR_BS, &FQTermDecode::bs, bracket_state_
+ }, {
+ CHAR_BELL, &FQTermDecode::bell, bracket_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC (
+StateOption FQTermDecode::VT100StateMachine::select_g0_charset_state_[] = {
+ {
+ 'A', &FQTermDecode::selectG0A, normal_state_
+ }, {
+ 'B', &FQTermDecode::selectG0B, normal_state_
+ }, {
+ '0', &FQTermDecode::selectG00, normal_state_
+ }, {
+ '1', &FQTermDecode::selectG01, normal_state_
+ }, {
+ '2', &FQTermDecode::selectG02, normal_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC )
+StateOption FQTermDecode::VT100StateMachine::select_g1_charset_state_[] = {
+ {
+ 'A', &FQTermDecode::selectG1A, normal_state_
+ }, {
+ 'B', &FQTermDecode::selectG1B, normal_state_
+ }, {
+ '0', &FQTermDecode::selectG10, normal_state_
+ }, {
+ '1', &FQTermDecode::selectG11, normal_state_
+ }, {
+ '2', &FQTermDecode::selectG12, normal_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC [ ?
+StateOption FQTermDecode::VT100StateMachine::private_state_[] = {
+ {
+ '0', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '1', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '2', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '3', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '4', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '5', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '6', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '7', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '8', &FQTermDecode::paramDigit, private_state_
+ }, {
+ '9', &FQTermDecode::paramDigit, private_state_
+ }, {
+ ';', &FQTermDecode::nextParam, private_state_
+ }, {
+ 'h', &FQTermDecode::setDecPrivateMode, normal_state_
+ }, {
+ 'l', &FQTermDecode::resetDecPrivateMode, normal_state_
+ }, {
+ 's', &FQTermDecode::saveMode, normal_state_
+ }, {
+ 'r', &FQTermDecode::restoreMode, normal_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC #
+StateOption FQTermDecode::VT100StateMachine::sharp_state_[] = {
+ {
+ '8', &FQTermDecode::fillScreen, normal_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+// state after ESC ]
+// to change the terminal title dynamically.
+// Dynamic titles
+// Many people find it useful to set the title of a terminal to reflect dynamic information,
+// such as the name of the host the user is logged into, the current working directory, etc.
+// This may be done by using XTerm escape sequences. The following sequences are useful in this respect:
+// ESC]0;stringBEL Set icon name and window title to string
+// ESC]1;stringBEL Set icon name to string
+// ESC]2;stringBEL Set window title to string
+// where ESC is the escape character (\033), and BEL is the bell character (\007).
+// Currently we just ignore this.
+StateOption FQTermDecode::VT100StateMachine::title_state_[] = {
+ {
+ '0', &FQTermDecode::paramDigit, title_state_
+ }, {
+ '1', &FQTermDecode::paramDigit, title_state_
+ }, {
+ '2', &FQTermDecode::paramDigit, title_state_
+ }, {
+ '4', &FQTermDecode::paramDigit, title_state_
+ }, {
+ '5', &FQTermDecode::paramDigit, title_state_
+ }, {
+ '6', &FQTermDecode::paramDigit, title_state_
+ }, {
+ ';', &FQTermDecode::clearText, title_text_state_
+ }, {
+ CHAR_NORMAL, 0, normal_state_
+ }
+};
+
+StateOption FQTermDecode::VT100StateMachine::title_text_state_[] = {
+ {
+ CHAR_ESC, 0, esc_state_
+ }, {
+ CHAR_BELL, &FQTermDecode::setTitle, normal_state_
+ }, {
+ CHAR_NORMAL, &FQTermDecode::collectText, title_state_
+ }
+};
+
+const char *FQTermDecode::getStateName(const StateOption *state) {
+ if (state == VT100StateMachine::normal_state_) {
+ return "VT100StateMachine::normal_state_";
+ } else if (state == VT100StateMachine::esc_state_) {
+ return "VT100StateMachine::esc_state_";
+ } else if (state == VT100StateMachine::bracket_state_) {
+ return "VT100StateMachine::bracket_state_";
+ } else if (state == VT100StateMachine::private_state_) {
+ return "VT100StateMachine::private_state_";
+ } else if (state == VT100StateMachine::title_state_) {
+ return "VT100StateMachine::title_state_";
+ } else {
+ return "Unknow";
+ }
+}
+
+
+FQTermDecode::FQTermDecode(FQTermBuffer *buffer, int server_encoding) {
+ leftToDecode_.clear();
+ termBuffer_ = buffer;
+
+ interrupt_decode_ = false;
+
+ current_state_ = VT100StateMachine::normal_state_;
+
+ default_color_ = NO_COLOR;
+ default_attr_ = NO_ATTR;
+
+ current_color_ = default_color_;
+ current_attr_ = default_attr_;
+
+ isBell_ = false;
+
+ termBuffer_->setCurrentAttr(current_color_, current_attr_);
+
+ // TODO_UTF16: please create a encoding enum.
+ server_encoding_ = server_encoding;
+
+ currentMode_[MODE_MouseX11] = savedMode_[MODE_MouseX11] = false;
+ FQ_VERIFY(connect(termBuffer_, SIGNAL(caretChangeRow()), this, SLOT(onCaretChangeRow())));
+}
+
+FQTermDecode::~FQTermDecode() {
+
+}
+
+// precess input string from telnet socket
+//void FQTermDecode::ansiDecode( const QCString &cstr, int length )
+int FQTermDecode::decode(const char *cstr, int length) {
+ inputData_ = cstr;
+ inputLength_ = length; //inputData.length();
+
+ dataIndex_ = 0;
+ isBell_ = false;
+
+ interrupt_decode_ = false;
+
+ int i;
+ StateOption *lastState;
+
+ termBuffer_->startDecode();
+
+ // here we use FSM to ANSI decoding
+ // use switch case is ok too
+ // but i think use function pointer array can make this clear
+ // you can see the defination at the beginning
+ while (dataIndex_ < inputLength_) {
+ // current state always be initialized to point to the deginning of three structures
+ // ( normalState, escState, bracketState )
+ i = 0;
+ while (current_state_[i].byte != CHAR_NORMAL && current_state_[i].byte !=
+ inputData_[dataIndex_]) {
+ i++;
+ }
+
+ // action must be allowed to redirect state change
+ // get current state with input character i ( hehe, current now become last one )
+ lastState = current_state_ + i; // good !!
+
+ bool trace_state = true;
+ if (current_state_ == VT100StateMachine::normal_state_
+ && lastState->nextState == VT100StateMachine::normal_state_) {
+ trace_state = false;
+ }
+
+ if (trace_state) {
+ FQ_TRACE("ansi", 10) << "Current state: " << getStateName(current_state_);
+ FQ_TRACE("ansi", 10) << dumpHexString << std::string("") + inputData_[dataIndex_]
+ << dumpNormalString << " " << inputData_[dataIndex_];
+ }
+
+ if (lastState->action != 0) {
+ (this->*(lastState->action))();
+ } else {
+ if (trace_state) FQ_TRACE("ansi", 10) << "No action";
+ }
+ if (trace_state) FQ_TRACE("ansi", 10) << "Next state: " << getStateName(lastState->nextState);
+
+ // reinit current state
+ current_state_ = lastState->nextState;
+
+ dataIndex_++;
+
+ if (interrupt_decode_) {
+ break;
+ }
+ }
+ termBuffer_->endDecode();
+
+ return dataIndex_;
+}
+
+
+QString FQTermDecode::bbs2unicode(const QByteArray &text) {
+ return encoding2unicode(text, server_encoding_);
+}
+
+///////////////////////////////////
+//helper function to decode utf8//
+//////////////////////////////////
+static int utf8_expected_byte_count(char first_byte)
+{
+ char expected = 0;
+ if (!(first_byte & 0x80))
+ return 0; //1 byte ascii
+ else
+ expected++;
+ if (!(first_byte & 0x40))
+ {
+ return -1; //not a leading byte.
+ }
+ if (!(first_byte & 0x20))
+ return expected;
+ else
+ expected++;
+ if (!(first_byte & 0x10))
+ return expected;
+ else
+ expected++;
+ if (!(first_byte & 0x08))
+ return expected;
+ return -1;
+}
+
+///////////////////////////////////
+//helper function to decode utf8//
+//////////////////////////////////
+static int gdbnbig5_expected_byte_count(char first_byte)
+{
+ char expected = 0;
+ if (!(first_byte & 0x80))
+ return 0; //1 byte ascii
+ else
+ expected++;
+ return expected;
+}
+
+int FQTermDecode::processInput(QByteArray& result)
+{
+ int charstate = FQTermTextLine::NORMAL;
+ int expect_bytes = 0;
+ if (leftToDecode_.size())
+ {
+ switch(server_encoding_)
+ {
+ case FQTERM_ENCODING_GBK:
+ case FQTERM_ENCODING_BIG5:
+ case FQTERM_ENCODING_HKSCS:
+ case FQTERM_ENCODING_UAO:
+ expect_bytes = gdbnbig5_expected_byte_count(leftToDecode_[0]);
+ break;
+ case FQTERM_ENCODING_UTF8:
+ expect_bytes = utf8_expected_byte_count(leftToDecode_[0]);
+ break;
+ }
+ }
+
+ //TODO: error.
+ if (expect_bytes < 0) {
+ expect_bytes = 0;
+ }
+
+
+ int n = 0;
+ int last_char_index = n;
+ while (((dataIndex_ + n) < inputLength_) && (signed(inputData_[dataIndex_ + n]) >= 0x20
+ || signed(inputData_[dataIndex_ + n]) < 0x00)) {
+
+ if (expect_bytes != 0) {
+ expect_bytes--;
+ } else {
+ last_char_index = n;
+ switch(server_encoding_)
+ {
+ case FQTERM_ENCODING_GBK:
+ case FQTERM_ENCODING_BIG5:
+ case FQTERM_ENCODING_HKSCS:
+ case FQTERM_ENCODING_UAO:
+ expect_bytes = gdbnbig5_expected_byte_count(inputData_[dataIndex_ + n]);
+ break;
+ case FQTERM_ENCODING_UTF8:
+ expect_bytes = utf8_expected_byte_count(inputData_[dataIndex_ + n]);
+ break;
+ }
+ //TODO: error.
+ if (expect_bytes < 0) {
+ expect_bytes = 0;
+ }
+
+
+ }
+
+ n++;
+ }
+
+ if (expect_bytes) {
+ if(dataIndex_ + n == inputLength_) {
+ interrupt_decode_ = true;
+ }
+ }
+
+ result.clear();
+ result.reserve(n + 1);
+ if (leftToDecode_.size()) {
+ result += leftToDecode_;
+ switch(server_encoding_)
+ {
+ case FQTERM_ENCODING_GBK:
+ case FQTERM_ENCODING_BIG5:
+ case FQTERM_ENCODING_HKSCS:
+ case FQTERM_ENCODING_UAO:
+ charstate |= FQTermTextLine::SECONDPART;
+ break;
+ case FQTERM_ENCODING_UTF8:
+ break;
+ }
+ leftToDecode_.clear();
+ }
+ int real_n = n;
+ if (expect_bytes) {
+ real_n = last_char_index;
+ leftToDecode_ = QByteArray(inputData_ + dataIndex_ + last_char_index, n - last_char_index);
+ }
+
+ result.push_back(QByteArray(inputData_ + dataIndex_, real_n));
+ if (expect_bytes) {
+ switch(server_encoding_)
+ {
+ case FQTERM_ENCODING_GBK:
+ case FQTERM_ENCODING_BIG5:
+ case FQTERM_ENCODING_HKSCS:
+ case FQTERM_ENCODING_UAO:
+ result.push_back('?'); //make sure the attr is recorded,
+ //since last -1 operation can make cstr to be empty
+ charstate |= FQTermTextLine::FIRSTPART;
+ break;
+ case FQTERM_ENCODING_UTF8:
+ break;
+ }
+ }
+
+ n--;
+ dataIndex_ += n;
+
+ return charstate;
+}
+
+
+// fill letters into char buffer
+// TODO: this function may contain bug, need double-check.
+// 1. input should be ascii-compitable encoding.
+void FQTermDecode::normalInput() {
+
+ FQ_FUNC_TRACE("ansi", 8);
+
+ // TODO_UTF16: check ascii-incompitable encoding.
+ if (signed(inputData_[dataIndex_]) < 0x20 && signed(inputData_[dataIndex_]) >= 0x00) {
+ // not print char
+ return ;
+ }
+
+ QByteArray cstr;
+ int charstate = processInput(cstr);
+
+
+
+ QString str = bbs2unicode(cstr);
+ FQ_TRACE("normal_input", 9) << "hex: " << dumpHexString
+ << str.toLocal8Bit().constData() ;
+ FQ_TRACE("normal_input", 9) << "text: " << str;
+ termBuffer_->writeText(str, charstate);
+
+}
+
+// non-printing characters functions
+void FQTermDecode::cr() {
+ FQ_FUNC_TRACE("ansi", 8);
+ // FIXME: dirty
+ termBuffer_->carriageReturn();
+}
+
+void FQTermDecode::lf() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->lineFeed();
+}
+
+void FQTermDecode::ff() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->eraseEntireTerm();
+
+ termBuffer_->moveCaretTo(0, 0);
+}
+
+void FQTermDecode::tab() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->tab();
+}
+
+void FQTermDecode::bs() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->moveCaretOffset(-1, 0);
+}
+
+void FQTermDecode::bell() {
+ FQ_FUNC_TRACE("ansi", 8);
+ isBell_ = true;
+}
+
+void FQTermDecode::del() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->moveCaretOffset(-1, 0);
+ //termBuffer_->deleteText(1);
+}
+
+void FQTermDecode::g0() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->invokeCharset(true);
+}
+
+void FQTermDecode::g1() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->invokeCharset(false);
+}
+
+void FQTermDecode::enq() {
+ emit enqReceived();
+}
+
+void FQTermDecode::addTabStop() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->addTabStop();
+}
+
+void FQTermDecode::clearTabStop() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ if (param_[0] == 0) {
+ termBuffer_->clearTabStop(false);
+ } else if (param_[0] == 3) {
+ termBuffer_->clearTabStop(true);
+ }
+}
+
+void FQTermDecode::setMargins() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ if (isParamAvailable_) {
+ termBuffer_->setMargins(param_[0] - 1, param_[1] - 1);
+ } else {
+ termBuffer_->setMargins(0, termBuffer_->getNumRows() - 1);
+ }
+}
+
+// parameters functions
+void FQTermDecode::clearParam() {
+ FQ_FUNC_TRACE("ansi", 9);
+ paramIndex_ = 0;
+ memset(param_, 0, sizeof(param_));
+ isParamAvailable_ = false;
+}
+
+// for performance, this grabs all digits
+void FQTermDecode::paramDigit() {
+ FQ_FUNC_TRACE("ansi", 10);
+ isParamAvailable_ = true;
+
+ // make stream into number
+ // ( e.g. this input character is '1' and this param is 4
+ // after the following sentence this param is changed to 41
+ param_[paramIndex_] = param_[paramIndex_] *10+inputData_[dataIndex_] - '0';
+}
+
+void FQTermDecode::nextParam() {
+ FQ_FUNC_TRACE("ansi", 9);
+ paramIndex_++;
+}
+
+void FQTermDecode::saveCursor() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->saveCaret();
+}
+
+void FQTermDecode::restoreCursor() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->restoreCaret();
+}
+
+void FQTermDecode::cursorPosition() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int x = param_[1];
+ int y = param_[0];
+
+ if (x == 0) {
+ x = 1;
+ }
+ if (y == 0) {
+ y = 1;
+ }
+
+ termBuffer_->changeCaretPosition(x - 1, y - 1);
+}
+
+void FQTermDecode::cursorLeft() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->moveCaretOffset(-n, 0);
+}
+
+void FQTermDecode::cursorRight() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->moveCaretOffset(n, 0);
+}
+
+void FQTermDecode::cursorUp() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->moveCaretOffset(0, -n);
+}
+
+void FQTermDecode::cursorDown() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->moveCaretOffset(0, n);
+}
+
+void FQTermDecode::moveCursorUp() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->scrollTerm(-1);//moveCaretOffset(0, -1, true);
+}
+
+void FQTermDecode::moveCursorDown() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->scrollTerm(1);//moveCaretOffset(0, 1, true);
+}
+
+void FQTermDecode::nextLine() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->carriageReturn();
+ termBuffer_->moveCaretOffset(0, 1, true);
+}
+
+void FQTermDecode::selectG0A() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::UNITED_KINGDOM_SET, true);
+}
+
+void FQTermDecode::selectG0B() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::ASCII_SET, true);
+}
+
+void FQTermDecode::selectG00() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::SPECIAL_GRAPHICS, true);
+}
+
+void FQTermDecode::selectG01() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(
+ FQTermBuffer::ALTERNATE_CHARACTER_ROM_STANDARD_CHARACTER_SET, true);
+}
+
+void FQTermDecode::selectG02() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(
+ FQTermBuffer::ALTERNATE_CHARACTER_ROM_SPECIAL_GRAPHICS, true);
+}
+
+void FQTermDecode::selectG1A() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::UNITED_KINGDOM_SET, false);
+}
+
+void FQTermDecode::selectG1B() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::ASCII_SET, false);
+}
+
+void FQTermDecode::selectG10() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(FQTermBuffer::SPECIAL_GRAPHICS, false);
+}
+
+void FQTermDecode::selectG11() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(
+ FQTermBuffer::ALTERNATE_CHARACTER_ROM_STANDARD_CHARACTER_SET, false);
+}
+
+void FQTermDecode::selectG12() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->SelectVtCharacterSet(
+ FQTermBuffer::ALTERNATE_CHARACTER_ROM_SPECIAL_GRAPHICS, false);
+}
+
+// erase functions
+void FQTermDecode::eraseStr() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->eraseText(n);
+}
+
+// insert functions
+void FQTermDecode::insertStr() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->insertSpaces(n);
+}
+
+// delete functions
+void FQTermDecode::deleteStr() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->deleteText(n);
+}
+
+void FQTermDecode::eraseLine() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ switch (param_[0]) {
+ case 0:
+ termBuffer_->eraseToLineEnd();
+ break;
+ case 1:
+ termBuffer_->eraseToLineBegin();
+ break;
+ case 2:
+ termBuffer_->eraseEntireLine();
+ break;
+ default:
+ break;
+ }
+}
+
+void FQTermDecode::insertLine() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->insertLines(n);
+}
+
+void FQTermDecode::deleteLine() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ int n = param_[0];
+
+ if (n < 1) {
+ n = 1;
+ }
+
+ termBuffer_->deleteLines(n);
+}
+
+void FQTermDecode::eraseScreen() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ switch (param_[0]) {
+ case 0:
+ termBuffer_->eraseToTermEnd();
+ break;
+ case 1:
+ termBuffer_->eraseToTermBegin();
+ break;
+ case 2:
+ termBuffer_->eraseEntireTerm();
+ break;
+ case 3:
+ break;
+ }
+}
+
+void FQTermDecode::setAttr() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ // get all attributes of character
+ if (!paramIndex_ && param_[0] == 0) {
+ current_color_ = default_color_;
+ current_attr_ = default_attr_;
+
+ termBuffer_->setCurrentAttr(current_color_, current_attr_);
+ return ;
+ }
+
+ unsigned char cp = current_color_;
+ unsigned char ea = current_attr_;
+ for (int n = 0; n <= paramIndex_; n++) {
+ if (param_[n] / 10 == 4) {
+ // background color
+ cp = cp &~BGMASK;
+ cp += SETBG(param_[n] % 10);
+ } else if (param_[n] / 10 == 3) {
+ // front color
+ cp = cp &~FGMASK;
+ cp += SETFG(param_[n] % 10);
+ } else {
+ switch (param_[n]) {
+ case 0:
+ // attr off
+ cp = default_color_; //NO_COLOR;
+ ea = default_attr_; //NO_ATTR;
+ break;
+ case 1:
+ // bright
+ ea = SETBRIGHT(ea);
+ break;
+ case 2:
+ // dim
+ ea = SETDIM(ea);
+ break;
+ case 4:
+ // underline
+ ea = SETUNDERLINE(ea);
+ break;
+ case 5:
+ // blink
+ ea = SETBLINK(ea);
+ break;
+ case 7:
+ // reverse
+ ea = SETREVERSE(ea);
+ break;
+ case 8:
+ // invisible
+ ea = SETINVISIBLE(ea);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ current_color_ = cp;
+ current_attr_ = ea;
+
+ termBuffer_->setCurrentAttr(current_color_, current_attr_);
+}
+
+void FQTermDecode::setMode() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 4:
+ termBuffer_->setMode(FQTermBuffer::INSERT_MODE);
+ break;
+ case 20:
+ termBuffer_->setMode(FQTermBuffer::NEWLINE_MODE);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::resetMode() {
+ //TODO: more modes here.
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 4:
+ termBuffer_->resetMode(FQTermBuffer::INSERT_MODE);
+ break;
+ case 20:
+ termBuffer_->resetMode(FQTermBuffer::NEWLINE_MODE);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::setDecPrivateMode() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 1:
+ termBuffer_->setMode(FQTermBuffer::CURSOR_MODE);
+ break;
+ case 2:
+ termBuffer_->setMode(FQTermBuffer::ANSI_MODE);
+ break;
+ case 3:
+ termBuffer_->setTermSize(132, 24);
+ break;
+ case 4:
+ // smooth scrolling mode
+ // nothing to do.
+ break;
+ case 5:
+ termBuffer_->setMode(FQTermBuffer::LIGHTBG_MODE);
+ break;
+ case 6:
+ termBuffer_->setMode(FQTermBuffer::ORIGIN_MODE);
+ break;
+ case 7:
+ termBuffer_->setMode(FQTermBuffer::AUTOWRAP_MODE);
+ break;
+ case 8:
+ termBuffer_->setMode(FQTermBuffer::AUTOREPEAT_MODE);
+ break;
+ case 9:
+ // Interlace mode
+ // nothing to do.
+ break;
+ case 1000:
+ case 1001:
+ emit mouseMode(true);
+ currentMode_[MODE_MouseX11] = true;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::resetDecPrivateMode() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 1:
+ termBuffer_->resetMode(FQTermBuffer::CURSOR_MODE);
+ break;
+ case 2:
+ termBuffer_->resetMode(FQTermBuffer::ANSI_MODE);
+ break;
+ case 3:
+ termBuffer_->setTermSize(80, 24);
+ break;
+ case 4:
+ // jump scrolling mode
+ // nothing to do.
+ break;
+ case 5:
+ termBuffer_->resetMode(FQTermBuffer::LIGHTBG_MODE);
+ break;
+ case 6:
+ termBuffer_->resetMode(FQTermBuffer::ORIGIN_MODE);
+ break;
+ case 7:
+ termBuffer_->resetMode(FQTermBuffer::AUTOWRAP_MODE);
+ break;
+ case 8:
+ termBuffer_->resetMode(FQTermBuffer::AUTOREPEAT_MODE);
+ break;
+ case 9:
+ // Interlace mode
+ // nothing to do.
+ break;
+ case 1000:
+ case 1001:
+ currentMode_[MODE_MouseX11] = false;
+ emit mouseMode(false);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::setNumericKeypad() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->setMode(FQTermBuffer::NUMERIC_MODE);
+}
+
+void FQTermDecode::setAppKeypad() {
+ FQ_FUNC_TRACE("ansi", 8);
+ termBuffer_->resetMode(FQTermBuffer::NUMERIC_MODE);
+}
+
+void FQTermDecode::saveMode() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 1000:
+ case 1001:
+ savedMode_[MODE_MouseX11] = currentMode_[MODE_MouseX11];
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::restoreMode() {
+ FQ_FUNC_TRACE("ansi", 8);
+ logParam();
+
+ for (int i = 0; i <= paramIndex_; i++) {
+ int n = param_[i];
+ switch (n) {
+ case 1000:
+ case 1001:
+ currentMode_[MODE_MouseX11] = savedMode_[MODE_MouseX11];
+ emit mouseMode(currentMode_[MODE_MouseX11]);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void FQTermDecode::fillScreen() {
+ FQ_FUNC_TRACE("ansi", 8);
+
+ termBuffer_->fillScreenWith('E');
+}
+
+void FQTermDecode::test() {
+ FQ_FUNC_TRACE("ansi", 8);
+}
+
+void FQTermDecode::logParam() const {
+ if (isParamAvailable_) {
+ for (int i = 8; i <= paramIndex_; ++i) {
+ FQ_TRACE("ansi", 8) << "Parameter[" << i << "] = " << param_[i];
+ }
+ } else {
+ FQ_TRACE("ansi", 8) << "No parameter.";
+ }
+}
+
+void FQTermDecode::onCaretChangeRow() {
+ leftToDecode_.clear();
+}
+
+void FQTermDecode::setTitle() {
+ if (!isParamAvailable_) {
+ return;
+ }
+ switch(param_[0])
+ {
+ case 0:
+ //icon and text
+ break;
+ case 1:
+ //icon
+ break;
+ case 2:
+ //text
+ emit onTitleSet(bbs2unicode(textParam_));
+ break;
+ case 46:
+ //log file
+ break;
+ case 50:
+ //font
+ break;
+ default:
+ break;
+ }
+}
+
+void FQTermDecode::collectText() {
+ FQ_FUNC_TRACE("ansi", 10);
+ if (textParam_.length() < 4096)
+ textParam_ += inputData_[dataIndex_];
+}
+
+void FQTermDecode::clearText() {
+ FQ_FUNC_TRACE("ansi", 10);
+ textParam_.clear();
+}
+
+
+
+
+} // namespace FQTerm
+
+#include "fqterm_decode.moc"
diff --git a/src/terminal/internal/fqterm_decode.h b/src/terminal/internal/fqterm_decode.h
new file mode 100644
index 0000000..5f3f46d
--- /dev/null
+++ b/src/terminal/internal/fqterm_decode.h
@@ -0,0 +1,194 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_DECODE_H
+#define FQTERM_DECODE_H
+
+#include <QObject>
+#include "fqterm_text_line.h"
+
+namespace FQTerm {
+
+class FQTermDecode;
+class FQTermBuffer;
+class FQTermTelnet;
+
+// this for FSM
+typedef void(FQTermDecode:: *StateFunc)();
+
+struct StateOption {
+ int byte; // char value to look for; -1==end/default
+ StateFunc action;
+ StateOption *nextState;
+};
+
+class FQTermDecode: public QObject {
+ Q_OBJECT;
+ public:
+ FQTermDecode(FQTermBuffer *, int server_encoding);
+ ~FQTermDecode();
+
+
+ // translate data from telnet socket to our own buffer
+ // return how many bytes are processed.
+ int decode(const char *cstr, int length);
+
+ bool bellReceive() {
+ return isBell_;
+ }
+
+ void setServerEncoding(int encoding) {
+ server_encoding_ = encoding;
+ }
+
+
+ //signals:
+ // void decodeFinished();
+ signals:
+ void mouseMode(bool);
+ void enqReceived();
+ void onTitleSet(const QString&);
+ public slots:
+ void onCaretChangeRow();
+ private:
+ // escape sequence actions
+ // you'd better see FSM structure array in FQTermDecode.cpp
+
+ void setAttr();
+ void setMargins();
+
+ // char screen functions
+ void deleteStr();
+ void deleteLine();
+ void insertStr();
+ void insertLine();
+ void eraseStr();
+ void eraseLine();
+ void eraseScreen();
+
+ // cursor functions
+ void saveCursor();
+ void restoreCursor();
+ void cursorPosition();
+
+ // Move cursor, stop at boundary.
+ void cursorLeft();
+ void cursorDown();
+ void cursorRight();
+ void cursorUp();
+
+ // Move cursor, scroll at boundary.
+ void moveCursorUp();
+ void moveCursorDown();
+ void nextLine();
+
+ void selectG0A();
+ void selectG0B();
+ void selectG00();
+ void selectG01();
+ void selectG02();
+
+ void selectG1A();
+ void selectG1B();
+ void selectG10();
+ void selectG11();
+ void selectG12();
+
+ // other escape sequence actions
+ void normalInput();
+
+ //title settings
+ void setTitle();
+ void collectText();
+ void clearText();
+
+ // action parameters
+ void clearParam();
+ void paramDigit();
+ void nextParam();
+
+ // non-printing characters
+ void cr(), lf(), ff(), bell(), tab(), bs(), del(), g0(), g1(), enq();
+
+ void addTabStop();
+ void clearTabStop();
+
+ void setMode();
+ void resetMode();
+ void setDecPrivateMode();
+ void resetDecPrivateMode();
+ void setNumericKeypad();
+ void setAppKeypad();
+
+ void saveMode();
+ void restoreMode();
+
+ // video alignment test - fill screen with E's.
+ void fillScreen();
+
+ void test();
+
+ private:
+
+ bool isBell_;
+ // short currentAttr_, defaultAttr_;
+ unsigned char current_color_, default_color_;
+ unsigned char current_attr_, default_attr_;
+
+ // ********** ansi decoder states ****************
+ StateOption *current_state_;
+
+ struct VT100StateMachine {
+ static StateOption normal_state_[], esc_state_[], bracket_state_[];
+ static StateOption private_state_[], sharp_state_[];
+ static StateOption title_state_[], title_text_state_[];
+ static StateOption select_g0_charset_state_[], select_g1_charset_state_[];
+ };
+
+ static const char *getStateName(const StateOption *state);
+
+ void logParam() const;
+
+
+ bool interrupt_decode_;
+
+ // ********** decoder *****************
+ const char *inputData_;
+ int inputLength_, dataIndex_;
+
+ int paramIndex_, param_[30];
+ bool isParamAvailable_;
+
+ QByteArray textParam_;
+
+ bool savedMode_[30];
+ bool currentMode_[30];
+
+ FQTermBuffer *termBuffer_;
+ int server_encoding_;
+
+ QString bbs2unicode(const QByteArray &text);
+ int processInput(QByteArray& result);
+ QByteArray leftToDecode_;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_DECODE_H
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"
diff --git a/src/terminal/internal/fqterm_telnet.h b/src/terminal/internal/fqterm_telnet.h
new file mode 100644
index 0000000..33e8c6e
--- /dev/null
+++ b/src/terminal/internal/fqterm_telnet.h
@@ -0,0 +1,195 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_TELNET_H
+#define FQTERM_TELNET_H
+
+#include <QAbstractSocket>
+#include <QByteArray>
+
+namespace FQTerm {
+
+#ifndef u_char
+#define u_char uchar
+#endif
+
+#define NTSTATES 6 // # of Telnet Socket-Input FSM States
+#define NSSTATES 3 // # of Telnet Option Subnegotiation FSM States
+#define NCHRS 256 // # of valid characters
+
+// decleration
+class FQTermTelnet;
+// actionFunc is a pointer, point to a FQTermTelnet's func
+typedef int(FQTermTelnet:: *ptrActionFunc)(int c);
+
+//fsm struct
+struct fsm_trans {
+ u_char ft_state; // current state
+ short ft_char; // input character
+ u_char ft_next; // next state
+ ptrActionFunc ft_action; // action to take
+};
+
+
+/*------------------------------------------------------------------------------
+ * FQTermTelnet class definition
+ *-------------------------------------------------------------------------------
+ */
+class FQTermSocket;
+
+// Telnet connection, a wrapper of socket.
+// It will translate raw NVT data from low level socket to ansi data,
+// and then upper level application can read it.
+// It also can send ascii data (0~127).
+class FQTermTelnet: public QObject {
+ Q_OBJECT;
+ public:
+ FQTermTelnet(const QString &termtype, int rows, int numColumns, int protocolType, int hostType, const
+ char *sshuser = NULL, const char *sshpasswd = NULL);
+ ~FQTermTelnet();
+
+ void setProxy(int nProxyType, //0-no proxy; 1-wingate; 2-sock4; 3-socks5
+ bool bAuth, // if authentation needed
+ const QString &strProxyHost, quint16 uProxyPort, const QString &strProxyUsr,
+ const QString &strProxyPwd);
+ void connectHost(const QString &hostname, quint16 portnumber);
+
+ // Read ansi data.
+ int read(char *data, uint maxlen);
+
+ // Write data raw data
+ int write(const char *data, uint len);
+
+ void close(); // User close the connection
+
+ int raw_len();
+ int read_raw(char *data, uint maxlen);
+
+ bool readyForInput();
+
+
+ signals:
+ void readyRead(int, int); // There are datas to be read out
+ void TelnetState(int); // The state telnet, defined as TSXXXX in fqterm.h
+ void requestUserPwd(QString *user, QString *pwd, bool *isOK);
+ void errorMessage(QString);
+
+ public slots:
+ void windowSizeChanged(int, int);
+
+ private slots:
+ // Retrieve data from socket, translate NVT data to ansi data,
+ // then notify data ready.
+ void socketReadyRead();
+
+ void connected();
+ void showError(QAbstractSocket::SocketError);
+ void hostFound();
+ void delayCloseFinished();
+ void closed();
+ protected:
+ //init structure fsm
+ void init_telnet();
+ void fsmbuild();
+ void fsminit(u_char fsm[][NCHRS], struct fsm_trans ttab[], int nstates);
+
+ //actions
+ int tcdm(int);
+ int recopt(int);
+ int no_op(int);
+ int do_echo(int);
+ int do_notsup(int);
+ int do_noga(int);
+ int do_txbinary(int);
+ int will_notsup(int);
+ int will_txbinary(int);
+ int will_termtype(int);
+ int will_naws(int);
+ int subopt(int);
+ int subtermtype(int);
+ int subend(int);
+ int soputc(int);
+ int ttputc(int);
+ int tnabort(int);
+
+ //utility functions
+ int xputc_up(char);
+ int xputs_up(char*);
+ void putc_down(u_char);
+
+signals:
+
+ void onSSHAuthOK();
+
+ private:
+ // Boolean Flags
+ char synching, doecho, sndbinary, rcvbinary;
+ char noga;
+ char naws;
+ char server_sent_do_naws;
+ u_char option_cmd; // has value WILL, WONT, DO, or DONT
+
+ char termtype; // non-zero if received "DO TERMTYPE"
+ char *term; // terminal name
+
+ /* // TODO: BBS don't need control signals
+ // Special keys - Terminal control characters
+ // need work...
+ static const char t_flushc=FQTERM_CTRL('S'); // Abort Output i.e:(^S)
+ static const char t_intrc=FQTERM_CTRL('C'); // Interrupt i.e:(^C)
+ static const char t_quitc=FQTERM_CTRL('\\'); // Quit i.e:(^\)
+ static const char sg_erase=FQTERM_CTRL('?'); // Erase a character i.e:(^?)
+ static const char sg_kill=FQTERM_CTRL('U'); // Kill a line i.e:(^U)
+ */
+
+ // FSM stuffs
+ static struct fsm_trans ttstab[];
+ int ttstate;
+ u_char ttfsm[NTSTATES][NCHRS];
+
+ static struct fsm_trans substab[];
+ int substate;
+ u_char subfsm[NSSTATES][NCHRS];
+
+ // socket stuffs
+ FQTermSocket *socket;
+
+ //Pointers to internal buffers
+ //
+ // |-->from_socket-->process-->to_ansi-->|
+ // socket<---> <---> ansi decode
+ // |<---to_socket<--process<--from_ansi--|
+ //
+ QByteArray from_socket, to_ansi, from_ansi, to_socket;
+ uint rsize; // size of to_ansi buffer
+ uint wsize; // size of to_socket buffer
+
+ // for test
+ int wx, wy;
+ int done_naws;
+ bool bConnected;
+ int raw_size;
+ int hostType_;
+ int protocolType_;
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_TELNET_H
diff --git a/src/terminal/internal/fqterm_zmodem.cpp b/src/terminal/internal/fqterm_zmodem.cpp
new file mode 100644
index 0000000..afb99c9
--- /dev/null
+++ b/src/terminal/internal/fqterm_zmodem.cpp
@@ -0,0 +1,3056 @@
+/***************************************************************************
+ * 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 <QApplication>
+#include <QString>
+#include <QFileDialog>
+#include <QFileInfo>
+
+#include "fqterm.h"
+#include "fqterm_config.h"
+#include "fqterm_param.h"
+#include "fqterm_path.h"
+#include "fqterm_telnet.h"
+#include "fqterm_zmodem.h"
+#include "fqterm_filedialog.h"
+
+#ifdef FQTERM_ZMODEM_DEBUG
+#include <sys/time.h>
+#endif
+
+namespace FQTerm {
+
+static const uchar zeros[4] = {
+ 0, 0, 0, 0
+};
+
+static const char hexChars[] = "0123456789abcdef";
+
+static const uchar AckStr[1] = {
+ ACK
+};
+static const uchar NakStr[1] = {
+ NAK
+};
+static const uchar CanStr[2] = {
+ CAN, CAN
+};
+static const uchar eotstr[1] = {
+ EOT
+};
+
+
+/*
+ * Crc calculation stuff
+ */
+
+/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
+unsigned short crctab[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129,
+ 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273,
+ 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a,
+ 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6,
+ 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf,
+ 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695,
+ 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc,
+ 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4,
+ 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf,
+ 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5,
+ 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a,
+ 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32,
+ 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59,
+ 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9,
+ 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290,
+ 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8,
+ 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481,
+ 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f,
+ 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676,
+ 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a,
+ 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75,
+ 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f,
+ 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64,
+ 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c,
+ 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93,
+ 0x3eb2, 0x0ed1, 0x1ef0
+};
+
+/*
+ * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
+ * NOTE: First argument must be in range 0 to 255.
+ * Second argument is referenced twice.
+ *
+ * Programmers may incorporate any or all code into their programs,
+ * giving proper credit within the source. Publication of the
+ * source routines is permitted so long as proper credit is given
+ * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
+ * Omen Technology.
+ */
+
+#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
+
+/*
+ * Copyright (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/* First, the polynomial itself and its table of feedback terms. The */
+/* polynomial is */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in */
+/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+/* the MSB being 1. */
+
+/* Note that the usual hardware shift register implementation, which */
+/* is what we're using (we're merely optimizing it by doing eight-bit */
+/* chunks at a time) shifts bits into the lowest-order term. In our */
+/* implementation, that means shifting towards the right. Why do we */
+/* do it this way? Because the calculated CRC must be transmitted in */
+/* order from highest-order term to lowest-order term. UARTs transmit */
+/* characters in order from LSB to MSB. By storing the CRC this way, */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part. Reception works similarly. */
+
+/* The feedback terms table consists of 256, 32-bit entries. Notes: */
+/* */
+/* The table can be generated at runtime if desired; code to do so */
+/* is shown later. It might not be obvious, but the feedback */
+/* terms simply represent the results of eight shift/xor opera- */
+/* tions for all combinations of data and CRC register values. */
+/* */
+/* The values must be right-shifted by eight bits by the "updcrc" */
+/* logic; the shift must be unsigned (bring in zeroes). On some */
+/* hardware you could probably optimize the shift in assembler by */
+/* using byte-swap instructions. */
+
+unsigned long cr3tab[] = {
+ /* CRC polynomial 0xedb88320 */
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+ };
+
+#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF))
+
+#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
+
+
+
+#if 1
+StateTable FQTermZmodem::RStartOps[] = {
+ {ZSINIT, &FQTermZmodem::GotSinit, 0, 1, RSinitWait},
+ /* SINIT, wait for attn str */
+ {ZFILE, &FQTermZmodem::GotFile, 0, 0, RFileName},
+ /* FILE, wait for filename */
+ {ZRQINIT, &FQTermZmodem::SendRinit, 0, 1, RStart},
+ /* sender confused, resend */
+ {ZFIN, &FQTermZmodem::GotFin, 1, 0, RFinish},
+ /* sender shutting down */
+ {ZNAK, &FQTermZmodem::SendRinit, 1, 0, RStart},
+ /* RINIT was bad, resend */
+ {ZFREECNT, &FQTermZmodem::GotFreecnt, 0, 0, RStart},
+ /* sender wants free space */
+ {ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData},
+ /* sender wants command */
+ {ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData},
+ /* sender wants to send msg */
+ {ZRINIT, &FQTermZmodem::ZmodemTInit, 1, 1, TStart},
+ {99, &FQTermZmodem::ZPF, 0, 0, RStart}
+ /* anything else is an error */
+};
+
+StateTable FQTermZmodem::RSinitWaitOps[] = {
+ /* waiting for data */
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, RSinitWait
+ },
+};
+
+StateTable FQTermZmodem::RFileNameOps[] = {
+ /* waiting for file name */
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, RFileName
+ },
+};
+
+StateTable FQTermZmodem::RCrcOps[] = {
+ /* waiting for CRC */
+ {
+ ZCRC, &FQTermZmodem::GotFileCrc, 0, 0, RFile
+ }, { /* sender sent it */
+ ZNAK, &FQTermZmodem::ResendCrcReq, 0, 0, RCrc
+ }, { /* ZCRC was bad, resend */
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RCrc
+ },
+};
+
+
+StateTable FQTermZmodem::RFileOps[] = {
+ /* waiting for ZDATA */
+ {
+ ZDATA, &FQTermZmodem::GotData, 0, 0, RData
+ }, { /* got it */
+ ZNAK, &FQTermZmodem::ResendRpos, 0, 0, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZEOF, &FQTermZmodem::GotEof, 0, 0, RStart
+ }, { /* end of file */
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::ResendRpos, 0, 0, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RFile
+ },
+};
+
+/* waiting for data, but a packet could possibly arrive due
+ * to error recovery or something
+ */
+StateTable FQTermZmodem::RDataOps[] = {
+ {
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ }, { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::GotFile, 0, 1, RFileName
+ }, { /* start a new file (??) */
+ ZNAK, &FQTermZmodem::ResendRpos, 1, 1, RFile
+ }, { /* ZRPOS was bad, resend */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ ZDATA, &FQTermZmodem::GotData, 0, 1, RData
+ }, { /* file data follows */
+ ZEOF, &FQTermZmodem::GotEof, 1, 1, RStart
+ }, { /* end of file */
+ 99, &FQTermZmodem::ZPF, 0, 0, RData
+ },
+};
+
+/* here if we've sent ZFERR or ZABORT. Waiting for ZFIN */
+
+StateTable FQTermZmodem::RFinishOps[] = {
+
+ {
+ ZRQINIT, &FQTermZmodem::SendRinit, 1, 1, RStart
+ } , { /* sender confused, restart */
+ ZFILE, &FQTermZmodem::GotFile, 1, 1, RFileName
+ }, { /* start a new file */
+ ZNAK, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* resend ZFIN */
+ ZFIN, &FQTermZmodem::GotFin, 1, 1, RFinish
+ }, { /* sender signing off */
+ 99, &FQTermZmodem::ZPF, 0, 0, RFinish
+ },
+};
+
+
+/* sent ZRQINIT, waiting for response */
+StateTable FQTermZmodem::TStartOps[] = {
+ {
+ ZRINIT, &FQTermZmodem::GotRinit, 1, 1, TStart
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, TStart
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZNAK, &FQTermZmodem::Ignore, 0, 0, TStart
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TStart
+ },
+};
+
+/* sent ZSINIT, waiting for response */
+StateTable FQTermZmodem::TInitOps[] = {
+ {
+ ZACK, &FQTermZmodem::RetDone, 1, 0, TInit
+ }, {
+ ZNAK, &FQTermZmodem::SendZSInit, 1, 0, TInit
+ }, {
+ ZRINIT, &FQTermZmodem::GotRinit, 1, 1, TInit
+ }, { /* redundant, but who cares */
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, TInit
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TInit
+ },
+};
+
+/* sent ZFILE, waiting for response */
+StateTable FQTermZmodem::FileWaitOps[] = {
+ {
+ ZRPOS, &FQTermZmodem::SendFileData, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 0, TStart
+ }, {
+ ZCRC, &FQTermZmodem::SendFileCrc, 1, 0, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::sendFilename, 1, 0, FileWait
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 1, 0, FileWait
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ ZACK, &FQTermZmodem::SendFileData, 1, 0, Sending
+ }, { // receiver always sends ZACK back
+ 99, &FQTermZmodem::ZPF, 0, 0, FileWait
+ },
+};
+
+/* sent file CRC, waiting for response */
+StateTable FQTermZmodem::CrcWaitOps[] = {
+ {
+ ZRPOS, &FQTermZmodem::SendFileData, 1, 0, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 0, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::SendFileCrc, 1, 0, CrcWait
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZCRC, &FQTermZmodem::SendFileCrc, 0, 0, CrcWait
+ }, {
+ ZCHALLENGE, &FQTermZmodem::AnswerChallenge, 0, 0, CrcWait
+ }, {
+ ZCOMMAND, &FQTermZmodem::GotCommand, 0, 0, CommandData
+ }, {
+ ZSTDERR, &FQTermZmodem::GotStderr, 0, 0, StderrData
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, CrcWait
+ },
+};
+
+/* sending data, interruptable */
+StateTable FQTermZmodem::SendingOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendAck, 0, 0, Sending
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 1, 1, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ },{ /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sent data, need to send EOF */
+StateTable FQTermZmodem::SendDoneOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendDoneAck, 0, 0, SendWait
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, Sending
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 1, 1, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sending data, waiting for ACK */
+StateTable FQTermZmodem::SendWaitOps[] = {
+ {
+ ZACK, &FQTermZmodem::GotSendWaitAck, 0, 0, Sending
+ }, {
+ ZRPOS, &FQTermZmodem::GotSendPos, 0, 0, SendWait
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, FileWait
+ }, {
+ ZNAK, &FQTermZmodem::GotSendNak, 0, 0, Sending
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendWait
+ },
+};
+
+/* sent ZEOF, waiting for new RINIT */
+StateTable FQTermZmodem::SendEofOps[] = {
+ {
+ ZRINIT, &FQTermZmodem::SkipFile, 1, 0, TFinish
+ }, { /* successful completion */
+ ZACK, &FQTermZmodem::Ignore, 0, 0, SendEof
+ }, { /* probably ACK from last packet */
+ ZRPOS, &FQTermZmodem::GotSendPos, 1, 1, SendWait
+ }, {
+ ZSKIP, &FQTermZmodem::SkipFile, 1, 1, TStart
+ }, {
+ ZNAK, &FQTermZmodem::ResendEof, 1, 0, SendEof
+ }, {
+ ZRINIT, &FQTermZmodem::sendFilename, 1, 1, FileWait
+ }, { /* rcvr confused, retry file */
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, SendEof
+ },
+};
+
+StateTable FQTermZmodem::TFinishOps[] = {
+ {
+ ZFIN, &FQTermZmodem::OverAndOut, 1, 1, Done
+ }, {
+ ZNAK, &FQTermZmodem::ZmodemTFinish, 1, 1, TFinish
+ }, {
+ ZRINIT, &FQTermZmodem::ZmodemTFinish, 1, 1, TFinish
+ }, {
+ ZABORT, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ ZFERR, &FQTermZmodem::GotAbort, 1, 1, TFinish
+ }, {
+ 99, &FQTermZmodem::ZPF, 0, 0, TFinish
+ },
+};
+
+StateTable FQTermZmodem::CommandDataOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, CommandData
+ },
+};
+
+StateTable FQTermZmodem::CommandWaitOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, CommandWait
+ },
+};
+
+StateTable FQTermZmodem::StderrDataOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, StderrData
+ },
+};
+
+StateTable FQTermZmodem::DoneOps[] = {
+ {
+ 99, &FQTermZmodem::ZPF, 0, 0, Done
+ },
+};
+
+StateTable *FQTermZmodem::tables[] = {
+ FQTermZmodem::RStartOps, FQTermZmodem::RSinitWaitOps, FQTermZmodem::RFileNameOps,
+ FQTermZmodem::RCrcOps, FQTermZmodem::RFileOps, FQTermZmodem::RDataOps,
+ FQTermZmodem::RDataOps, /* RDataErr is the same as RData */
+ FQTermZmodem::RFinishOps,
+
+ FQTermZmodem::TStartOps, FQTermZmodem::TInitOps, FQTermZmodem::FileWaitOps,
+ FQTermZmodem::CrcWaitOps, FQTermZmodem::SendingOps, FQTermZmodem::SendWaitOps,
+ FQTermZmodem::SendDoneOps, FQTermZmodem::SendEofOps, FQTermZmodem::TFinishOps,
+
+ FQTermZmodem::CommandDataOps, FQTermZmodem::CommandWaitOps,
+ FQTermZmodem::StderrDataOps, FQTermZmodem::DoneOps,
+};
+
+const char *FQTermZmodem::hdrnames[] = {
+ "ZRQINIT", "ZRINIT", "ZSINIT", "ZACK", "ZFILE", "ZSKIP", "ZNAK", "ZABORT",
+ "ZFIN", "ZRPOS", "ZDATA", "ZEOF", "ZFERR", "ZCRC", "ZCHALLENGE", "ZCOMPL",
+ "ZCAN", "ZFREECNT", "ZCOMMAND", "ZSTDERR",
+};
+
+#endif
+
+FQTermZmodem::FQTermZmodem(FQTermConfig *config, FQTermTelnet *netinterface, int type, int serverEncoding) {
+
+ //now set network interface Telnet
+
+ connectionType = type;
+
+ serverEncodingID = serverEncoding;
+
+ switch (connectionType) {
+ case 0:
+ case 1:
+ case 2:
+ telnet_ = netinterface;
+ break;
+ default:
+ FQ_TRACE("zmodem", 0) << "connection type unknown! Expect segmentation fault!";
+ telnet_ = netinterface;
+ break;
+ }
+
+ config_ = config;
+
+ sending = false;
+
+ transferstate = notransfer;
+
+ zmodemTimer = new QTimer(this);
+ FQ_VERIFY(connect(zmodemTimer, SIGNAL(timeout()), this, SLOT(ZmodemTimeout())));
+
+#ifdef FQTERM_ZMODEM_DEBUG
+ zmodemlogfile = fopen("zmodem.log", "w+");
+ fprintf(zmodemlogfile, "%s", "\n================================\n");
+ fclose(zmodemlogfile);
+#endif
+
+
+ //init struct INFO
+ info.zrinitflags = CANFDX | CANOVIO | CANBRK | CANFC32;
+ info.zsinitflags = 0;
+ info.attn = NULL;
+ info.bufsize = 0; /* full streaming */
+ info.buffer = NULL;
+
+ zerrno = 0;
+ lastPullByte = 0;
+
+ ZmodemReset(&info);
+ // other init function not complete
+
+}
+
+FQTermZmodem::~FQTermZmodem(){}
+
+int FQTermZmodem::ZmodemTInit(ZModem *info) {
+ int err;
+ int i;
+
+ info->state = TStart;
+ info->Protocol = ZMODEM;
+ info->crc32 = 0;
+ info->packetCount = 0;
+ info->errCount = 0;
+ info->escCtrl = info->escHibit = info->atSign = info->escape = 0;
+ info->InputState = Idle;
+ info->canCount = info->chrCount = 0;
+ info->windowCount = 0;
+ info->filename = NULL;
+ info->bufsize = 0;
+ info->interrupt = 0;
+ info->waitflag = 0;
+
+ // if( info->packetsize <= 0 )
+ info->packetsize = 8096;
+ info->windowsize = 0;
+ //we won't be receiving much data, pick a reasonable buffer
+ // size (largest packet will do)
+ //
+
+ i = info->packetsize *2;
+
+ if (info->buffer != NULL) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ //since in the constructor function buffer is malloc
+
+ if (i < 1024) {
+ i = 1024;
+ }
+ info->buffer = (uchar*)malloc(i);
+
+ ZIFlush(info);
+
+// FQTermConfig *config = new FQTermConfig(getPath(USER_CONFIG) + "fqterm.cfg");
+// QString strPrevSave = config->getItemValue("global", "previous");
+
+// if (strPrevSave.isEmpty()) {
+// strFileList = QFileDialog::getOpenFileNames(0, "Choose the files",
+// getPath(USER_CONFIG), "All files(*)");
+// } else {
+// strFileList = QFileDialog::getOpenFileNames(0, "Choose the files",
+// strPrevSave, "All files(*)");
+// }
+ FQTermFileDialog fileDialog(config_);
+ strFileList = fileDialog.getOpenNames("Choose a file to upload", "");
+ if (strFileList.count() != 0) {
+ QStringList::Iterator itFile = strFileList.begin();
+ QFileInfo fi(*itFile);
+ }
+
+ this->transferstate = transferstop;
+
+ // optional: send "rz\r" to remote end
+ if (DoInitRZ) {
+ if ((err = ZXmitStr((uchar*)"rz\r", 3, info))) {
+ return err;
+ }
+ }
+
+ if ((err = ZXmitHdr(ZRQINIT, ZHEX, zeros, info))) {
+ // nudge receiver
+ return err;
+ }
+
+ info->timeout = 60;
+
+ zmodemlog("ZmodemTInit[%s]: sent ZRQINIT\n", sname(info));
+
+ return 0;
+}
+
+
+int FQTermZmodem::ZmodemTFile(char *file, char *rfile, uint f0, uint f1, uint f2,
+ uint f3, int filesRem, int bytesRem, ZModem *info) {
+ if (file == NULL || (info->file = fopen(file, "rb")) == NULL) {
+ return ZmErrCantOpen;
+ }
+
+ info->fileEof = 0;
+ info->filename = file;
+ if (rfile != NULL) {
+ info->rfilename = rfile;
+ } else {
+ info->rfilename = strdup("noname");
+ }
+ info->filesRem = filesRem;
+ info->bytesRem = bytesRem;
+ info->fileFlags[3] = f0;
+ info->fileFlags[2] = f1;
+ info->fileFlags[1] = f2;
+ info->fileFlags[0] = f3;
+ info->offset = info->lastOffset = 0;
+ info->len = info->date = info->fileType = info->mode = 0;
+ if (info->filename != NULL) {
+ struct stat buf;
+ if (stat(info->filename, &buf) == 0) {
+ info->len = buf.st_size;
+ info->date = buf.st_mtime;
+ info->fileType = 0;
+ info->mode = (buf.st_mode &0777) | 0100000;
+ }
+ }
+
+ if (info->Protocol == XMODEM) {
+ return YSendData(info);
+ }
+
+ if (info->Protocol == YMODEM) {
+ return YSendFilename(info);
+ }
+
+ info->state = FileWait;
+ ZStatus(FileBegin, info->bytesRem, info->rfilename);
+ zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n", sname(info), info->rfilename);
+ return sendFilename(info);
+}
+
+
+int FQTermZmodem::ZmodemTFinish(ZModem *info) {
+ int i;
+ if (info->Protocol == XMODEM) {
+ return ZmDone;
+ }
+
+ if (info->Protocol == YMODEM) {
+ return YSendFin(info);
+ }
+
+ info->state = TFinish;
+ if (info->buffer != NULL) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+ zmodemlog("ZmodemTFinish[%s]: send ZFIN\n", sname(info));
+ i = ZXmitHdr(ZFIN, ZHEX, zeros, info);
+
+ return i;
+}
+
+
+
+int FQTermZmodem::ZmodemAbort(ZModem *info) {
+ uchar canistr[] = {
+ CAN, CAN, CAN, CAN, CAN, CAN, CAN, CAN, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0
+ };
+ info->state = Done;
+ ZIFlush(info);
+ ZOFlush(info);
+
+ transferstate = transferstop; // transfer complete
+ ZmodemReset(info); //Tranfer complete, zmodem return to receive state
+
+ zmodemlog("ZmodemAbort[%s]: send CAN\n", sname(info));
+ return ZXmitStr(canistr, sizeof(canistr), info);
+}
+
+
+int FQTermZmodem::ZmodemRInit(ZModem *info) {
+ info->packetCount = 0;
+ info->offset = 0;
+ info->errCount = 0;
+ info->escCtrl = info->escHibit = info->atSign = info->escape = 0;
+ info->InputState = Idle;
+ info->canCount = info->chrCount = 0;
+ info->filename = NULL;
+ info->interrupt = 0;
+ info->waitflag = 0;
+ info->attn = NULL;
+ info->file = NULL;
+
+ if (info->buffer != NULL) {
+ free(info->buffer);
+ info->buffer = NULL;
+ }
+
+ info->buffer = (uchar*)malloc(1024);
+
+ info->state = RStart;
+ info->timeoutCount = 0;
+
+ ZIFlush(info);
+
+ /* Don't send ZRINIT right away, there might be a ZRQINIT in
+ * the input buffer. Instead, set timeout to zero and return.
+ * This will allow ZmodemRcv() to check the input stream first.
+ * If nothing found, a ZRINIT will be sent immediately.
+ */
+ info->timeout = 0;
+
+ zmodemlog("ZmodemRInit[%s]: flush input, new state = RStart\n", sname(info));
+
+ return 0;
+}
+
+int FQTermZmodem::ZmodemRcv(uchar *str, int len, ZModem *info,
+ int &consumed_bytes) {
+ register uchar c;
+ int err;
+
+ zmodemlog("zmodemRcv called");
+
+ info->rcvlen = len;
+
+ while (--info->rcvlen >= 0) {
+ c = *str++;
+
+ if (c == CAN) {
+ if (++info->canCount >= 5) {
+ ZStatus(RmtCancel, 0, NULL);
+ consumed_bytes = len - info->rcvlen - 1;
+ return ZmErrCancel;
+ }
+ } else {
+ info->canCount = 0;
+ }
+
+ if (info->InputState == Ysend) {
+ if ((err = YsendChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ } else if (info->InputState == Yrcv) {
+ if ((err = YrcvChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ } else if (c != XON && c != XOFF) {
+ /* now look at what we have */
+ switch (info->InputState) {
+ case Idle:
+ if ((err = IdleChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Inhdr:
+ if ((err = HdrChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Indata:
+ if ((err = DataChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ case Finish:
+ if ((err = FinishChar(c, info))) {
+ consumed_bytes = len - info->rcvlen - 1;
+ return err;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ consumed_bytes = len - info->rcvlen - 1;
+ return 0;
+}
+
+
+
+
+int FQTermZmodem::ZmodemTimeout( /*ZModem *info*/) {
+ /* timed out while waiting for input */
+
+ ++info.timeoutCount;
+
+ zmodemlog("timeout %d [%s]\n", info.timeoutCount, sname(&info));
+
+ switch (info.state) {
+ /* receive */
+ case RStart:
+ /* waiting for INIT frame from other end */
+ if (info.timeoutCount > 4) {
+ return YmodemRInit(&info);
+ }
+
+ case RSinitWait:
+ case RFileName:
+ if (info.timeout > 0) {
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ }
+ if (info.timeoutCount > 4) {
+ return ZmErrRcvTo;
+ }
+ info.state = RStart;
+ return SendRinit(&info);
+
+ case RCrc:
+ case RFile:
+ case RData:
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ if (info.timeoutCount > 2) {
+ info.timeoutCount = 0;
+ info.state = RStart;
+ return SendRinit(&info);
+ }
+ return info.state == RCrc ? ResendCrcReq(&info): ResendRpos(&info);
+
+ case RFinish:
+ ZStatus(SndTimeout, info.timeoutCount, NULL);
+ return ZmDone;
+
+ case YRStart:
+ case YRDataWait:
+ case YRData:
+ case YREOF:
+ return YrcvTimeout(&info);
+
+ /* transmit */
+ case TStart:
+ /* waiting for INIT frame from other end */
+ case TInit:
+ /* sent INIT, waiting for ZACK */
+ case FileWait:
+ /* sent file header, waiting for ZRPOS */
+ case CrcWait:
+ /* sent file crc, waiting for ZRPOS */
+ case SendWait:
+ /* waiting for ZACK */
+ case SendEof:
+ /* sent EOF, waiting for ZACK */
+ case TFinish:
+ /* sent ZFIN, waiting for ZFIN */
+ case YTStart:
+ case YTFile:
+ case YTDataWait:
+ case YTData:
+ case YTEOF:
+ case YTFin:
+ ZStatus(RcvTimeout, 0, NULL);
+ return ZmErrRcvTo;
+
+ case Sending:
+ /* sending data subpackets, ready for int */
+ return SendMoreFileData(&info);
+
+ /* general */
+ case CommandData:
+ /* waiting for command data */
+ case StderrData:
+ /* waiting for stderr data */
+ return ZmErrSndTo;
+ case CommandWait:
+ /* waiting for command to execute */
+ return ZmErrCmdTo;
+ case Done:
+ return ZmDone;
+ default:
+ return 0;
+ }
+}
+
+
+
+int FQTermZmodem::ZmodemAttention(ZModem *info) {
+ /* attention received from remote end */
+ if (info->state == Sending) {
+ ZOFlush(info);
+ info->interrupt = 1;
+ }
+ return 0;
+}
+
+int FQTermZmodem::YmodemTInit(ZModem *info) {
+ info->state = YTStart;
+ info->Protocol = YMODEM;
+ info->errCount = 0;
+ info->InputState = Ysend;
+ info->canCount = info->chrCount = 0;
+ info->windowCount = 0;
+ info->filename = NULL;
+
+ if (info->packetsize != 1024) {
+ info->packetsize = 128;
+ }
+
+ info->buffer = (uchar*)malloc(1024);
+
+ ZIFlush(info);
+ ZFlowControl(0, info);
+
+ info->timeout = 60;
+
+ return 0;
+}
+
+
+int FQTermZmodem::XmodemTInit(ZModem *info) {
+ (void)YmodemTInit(info);
+ info->Protocol = XMODEM;
+ return 0;
+}
+
+
+int FQTermZmodem::YmodemRInit(ZModem *info) {
+ info->errCount = 0;
+ info->InputState = Yrcv;
+ info->canCount = info->chrCount = 0;
+ info->noiseCount = 0;
+ info->filename = NULL;
+ info->file = NULL;
+
+ if (info->buffer == NULL) {
+ info->buffer = (uchar*)malloc(1024);
+ }
+
+ info->state = YRStart;
+ info->packetCount = -1;
+ info->timeoutCount = 0;
+ info->timeout = 10;
+ info->offset = 0;
+
+ ZIFlush(info);
+
+ return ZXmitStr((uchar*)"C", 1, info);
+}
+
+
+int FQTermZmodem::XmodemRInit(ZModem *info) {
+#if 0
+ int err;
+
+ state = Start;
+ protocol = prot;
+ ymodem = prot == Ymodem || prot == YmodemG;
+
+ if (ymodem) {
+ strcpy(xmDefPath, file);
+ } else {
+ strcpy(xmFilename, file);
+ }
+
+ eotCount = errorCount = errorCount2 = 0;
+
+ if (err = XmodemRStart()) {
+ return err;
+ }
+
+ state = Init;
+ packetId = ymodem ? 255 : 0;
+ packetCount = 0;
+
+ pktHdrLen = protocol == Xmodem ? 3 : 4;
+#endif
+ return 0;
+}
+
+
+ulong FQTermZmodem::FileCrc(char *name) {
+ ulong crc;
+ FILE *ifile = fopen(name, "rb");
+ int i;
+
+ if (ifile == NULL)
+ /* shouldn't happen, since we did access(2) */ {
+ return 0;
+ }
+
+ crc = 0xffffffff;
+
+ while ((i = fgetc(ifile)) != EOF) {
+ crc = UPDC32(i, crc);
+ }
+
+ fclose(ifile);
+ return ~crc;
+}
+
+
+
+const char *FQTermZmodem::sname(ZModem *info) {
+ return sname2(info->state);
+}
+
+const char *FQTermZmodem::sname2(ZMState state) {
+ const char *names[] = {
+ "RStart", "RSinitWait", "RFileName", "RCrc", "RFile", "RData", "RDataErr",
+ "RFinish", "TStart", "TInit", "FileWait", "CrcWait", "Sending", "SendWait",
+ "SendDone", "SendEof", "TFinish", "CommandData", "CommandWait", "StderrData",
+ "Done", "YTStart", "YTFile", "YTDataWait", "YTData", "YTEOF", "YTFin",
+ "YRStart", "YRDataWait", "YRData", "YREOF"
+ };
+
+ return names[(int)state];
+
+}
+
+
+int FQTermZmodem::ZXmitChr(uchar c, ZModem *info) {
+ // to be completed
+ return 0;
+}
+
+int FQTermZmodem::ZXmitStr(const uchar *str, int len, ZModem *info) {
+ //to be completed
+ telnet_->write((const char*)str, (uint)len);
+ return 0;
+}
+
+void FQTermZmodem::ZIFlush(ZModem *info) {
+ //to be completed
+}
+
+void FQTermZmodem::ZOFlush(ZModem *info) {
+ //to be completed
+}
+
+int FQTermZmodem::ZAttn(ZModem *info) {
+ //to be completed
+ return 0;
+}
+
+void FQTermZmodem::ZStatus(int type, int value, const char *status) {
+ emit ZmodemState(type, value, status);
+ switch (type) {
+ case RcvByteCount:
+ FQ_TRACE("zmodem", 5) << value << " bytes received.";
+ break;
+ case SndByteCount:
+ FQ_TRACE("zmodem", 5) << value << " bytes sent.";
+ break;
+ case RcvTimeout:
+ /* receiver did not respond, aborting */
+ FQ_TRACE("zmodem", 0) << "Zmodem time out!";
+ break;
+ case SndTimeout:
+ /* value is # of consecutive send timeouts */
+ FQ_TRACE("zmodem", 0) << "Zmodem time out after trying "
+ << value << " times";
+ break;
+ case RmtCancel:
+ /* remote end has cancelled */
+ FQ_TRACE("zmodem", 1) << "Zmodem canceled by remote peer";
+ break;
+ case ProtocolErr:
+ /* protocol error has occurred, val=hdr */
+ FQ_TRACE("zmodem", 0) << "Unhandled header " << value
+ << " at state " << status;
+ break;
+ case RemoteMessage:
+ /* message from remote end */
+ FQ_TRACE("zmodem", 0) << "Message from remote peer: " << status;
+ break;
+ case DataErr:
+ /* data error, val=error count */
+ FQ_TRACE("zmodem", 0) << "Data errors " << value;
+ break;
+ case FileErr:
+ /* error writing file, val=errno */
+ FQ_TRACE("zmodem", 0) << "Falied to write file.";
+ break;
+ case FileBegin:
+ /* file transfer begins, str=name */
+ FQ_TRACE("zmodem", 0) << "Starting file: " << status;
+ break;
+ case FileEnd:
+ /* file transfer ends, str=name */
+ FQ_TRACE("zmodem", 0) << "Finishing file: " << status;
+ break;
+ case FileSkip:
+ /* file being skipped, str=name */
+ FQ_TRACE("zmodem", 0) << "Skipping file: " << status;
+ break;
+ }
+}
+
+FILE *FQTermZmodem::ZOpenFile(char *name, ulong crc, ZModem *info) {
+ //to be complete
+ FILE *rval;
+ int apnd = 0;
+ QString strText;
+ strText = encoding2unicode(name, serverEncodingID);
+ QString str = FQTermPref::getInstance()->zmodemDir_ + strText;
+
+ QString zpath = QFileInfo(FQTermPref::getInstance()->zmodemDir_).absoluteFilePath();
+ QString path = QFileInfo(str).absoluteFilePath();
+
+ lastDownloadedFilename_ = path;
+
+ if (path.startsWith(zpath)) {
+ // Ensure that file is only saved under zmodemDir_.
+ // TODO: lazy, should use bbs2unicode
+ rval = fopen(str.toLocal8Bit(), apnd ? "ab" : "wb");
+ } else {
+ rval = NULL;
+ }
+
+ if (rval == NULL) {
+ perror(name);
+ }
+
+ return rval;
+}
+
+int FQTermZmodem::ZXmitHdr(int type, int format, const uchar data[4], ZModem *info) {
+ if (format == ZBIN && info->crc32) {
+ format = ZBIN32;
+ }
+
+ switch (format) {
+ case ZHEX:
+ return ZXmitHdrHex(type, data, info);
+
+ case ZBIN:
+ return ZXmitHdrBin(type, data, info);
+
+ case ZBIN32:
+ return ZXmitHdrBin32(type, data, info);
+
+ default:
+ return 0;
+ }
+}
+
+
+int FQTermZmodem::ZXmitHdrHex(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ uint crc;
+ int i;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZHEX;
+
+ ptr = putHex(ptr, type);
+ crc = updcrc(type, 0);
+ for (i = 4; --i >= 0; ++data) {
+ ptr = putHex(ptr, *data);
+ crc = updcrc(*data, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putHex(ptr, (crc >> 8) &0xff);
+ ptr = putHex(ptr, crc &0xff);
+ *ptr++ = '\r';
+ *ptr++ = '\n';
+ if (type != ZACK && type != ZFIN) {
+ *ptr++ = XON;
+ }
+
+ return ZXmitStr(buffer, ptr - buffer, info);
+}
+
+
+int FQTermZmodem::ZXmitHdrBin(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ uint crc;
+ int len;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZBIN;
+
+ ptr = putZdle(ptr, type, info);
+ crc = updcrc(type, 0);
+ for (len = 4; --len >= 0; ++data) {
+ ptr = putZdle(ptr, *data, info);
+ crc = updcrc(*data, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putZdle(ptr, (crc >> 8) &0xff, info);
+ ptr = putZdle(ptr, crc &0xff, info);
+
+ len = ptr - buffer;
+ return ZXmitStr(buffer, len, info);
+}
+
+int FQTermZmodem::ZXmitHdrBin32(int type, const uchar data[4], ZModem *info) {
+ uchar buffer[128];
+ uchar *ptr = buffer;
+ ulong crc;
+ int len;
+
+ zmodemlog("sending %s: %2.2x %2.2x %2.2x %2.2x = %lx\n", hdrnames[type],
+ data[0], data[1], data[2], data[3], ZDec4(data));
+
+ *ptr++ = ZPAD;
+ *ptr++ = ZDLE;
+ *ptr++ = ZBIN32;
+ ptr = putZdle(ptr, type, info);
+ crc = UPDC32(type, 0xffffffffL);
+ for (len = 4; --len >= 0; ++data) {
+ ptr = putZdle(ptr, *data, info);
+ crc = UPDC32(*data, crc);
+ }
+ crc = ~crc;
+ for (len = 4; --len >= 0; crc >>= 8) {
+ ptr = putZdle(ptr, crc &0xff, info);
+ }
+
+ len = ptr - buffer;
+ return ZXmitStr(buffer, len, info);
+}
+
+
+uchar *FQTermZmodem::putZdle(uchar *ptr, uchar c, ZModem *info) {
+ uchar c2 = c &0177;
+
+ if (c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 || c2 == 0177 || (c2 ==
+ 015 && connectionType == 0 /*&& info->atSign*/) ||
+#ifdef COMMENT
+ c2 == 035 || (c2 == '~' && info->lastCR) ||
+#endif /* COMMENT */
+ c2 == 035 || (c2 < 040 && info->escCtrl)) {
+ *ptr++ = ZDLE;
+ if (c == 0177) {
+ *ptr = ZRUB0;
+ } else if (c == 0377) {
+ *ptr = ZRUB1;
+ } else {
+ *ptr = c ^ 0100;
+ }
+ } else {
+ *ptr = c;
+ }
+
+ info->atSign = c2 == '@';
+ info->lastCR = c2 == '\r';
+
+ return ++ptr;
+}
+
+
+uchar *FQTermZmodem::ZEnc4(ulong n) {
+ static uchar buf[4];
+ buf[0] = n &0xff;
+ n >>= 8;
+ buf[1] = n &0xff;
+ n >>= 8;
+ buf[2] = n &0xff;
+ n >>= 8;
+ buf[3] = n &0xff;
+ return buf;
+}
+
+ulong FQTermZmodem::ZDec4(const uchar buf[4]) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+
+
+
+int FQTermZmodem::YrcvChar(char c, register ZModem *info) {
+ int err;
+
+ if (info->canCount >= 2) {
+ ZStatus(RmtCancel, 0, NULL);
+ return ZmErrCancel;
+ }
+
+ switch (info->state) {
+ case YREOF:
+ if (c == EOT) {
+ ZCloseFile(info);
+ info->file = NULL;
+ ZStatus(FileEnd, 0, info->filename);
+ if (info->filename != NULL) {
+ free(info->filename);
+ }
+ if ((err = acceptPacket(info)) != 0) {
+ return err;
+ }
+ info->packetCount = -1;
+ info->offset = 0;
+ info->state = YRStart;
+ return ZXmitStr((uchar*)"C", 1, info);
+ }
+ /* else, drop through */
+
+ case YRStart:
+ case YRDataWait:
+ switch (c) {
+ case SOH:
+ case STX:
+ info->pktLen = c == SOH ? (128+4): (1024+4);
+ info->state = YRData;
+ info->chrCount = 0;
+ info->timeout = 1;
+ info->noiseCount = 0;
+ info->crc = 0;
+ break;
+
+ case EOT:
+ /* ignore first EOT to protect against false eot */
+ info->state = YREOF;
+ return rejectPacket(info);
+
+ default:
+ if (++info->noiseCount > 135) {
+ return ZXmitStr(NakStr, 1, info);
+ }
+ break;
+ }
+ break;
+
+ case YRData:
+ info->buffer[info->chrCount++] = c;
+ if (info->chrCount >= info->pktLen) {
+ return ProcessPacket(info);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int FQTermZmodem::YrcvTimeout(ZModem *info) {
+ switch (info->state) {
+ case YRStart:
+ if (info->timeoutCount >= 10) {
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrInitTo;
+ }
+ return ZXmitStr((uchar*)"C", 1, info);
+
+ case YRDataWait:
+ case YREOF:
+ case YRData:
+ if (info->timeoutCount >= 10) {
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrRcvTo;
+ }
+ return ZXmitStr(NakStr, 1, info);
+ default:
+ return 0;
+ }
+}
+
+
+void FQTermZmodem::ZIdleStr(uchar *buffer, int len, ZModem *info) {
+ //to be completed
+}
+
+
+int FQTermZmodem::FinishChar(char c, register ZModem *info) {
+ if (c == 'O') {
+ if (++info->chrCount >= 2) {
+ return ZmDone;
+ }
+ } else {
+ info->chrCount = 0;
+ }
+ return 0;
+}
+
+
+
+int FQTermZmodem::DataChar(uchar c, register ZModem *info) {
+ if (c == ZDLE) {
+ info->escape = 1;
+ return 0;
+ }
+
+ if (info->escape) {
+ info->escape = 0;
+ switch (c) {
+ case ZCRCE:
+ case ZCRCG:
+ case ZCRCQ:
+ case ZCRCW:
+ info->PacketType = c;
+ info->crcCount = (info->DataType == ZBIN32) ? 4 : 2;
+ if (info->DataType == ZBIN) {
+ info->crc = updcrc(c, info->crc);
+ } else {
+ info->crc = UPDC32(c, info->crc);
+ }
+ return 0;
+ case ZRUB0:
+ c = 0177;
+ break;
+ case ZRUB1:
+ c = 0377;
+ break;
+ default:
+ c ^= 0100;
+ break;
+ }
+ }
+ if (connectionType == 0) {
+ if (lastPullByte == 0x0d && c == 0x00) {
+ lastPullByte = 0;
+ return 0;
+ } else if (lastPullByte == 0xff && c == 0xff) {
+ lastPullByte = 0;
+ return 0;
+ }
+ }
+ lastPullByte = c;
+
+ switch (info->DataType) {
+ /* TODO: are hex data packets ever used? */
+ case ZBIN:
+ info->crc = updcrc(c, info->crc);
+ if (info->crcCount == 0) {
+ info->buffer[info->chrCount++] = c;
+ } else if (--info->crcCount == 0) {
+ return ZDataReceived(info, (info->crc &0xffff) == 0);
+ }
+ break;
+
+
+ case ZBIN32:
+ info->crc = UPDC32(c, info->crc);
+ if (info->crcCount == 0) {
+ info->buffer[info->chrCount++] = c;
+ } else if (--info->crcCount == 0) {
+ return ZDataReceived(info, info->crc == 0xdebb20e3);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+int FQTermZmodem::HdrChar(uchar c, register ZModem *info) {
+ int i;
+ int crc = 0;
+
+ if (c == ZDLE) {
+ info->escape = 1;
+ return 0;
+ }
+
+ if (info->escape) {
+ info->escape = 0;
+ switch (c) {
+ case ZRUB0:
+ c = 0177;
+ break;
+ case ZRUB1:
+ c = 0377;
+ break;
+ default:
+ c ^= 0100;
+ break;
+ }
+ }
+
+ if (info->chrCount == 0) {
+ /* waiting for format */
+ switch (c) {
+ case ZHEX:
+ case ZBIN:
+ case ZBIN32:
+ info->DataType = c;
+ info->chrCount = 1;
+ info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL;
+ memset(info->hdrData, 0, sizeof(info->hdrData));
+ break;
+ default:
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+ return 0;
+ }
+
+
+ switch (info->DataType) {
+ /* hex header is 14 hex digits, cr, lf. Optional xon is ignored */
+ case ZHEX:
+ if (info->chrCount <= 14 && !isxdigit(c)) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+
+ if (info->chrCount <= 14) {
+ i = (info->chrCount - 1) / 2;
+ info->hdrData[i] = rcvHex(info->hdrData[i], c);
+ }
+
+ if (info->chrCount == 16) {
+ crc = 0;
+ for (i = 0; i < 7; ++i) {
+ crc = updcrc(info->hdrData[i], crc);
+ }
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if ((crc &0xffff) != 0) {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ else {
+ ++info->chrCount;
+ }
+ break;
+
+
+ case ZBIN:
+ /* binary header is type, 4 bytes data, 2 bytes CRC */
+ info->hdrData[info->chrCount - 1] = c;
+ info->crc = updcrc(c, info->crc);
+ if (++info->chrCount > 7) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if ((crc &0xffff) != 0) {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ break;
+
+
+ case ZBIN32:
+ /* binary32 header is type, 4 bytes data, 4 bytes CRC */
+ info->hdrData[info->chrCount - 1] = c;
+ info->crc = UPDC32(c, info->crc);
+ if (++info->chrCount > 9) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ if (info->crc != 0xdebb20e3)
+ /* see note below */ {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ } else {
+ return ZProtocol(info);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+
+
+
+int FQTermZmodem::IdleChar(uchar c, register ZModem *info) {
+ if (info->chrCount == 0) {
+ if (c == ZPAD) {
+ ++info->chrCount;
+ } else if (info->state == Sending && ++info->noiseCount > MaxNoise) {
+ info->waitflag = 1;
+ } else if (info->state == TStart && (c == 'C' || c == 'G' || c == NAK)) {
+ /* switch to ymodem */
+ info->state = YTStart;
+ info->InputState = Ysend;
+ info->Protocol = YMODEM;
+ return YsendChar(c, info);
+ } else {
+ ZIdleStr(&c, 1, info);
+ }
+ }
+
+ else {
+ switch (c) {
+ case ZPAD:
+ ++info->chrCount;
+ break;
+ case ZDLE:
+ info->InputState = Inhdr;
+ info->chrCount = 0;
+ break;
+ default:
+ while (--info->chrCount >= 0) {
+ ZIdleStr((uchar*)"*", 1, info);
+ }
+ info->chrCount = 0;
+ break;
+ }
+ }
+ return 0;
+}
+
+
+
+int FQTermZmodem::YsendChar(char c, ZModem *info) {
+ int err;
+
+ if (info->canCount >= 2) {
+ ZStatus(RmtCancel, 0, NULL);
+ return ZmErrCancel;
+ }
+
+ switch (info->state) {
+ case YTStart:
+ /* wait for 'G', 'C' or NAK */
+ switch (c) {
+ case 'G':
+ /* streaming YModem */
+ case 'C':
+ /* CRC YModem */
+ case NAK:
+ /* checksum YModem */
+ info->PacketType = c;
+ return ZmDone;
+ default:
+ return 0;
+ }
+
+ case YTFile:
+ /* sent filename, waiting for ACK or NAK */
+ switch (c) {
+ case NAK:
+ /* resend */
+ case 'C':
+ case 'G':
+ ZStatus(DataErr, ++info->errCount, NULL);
+ return YSendFilename(info);
+ case ACK:
+ info->state = YTDataWait;
+ default:
+ return 0;
+ }
+
+ case YTDataWait:
+ /* sent filename, waiting for G,C or NAK */
+ switch (c) {
+ case NAK:
+ case 'C':
+ case 'G':
+ info->chrCount = 0;
+ if (info->PacketType == 'G') {
+ /* send it all at once */
+ while (info->state == YTData) {
+ if ((err = YSendData(info))) {
+ return err;
+ }
+ }
+ return 0;
+ } else {
+ return YSendData(info);
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ case YTData:
+ /* sent data, waiting for ACK or NAK */
+ switch (c) {
+ case 'C':
+ case 'G':
+ /* protocol failure, resend filename */
+ if (info->Protocol == YMODEM) {
+ ZStatus(DataErr, ++info->errCount, NULL);
+ info->state = YTFile;
+ rewind(info->file);
+ return YSendFilename(info);
+ }
+ /* else XModem, treat it like a NAK */
+ case NAK:
+ ZStatus(DataErr, ++info->errCount, NULL);
+ return YXmitData(info->buffer + info->bufp, info->ylen, info);
+ case ACK:
+ info->offset += info->ylen;
+ info->bufp += info->ylen;
+ info->chrCount -= info->ylen;
+ ZStatus(SndByteCount, info->offset, NULL);
+ return YSendData(info);
+ default:
+ return 0;
+ }
+
+ case YTEOF:
+ /* sent EOF, waiting for ACK or NAK */
+ switch (c) {
+ case NAK:
+ return ZXmitStr(eotstr, 1, info);
+ case ACK:
+ info->state = info->Protocol == YMODEM ? YTStart : Done;
+ return ZmDone;
+ default:
+ return 0;
+ }
+
+ case YTFin:
+ /* sent Fin, waiting for ACK or NAK */
+ switch (c) {
+ case NAK:
+ return YSendFin(info);
+ case ACK:
+ return ZmDone;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+
+
+int FQTermZmodem::ZProtocol(register ZModem *info) {
+ register StateTable *table;
+
+ zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
+ hdrnames[info->hdrData[0]], info->hdrData[1], info->hdrData[2],
+ info->hdrData[3], info->hdrData[4], ZDec4(info->hdrData + 1));
+
+ /* Flags are sent in F3 F2 F1 F0 order. Data is sent in P0 P1 P2 P3 */
+
+ info->timeoutCount = 0;
+ info->noiseCount = 0;
+
+ // zmodemTimer->start(info->timeout*1000);
+
+ table = tables[(int)info->state];
+ while (table->type != 99 && table->type != info->hdrData[0]) {
+ ++table;
+ }
+
+ zmodemlog(" state %s => %s, iflush=%d, oflush=%d, call %x\n", sname(info),
+ sname2(table->newstate), table->IFlush, table->OFlush, table->func);
+
+ info->state = table->newstate;
+
+ if (table->IFlush) {
+ info->rcvlen = 0;
+ ZIFlush(info);
+ }
+ if (table->OFlush) {
+ ZOFlush(info);
+ }
+ return (this->*(table->func))(info);
+}
+
+
+int FQTermZmodem::ZDataReceived(register ZModem *info, int crcGood) {
+ switch (info->state) {
+ case RSinitWait:
+ return GotSinitData(info, crcGood);
+ case RFileName:
+ return GotFileName(info, crcGood);
+ case RData:
+ return GotFileData(info, crcGood);
+ case CommandData:
+ return GotCommandData(info, crcGood);
+ case StderrData:
+ return GotStderrData(info, crcGood);
+ default:
+ return ZPF(info);
+ }
+}
+
+
+int FQTermZmodem::ZPF(ZModem *info) {
+ info->waitflag = 1; /* pause any in-progress transmission */
+ ZStatus(ProtocolErr, info->hdrData[0], sname(info));
+ return 0;
+}
+
+
+int FQTermZmodem::Ignore(ZModem *info) {
+ return 0;
+}
+
+
+int FQTermZmodem::AnswerChallenge(register ZModem *info) {
+ return ZXmitHdrHex(ZACK, info->hdrData + 1, info);
+}
+
+
+int FQTermZmodem::GotAbort(register ZModem *info) {
+ ZStatus(RmtCancel, 0, NULL);
+ return ZXmitHdrHex(ZFIN, zeros, info);
+}
+
+
+int FQTermZmodem::GotCancel(ZModem *info) {
+ return ZmErrCancel;
+}
+
+
+int FQTermZmodem::GotCommand(ZModem *info) {
+ uchar rbuf[4];
+ /* TODO: add command capability */
+
+
+ /// EPERM not defined????????
+
+ rbuf[0] = EPERM;
+ rbuf[1] = rbuf[2] = rbuf[3] = 0;
+ return ZXmitHdrHex(ZCOMPL, rbuf, info);
+}
+
+int FQTermZmodem::GotStderr(register ZModem *info) {
+ info->InputState = Indata;
+ info->chrCount = 0;
+ return 0;
+}
+
+
+int FQTermZmodem::RetDone(ZModem *info) {
+
+
+ return ZmDone;
+}
+
+
+int FQTermZmodem::GotCommandData(register ZModem *info, int crcGood) {
+ /* TODO */
+ return 0;
+}
+
+
+int FQTermZmodem::GotStderrData(register ZModem *info, int crcGood) {
+ info->buffer[info->chrCount] = '\0';
+ ZStatus(RemoteMessage, info->chrCount, (char*)info->buffer);
+ return 0;
+}
+
+
+int FQTermZmodem::GotFileName(ZModem *info, int crcGood) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+
+ if (!crcGood) {
+ zmodemlog("GotFileName[%s]: bad crc, send ZNAK\n", sname(info));
+ info->state = RStart;
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+
+ parseFileName(info, (char*)info->buffer);
+
+ if ((info->f1 &ZMMASK) == ZMCRC) {
+ info->state = RCrc;
+ return ZXmitHdrHex(ZCRC, zeros, info);
+ }
+
+ zmodemlog("GotFileName[%s]: good crc, call requestFile\n", sname(info));
+ info->state = RFile;
+ return requestFile(info, 0);
+}
+
+
+int FQTermZmodem::ResendCrcReq(ZModem *info) {
+ zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info));
+ return ZXmitHdrHex(ZCRC, zeros, info);
+}
+
+
+int FQTermZmodem::GotSinitData(ZModem *info, int crcGood) {
+ info->InputState = Idle;
+ info->chrCount = 0;
+ info->state = RStart;
+
+ zmodemlog("GotSinitData[%s]: crcGood=%d\n", sname(info), crcGood);
+
+ if (!crcGood) {
+ return ZXmitHdrHex(ZNAK, zeros, info);
+ }
+
+ if (info->attn != NULL) {
+ free(info->attn);
+ }
+ info->attn = NULL;
+ if (info->buffer[0] != '\0') {
+ info->attn = strdup((char*)info->buffer);
+ }
+ return ZXmitHdrHex(ZACK, ZEnc4(SerialNo), info);
+}
+
+
+int FQTermZmodem::ResendRpos(ZModem *info) {
+ zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n", sname(info), info->offset);
+ return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info);
+}
+
+
+int FQTermZmodem::GotFileData(ZModem *info, int crcGood) {
+ /* OK, now what? Fushing the buffers and executing the
+ * attn sequence has likely chopped off the input stream
+ * mid-packet. Now we switch to idle mode and treat all
+ * incoming stuff like noise until we get a new valid
+ * packet.
+ */
+
+ if (!crcGood) {
+ /* oh bugger, an error. */
+ zmodemlog("GotFileData[%s]: bad crc, send ZRPOS(%ld), new state = RFile\n",
+ sname(info), info->offset);
+ ZStatus(DataErr, ++info->errCount, NULL);
+ if (info->errCount > MaxErrs) {
+ ZmodemAbort(info);
+ return ZmDataErr;
+ } else {
+ info->state = RFile;
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return fileError(info, ZRPOS, info->offset);
+ }
+ }
+
+ if (ZWriteFile(info->buffer, info->chrCount, info->file, info)) {
+ /* RED ALERT! Could not write the file. */
+ ZStatus(FileErr, zerrno, NULL);
+ info->state = RFinish;
+ info->InputState = Idle;
+ info->chrCount = 0;
+ return fileError(info, ZFERR, zerrno);
+ }
+
+ zmodemlog("GotFileData[%s]: %ld.%d,", sname(info), info->offset,
+ info->chrCount);
+ info->offset += info->chrCount;
+ ZStatus(RcvByteCount, info->offset, NULL);
+
+ /* if this was the last data subpacket, leave data mode */
+ if (info->PacketType == ZCRCE || info->PacketType == ZCRCW) {
+ zmodemlog(" ZCRCE|ZCRCW, new state RFile");
+ info->state = RFile;
+ info->InputState = Idle;
+ info->chrCount = 0;
+ } else {
+ zmodemlog(" call dataSetup");
+ (void)dataSetup(info);
+ }
+
+ if (info->PacketType == ZCRCQ || info->PacketType == ZCRCW) {
+ zmodemlog(", send ZACK\n");
+ return ZXmitHdrHex(ZACK, ZEnc4(info->offset), info);
+ } else {
+ zmodemlog("\n");
+ }
+
+ return 0;
+}
+
+int FQTermZmodem::SendRinit(ZModem *info) {
+ uchar dbuf[4];
+
+
+
+#ifdef COMMENT
+ if (info->timeoutCount >= 5)
+ /* TODO: switch to Ymodem */
+#endif /* COMMENT */
+ {
+ transferstate = transferstart;
+ }
+ //transfer would be active, it must be set to false when transfer complete or abort
+ zmodemlog("SendRinit[%s]: send ZRINIT\n", sname(info));
+
+ info->timeout = ResponseTime;
+ dbuf[0] = info->bufsize &0xff;
+ dbuf[1] = (info->bufsize >> 8) &0xff;
+ dbuf[2] = 0;
+ dbuf[3] = info->zrinitflags;
+ return ZXmitHdrHex(ZRINIT, dbuf, info);
+}
+
+
+int FQTermZmodem::SendMoreFileData(ZModem *info) {
+ int type;
+ int qfull = 0;
+ int err;
+ int len; /* max # chars to send this packet */
+ long pending; /* # of characters sent but not acknowledged */
+
+ /* ZCRCE: CRC next, frame ends, header follows
+ * ZCRCG: CRC next, frame continues nonstop
+ * ZCRCQ: CRC next, send ZACK, frame continues nonstop
+ * ZCRCW: CRC next, send ZACK, frame ends, header follows
+ */
+
+ if (info->interrupt) {
+ /* Bugger, receiver sent an interrupt. Enter a wait state
+ * and see what they want. Next header *should* be ZRPOS.
+ */
+ info->state = SendWait;
+ info->timeout = 60;
+ return 0;
+ }
+
+ /* Find out how many bytes we can transfer in the next packet */
+
+ len = info->packetsize;
+
+ pending = info->offset - info->lastOffset;
+
+ if (info->windowsize != 0 && info->windowsize - pending <= len) {
+ len = info->windowsize - pending;
+ qfull = 1;
+ }
+ if (info->bufsize != 0 && info->bufsize - pending <= len) {
+ len = info->bufsize - pending;
+ qfull = 1;
+ }
+
+ if (len == 0) {
+ /* window still full, keep waiting */
+ info->state = SendWait;
+ info->timeout = 60;
+ return 0;
+ }
+
+
+ /* OK, we can safely transmit 'len' bytes of data. Start reading
+ * file until buffer is full.
+ */
+
+ len -= 10; /* Pre-deduct 10 bytes for trailing CRC */
+
+
+ /* find out what kind of packet to send */
+ if (info->waitflag) {
+ type = ZCRCW;
+ info->waitflag = 0;
+ }
+#ifdef COMMENT
+ else if (info->fileEof) {
+ type = ZCRCE;
+ }
+#endif /* COMMENT */
+ else if (qfull) {
+ type = ZCRCW;
+ } else {
+ switch (info->Streaming) {
+ case Full:
+ case Segmented:
+ type = ZCRCG;
+ break;
+
+ case StrWindow:
+ if ((info->windowCount += len) < info->windowsize / 4) {
+ type = ZCRCG;
+ } else {
+ type = ZCRCQ;
+ info->windowCount = 0;
+ }
+ break;
+
+ default:
+ case SlidingWindow:
+ type = ZCRCQ;
+ break;
+ }
+ }
+
+ {
+ int crc32 = info->crc32;
+ int c = 0, c2, atSign = 0;
+ ulong crc;
+ uchar *ptr = info->buffer;
+
+ crc = crc32 ? 0xffffffff : 0;
+
+ /* read characters from file and put into buffer until buffer is
+ * full or file is exhausted
+ */
+
+
+ while (len > 0 && (c = getc(info->file)) != EOF) {
+ if (!crc32) {
+ crc = updcrc(c, crc);
+ } else {
+ crc = UPDC32(c, crc);
+ }
+
+ /* zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and
+ * a CR following '@' be escaped. In addition, I escape '^]'
+ * to protect telnet, "<CR>~." to protect rlogin, and ESC for good
+ * measure.
+ */
+ c2 = c &0177;
+ if (c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 || c2 == 0177 || c2 ==
+ '\r' || c2 == '\n' || c2 == 033 || c2 == 035 || (c2 < 040 && info->escCtrl)) {
+ *ptr++ = ZDLE;
+ if (c == 0177) {
+ *ptr = ZRUB0;
+ } else if (c == 0377) {
+ *ptr = ZRUB1;
+ } else {
+ *ptr = c ^ 0100;
+ }
+ len -= 2;
+ } else {
+ *ptr = c;
+ --len;
+ }
+ ++ptr;
+
+ atSign = c2 == '@';
+ ++info->offset;
+ }
+
+ /* if we've reached file end, a ZEOF header will follow. If
+ * there's room in the outgoing buffer for it, end the packet
+ * with ZCRCE and append the ZEOF header. If there isn't room,
+ * we'll have to do a ZCRCW
+ */
+ if ((info->fileEof = (c == EOF))) {
+ if (qfull || (info->bufsize != 0 && len < 24)) {
+ type = ZCRCW;
+ } else {
+ type = ZCRCE;
+ }
+ }
+
+ *ptr++ = ZDLE;
+ if (!crc32) {
+ crc = updcrc(type, crc);
+ } else {
+ crc = UPDC32(type, crc);
+ }
+ *ptr++ = type;
+
+ if (!crc32) {
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putZdle(ptr, (crc >> 8) &0xff, info);
+ ptr = putZdle(ptr, crc &0xff, info);
+ } else {
+ crc = ~crc;
+ for (len = 4; --len >= 0; crc >>= 8) {
+ ptr = putZdle(ptr, crc &0xff, info);
+ }
+ }
+
+ len = ptr - info->buffer;
+ }
+
+ ZStatus(SndByteCount, info->offset, NULL);
+
+ if ((err = ZXmitStr(info->buffer, len, info))) {
+ return err;
+ }
+
+#ifdef COMMENT
+ if ((err = ZXmitData(ZBIN, len, uchar(type), info->buffer, info))) {
+ return err;
+ }
+#endif /* COMMENT */
+
+ /* finally, do we want to wait after this packet? */
+
+ switch (type) {
+ case ZCRCE:
+ info->state = SendEof;
+ info->timeout = 60;
+ return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info);
+ case ZCRCW:
+ info->state = info->fileEof ? SendDone : SendWait;
+ info->timeout = 60;
+ break;
+ default:
+ info->state = Sending;
+ info->timeout = 0;
+ break;
+ }
+
+
+#ifdef COMMENT
+ if (info->fileEof) {
+ /* Yes, file is done, send EOF and wait */
+ info->state = SendEof;
+ info->timeout = 60;
+ return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info);
+ } else if (type == ZCRCW) {
+ info->state = SendWait;
+ info->timeout = 60;
+ } else {
+ info->state = Sending;
+ info->timeout = 0;
+ }
+#endif /* COMMENT */
+ return 0;
+}
+
+
+uint FQTermZmodem::rcvHex(uint i, char c) {
+ if (c <= '9') {
+ c -= '0';
+ } else if (c <= 'F') {
+ c -= 'A' - 10;
+ } else {
+ c -= 'a' - 10;
+ }
+ return (i << 4) + c;
+}
+
+
+int FQTermZmodem::dataSetup(register ZModem *info) {
+ info->InputState = Indata;
+ info->chrCount = 0;
+ info->crcCount = 0;
+ info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL;
+ return 0;
+}
+
+int FQTermZmodem::ZWriteFile(uchar *buffer, int len, FILE *file, ZModem*) {
+ return (int)fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys;
+}
+
+int FQTermZmodem::ZCloseFile(ZModem *info) {
+ //to be completed
+ fclose(info->file);
+ return 0;
+}
+
+void FQTermZmodem::ZFlowControl(int onoff, ZModem *info) {
+ //to be completed
+}
+
+int FQTermZmodem::GotSinit(ZModem *info) {
+ zmodemlog("GotSinit[%s]: call dataSetup\n", sname(info));
+
+ info->zsinitflags = info->hdrData[4];
+ info->escCtrl = info->zsinitflags &TESCCTL;
+ info->escHibit = info->zsinitflags &TESC8;
+ ZFlowControl(1, info);
+ return dataSetup(info);
+}
+
+int FQTermZmodem::GotFile(ZModem *info) {
+ zmodemlog("GotFile[%s]: call dataSetup\n", sname(info));
+
+ info->errCount = 0;
+ info->f0 = info->hdrData[4];
+ info->f1 = info->hdrData[3];
+ info->f2 = info->hdrData[2];
+ info->f3 = info->hdrData[1];
+ return dataSetup(info);
+}
+
+int FQTermZmodem::GotFin(ZModem *info) {
+ int i;
+ zmodemlog("GotFin[%s]: send ZFIN\n", sname(info));
+ info->InputState = Finish;
+ info->chrCount = 0;
+ if (info->filename != NULL) {
+ free(info->filename);
+ }
+ i = ZXmitHdrHex(ZFIN, zeros, info);
+ ZmodemReset(info);
+ transferstate = transferstop; // transfer complete
+ return i;
+}
+
+
+int FQTermZmodem::GotData(ZModem *info) {
+ int err;
+
+ zmodemlog("GotData[%s]:\n", sname(info));
+
+ if (ZDec4(info->hdrData + 1) != info->offset) {
+ if (info->attn != NULL && (err = ZAttn(info)) != 0) {
+ return err;
+ }
+ zmodemlog(" bad, send ZRPOS(%ld)\n", info->offset);
+ return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info);
+ }
+
+ /* Let's do it! */
+ zmodemlog(" call dataSetup\n");
+ return dataSetup(info);
+}
+
+int FQTermZmodem::GotEof(ZModem *info) {
+ zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset);
+ if (ZDec4(info->hdrData + 1) != info->offset) {
+ zmodemlog("zdec4(info->hdrdata+1)=%ld\n", ZDec4(info->hdrData + 1));
+ zmodemlog(" bad length, state = RFile\n");
+ info->state = RFile;
+ return 0; /* it was probably spurious */
+ }
+
+ /* TODO: if we can't close the file, send a ZFERR */
+
+ ZCloseFile(info);
+ info->file = NULL;
+ ZStatus(FileEnd, 0, info->filename);
+ if (info->filename != NULL) {
+ free(info->filename);
+ info->filename = NULL;
+ }
+ return SendRinit(info);
+}
+
+int FQTermZmodem::GotFreecnt(ZModem *info) {
+ /* TODO: how do we find free space on system? */
+ return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info);
+}
+
+
+int FQTermZmodem::GotFileCrc(ZModem *info) {
+ zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info));
+ return requestFile(info, ZDec4(info->hdrData + 1));
+}
+
+int FQTermZmodem::requestFile(ZModem *info, ulong crc) {
+ info->file = ZOpenFile((char*)info->buffer, crc, info);
+
+ if (info->file == NULL) {
+ zmodemlog("requestFile[%s]: send ZSKIP\n", sname(info));
+
+ info->state = RStart;
+ ZStatus(FileSkip, 0, info->filename);
+ return ZXmitHdrHex(ZSKIP, zeros, info);
+ } else {
+ zmodemlog("requestFile[%s]: send ZRPOS(%ld)\n", sname(info), info->offset);
+ info->offset = info->f0 == ZCRESUM ? ftell(info->file): 0;
+ info->state = RFile;
+ ZStatus(FileBegin, info->len, info->filename);
+ return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info);
+ }
+}
+
+void FQTermZmodem::parseFileName(ZModem *info, char *fileinfo) {
+ char *ptr;
+ int serial = 0;
+
+ info->len = info->mode = info->filesRem = info->bytesRem = info->fileType = 0;
+ ptr = fileinfo + strlen(fileinfo) + 1;
+ if (info->filename != NULL) {
+ free(info->filename);
+ }
+ info->filename = strdup(fileinfo);
+ sscanf(ptr, "%d %lo %o %o %d %d %d", &info->len, &info->date, &info->mode,
+ &serial, &info->filesRem, &info->bytesRem, &info->fileType);
+}
+
+
+
+int FQTermZmodem::fileError(ZModem *info, int type, int data) {
+ int err;
+
+ info->InputState = Idle;
+ info->chrCount = 0;
+
+ if (info->attn != NULL && (err = ZAttn(info)) != 0) {
+ return err;
+ }
+ return ZXmitHdrHex(type, ZEnc4(data), info);
+}
+
+
+int FQTermZmodem::ProcessPacket(ZModem *info) {
+ int idx = (uchar)info->buffer[0];
+ int idxc = (uchar)info->buffer[1];
+ int crc0, crc1;
+ int err;
+
+ info->state = YRDataWait;
+
+ if (idxc != 255-idx) {
+ ZStatus(DataErr, ++info->errCount, NULL);
+ return rejectPacket(info);
+ }
+
+ if (idx == (info->packetCount % 256))
+ /* quietly ignore dup */ {
+ return acceptPacket(info);
+ }
+
+ if (idx != (info->packetCount + 1) % 256) {
+ /* out of sequence */
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrSequence;
+ }
+
+ crc0 = (uchar)info->buffer[info->pktLen - 2] << 8
+ | (uchar)info->buffer[info->pktLen - 1];
+ crc1 = calcCrc(info->buffer + 2, info->pktLen - 4);
+ if (crc0 != crc1) {
+ ZStatus(DataErr, ++info->errCount, NULL);
+ return rejectPacket(info);
+ }
+
+ ++info->packetCount;
+
+ if (info->packetCount == 0) {
+ /* packet 0 is filename */
+ if (info->buffer[2] == '\0') {
+ /* null filename is FIN */
+ (void)acceptPacket(info);
+ return ZmDone;
+ }
+
+ parseFileName(info, (char*)info->buffer + 2);
+ info->file = ZOpenFile(info->filename, 0, info);
+ if (info->file == NULL) {
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrCantOpen;
+ }
+ if ((err = acceptPacket(info)) != 0) {
+ return err;
+ }
+ return ZXmitStr((uchar*)"C", 1, info);
+ }
+
+
+ if (ZWriteFile(info->buffer + 2, info->pktLen - 4, info->file, info)) {
+ ZStatus(FileErr, zerrno, NULL);
+ (void)ZXmitStr(CanStr, 2, info);
+ return ZmErrSys;
+ }
+ info->offset += info->pktLen - 4;
+ ZStatus(RcvByteCount, info->offset, NULL);
+
+ (void)acceptPacket(info);
+ return 0;
+}
+
+int FQTermZmodem::rejectPacket(ZModem *info) {
+ info->timeout = 10;
+ return ZXmitStr(NakStr, 1, info);
+}
+
+
+int FQTermZmodem::acceptPacket(ZModem *info) {
+ info->state = YRDataWait;
+ info->timeout = 10;
+ return ZXmitStr(AckStr, 1, info);
+}
+
+
+int FQTermZmodem::calcCrc(uchar *str, int len) {
+ int crc = 0;
+ while (--len >= 0) {
+ crc = updcrc(*str++, crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ return crc &0xffff;
+}
+
+
+char *FQTermZmodem::strdup(const char *str) {
+ char *rval;
+ int len = strlen(str) + 1;
+ rval = (char*)malloc(len);
+ strcpy(rval, str);
+ return rval;
+}
+
+
+int FQTermZmodem::ZXmitData(int format, int len, uchar term, uchar *data, ZModem
+ *info) {
+ uchar *ptr = info->buffer;
+ uint crc;
+
+ if (format == ZBIN && info->crc32) {
+ format = ZBIN32;
+ }
+
+ zmodemlog("ZXmiteData: fmt=%c, len=%d, term=%c\n", format, len, term);
+
+ crc = (format == ZBIN) ? 0 : 0xffffffff;
+
+ while (--len >= 0) {
+ if (format == ZBIN) {
+ crc = updcrc(*data, crc);
+ } else {
+ crc = UPDC32(*data, crc);
+ }
+ ptr = putZdle(ptr, *data++, info);
+ }
+
+ *ptr++ = ZDLE;
+ if (format == ZBIN) {
+ crc = updcrc(term, crc);
+ } else {
+ crc = UPDC32(term, crc);
+ }
+ *ptr++ = term;
+ if (format == ZBIN) {
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ ptr = putZdle(ptr, (crc >> 8) &0xff, info);
+ ptr = putZdle(ptr, crc &0xff, info);
+ } else {
+ crc = ~crc;
+ for (len = 4; --len >= 0; crc >>= 8) {
+ ptr = putZdle(ptr, crc &0xff, info);
+ }
+ }
+
+ return ZXmitStr(info->buffer, ptr - info->buffer, info);
+}
+
+
+
+int FQTermZmodem::YXmitData(uchar *buffer, int len, ZModem *info) {
+ uchar hdr[3];
+ uchar trail[2];
+ ulong crc = 0;
+ int i, err;
+
+ hdr[0] = len == 1024 ? STX : SOH;
+ hdr[1] = info->packetCount;
+ hdr[2] = ~hdr[1];
+ if ((err = ZXmitStr(hdr, 3, info)) || (err = ZXmitStr(buffer, len, info))) {
+ return err;
+ }
+
+ if (info->PacketType == NAK) {
+ /* checksum */
+ for (i = 0; i < len; ++i) {
+ crc += buffer[i];
+ }
+ trail[0] = crc % 256;
+ return ZXmitStr(trail, 1, info);
+ } else {
+ for (i = 0; i < len; ++i) {
+ crc = updcrc(buffer[i], crc);
+ }
+ crc = updcrc(0, crc);
+ crc = updcrc(0, crc);
+ trail[0] = crc / 256;
+ trail[1] = crc % 256;
+ return ZXmitStr(trail, 2, info);
+ }
+}
+
+
+int FQTermZmodem::YSendFilename(ZModem *info) {
+ int i, len;
+ uchar obuf[1024];
+ uchar *ptr = obuf;
+
+ info->state = info->PacketType != 'G' ? YTFile : YTDataWait;
+ info->packetCount = 0;
+ info->offset = 0;
+
+ i = strlen(info->rfilename);
+ memcpy(ptr, info->rfilename, i + 1);
+ ptr += i + 1;
+ sprintf((char*)ptr, "%d %lo %o 0", info->len, info->date, info->mode);
+ ptr += strlen((char*)ptr);
+ *ptr++ = '\0';
+ /* pad out to 128 bytes or 1024 bytes */
+ i = ptr - obuf;
+ len = i > 128 ? 1024 : 128;
+ for (; i < len; ++i) {
+ *ptr++ = '\0';
+ }
+
+ return YXmitData(obuf, len, info);
+}
+
+int FQTermZmodem::YSendData(ZModem *info) {
+ int i;
+
+ /* are there characters still in the read buffer? */
+
+ if (info->chrCount <= 0) {
+ info->bufp = 0;
+ info->chrCount = fread(info->buffer, 1, info->packetsize, info->file);
+ info->fileEof = feof(info->file);
+ }
+
+ if (info->chrCount <= 0) {
+ fclose(info->file);
+ info->state = YTEOF;
+ return ZXmitStr(eotstr, 1, info);
+ }
+
+ /* pad out to 128 bytes if needed */
+ if (info->chrCount < 128) {
+ i = 128-info->chrCount;
+ memset(info->buffer + info->bufp + info->chrCount, 0x1a, i);
+ info->chrCount = 128;
+ }
+
+ info->ylen = info->chrCount >= 1024 ? 1024 : 128;
+ ++info->packetCount;
+
+ info->state = YTData;
+
+ return YXmitData(info->buffer + info->bufp, info->ylen, info);
+}
+
+
+int FQTermZmodem::YSendFin(ZModem *info) {
+ uchar obuf[128];
+
+ info->state = YTFin;
+ info->packetCount = 0;
+
+ memset(obuf, 0, 128);
+
+ return YXmitData(obuf, 128, info);
+}
+
+int FQTermZmodem::sendFilename(ZModem *info) {
+ int err;
+ int i;
+ uchar obuf[2048];
+ uchar *ptr = obuf;
+
+ info->state = FileWait;
+
+ if ((err = ZXmitHdr(ZFILE, ZBIN, info->fileFlags, info))) {
+ return err;
+ }
+
+ i = strlen(info->rfilename);
+ memcpy(ptr, info->rfilename, i + 1);
+ ptr += i + 1;
+ sprintf((char*)ptr, "%d %lo %o 0 %d %d 0", info->len, info->date, info->mode,
+ info->filesRem, info->bytesRem);
+ ptr += strlen((char*)ptr);
+ *ptr++ = '\0';
+
+ return ZXmitData(ZBIN, ptr - obuf, ZCRCW, obuf, info);
+}
+
+int FQTermZmodem::GotRinit(ZModem *info) {
+ if (strFileList.count() == 0) {
+ return ZmodemTFinish(info);
+ }
+
+
+ info->bufsize = info->hdrData[1] + info->hdrData[2] *256;
+ info->zrinitflags = info->hdrData[4] + info->hdrData[3] *256;
+ info->crc32 = info->zrinitflags &CANFC32;
+ info->escCtrl = info->zrinitflags &ESCCTL;
+ info->escHibit = info->zrinitflags &ESC8;
+
+ /* Full streaming: If receiver can overlap I/O, and if
+ * the sender can sample the reverse channel without hanging,
+ * and the receiver has not specified a buffer size, then we
+ * can simply blast away with ZCRCG packets. If the receiver
+ * detects an error, it sends an attn sequence and a new ZRPOS
+ * header to restart the file where the error occurred.
+ *
+ * [note that zmodem8.doc does not define how much noise is
+ * required to trigger a ZCRCW packet. We arbitrarily choose
+ * 64 bytes]
+ *
+ * If 'windowsize' is nonzero, and the receiver can do full
+ * duplex, ZCRCQ packets are sent instead of ZCRCG, to keep track
+ * of the number of characters in the queue. If the queue fills
+ * up, we pause and wait for a ZACK.
+ *
+ *
+ * Full Streaming with Reverse Interrupt: If sender cannot
+ * sample the input stream, then we define an Attn sequence
+ * that will be used to interrupt transmission.
+ *
+ *
+ * Full Streaming with Sliding Window: If sender cannot
+ * sample input stream or respond to Attn signal, we send
+ * several ZCRCQ packets until we're sure the receiver must
+ * have sent back at least one ZACK. Then we stop sending and
+ * read that ZACK. Then we send one more packet and so on.
+ *
+ *
+ * Segmented Streaming: If receiver cannot overlap I/O or can't do
+ * full duplex and has specified a maximum receive buffer size,
+ * whenever the buffer size is reached, we send a ZCRCW packet.
+ *
+ * TODO: what if receiver can't overlap, but hasn't set a buffer
+ * size?
+ *
+ * ZCRCE: CRC next, frame ends, header follows
+ * ZCRCG: CRC next, frame continues nonstop
+ * ZCRCQ: CRC next, send ZACK, frame continues nonstop
+ * ZCRCW: CRC next, send ZACK, frame ends, header follows
+ */
+
+ transferstate = transferstart;
+
+ ZFlowControl(1, info);
+
+ if ((info->zrinitflags &(CANFDX | CANOVIO)) == (CANFDX | CANOVIO) &&
+ (SendSample || SendAttn) && info->bufsize == 0) {
+ if (info->windowsize == 0) {
+ info->Streaming = Full;
+ } else {
+ info->Streaming = StrWindow;
+ }
+ }
+
+ else if ((info->zrinitflags &(CANFDX | CANOVIO)) == (CANFDX | CANOVIO) &&
+ info->bufsize == 0) {
+ info->Streaming = SlidingWindow;
+ }
+
+ else {
+ info->Streaming = Segmented;
+ }
+ // get filenames to transfer
+ zmodemlog("GotRinit[%s]\n", sname(info));
+
+ if (AlwaysSinit || info->zsinitflags != 0 || info->attn != NULL) {
+ SendZSInit(info);
+ }
+
+
+ itFile = strFileList.begin();
+ QFileInfo fi(*itFile);
+ FQ_TRACE("zmodem", 0) << "Number of files to be transfered: "
+ << strFileList.count();
+ char *filename = strdup(fi.absoluteFilePath().toLatin1());
+ char *rfilename = strdup(fi.fileName().toLatin1());
+ ZmodemTFile(filename, rfilename, 0, 0, 0, 0, strFileList.count(), fi.size(),
+ info);
+ strFileList.erase(itFile);
+ return ZmDone;
+}
+
+
+int FQTermZmodem::SendZSInit(ZModem *info) {
+ char tmp = '\0';
+ int err;
+ //char *at = (info->attn != NULL) ? info->attn : "" ;
+ char *at;
+ if (info->attn != NULL) {
+ at = info->attn;
+ } else {
+ at = &tmp;
+ }
+ uchar fbuf[4];
+
+ /* TODO: zmodem8.doc states: "If the ZSINIT header specifies
+ * ESCCTL or ESC8, a HEX header is used, and the receiver
+ * activates the specified ESC modes before reading the following
+ * data subpacket." What does that mean?
+ */
+
+ zmodemlog("SendZSInit[%s]\n", sname(info));
+
+ info->state = TInit;
+ fbuf[0] = fbuf[1] = fbuf[2] = 0;
+ fbuf[3] = info->zsinitflags;
+ if ((err = ZXmitHdr(ZSINIT, ZBIN, fbuf, info)) || (err = ZXmitData(ZBIN,
+ strlen(at) + 1, ZCRCW, (uchar*)at, info))) {
+ return err;
+ }
+ return 0;
+}
+
+
+int FQTermZmodem::SendFileCrc(ZModem *info) {
+ ulong crc;
+
+ crc = FileCrc(info->filename);
+
+ zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc);
+
+ return ZXmitHdrHex(ZCRC, ZEnc4(crc), info);
+}
+
+int FQTermZmodem::GotSendAck(ZModem *info) {
+ ulong offset;
+
+ offset = ZDec4(info->hdrData + 1);
+
+ if (offset > info->lastOffset) {
+ info->lastOffset = offset;
+ }
+
+ zmodemlog("GotSendAck[%s]: %lx\n", sname(info), info->offset);
+
+ return 0; /* DONT send more data, that will happen
+ * later anyway */
+}
+
+int FQTermZmodem::GotSendDoneAck(ZModem *info) {
+ ulong offset;
+
+ offset = ZDec4(info->hdrData + 1);
+
+ if (offset > info->lastOffset) {
+ info->lastOffset = offset;
+ }
+
+ zmodemlog("GotSendDoneAck[%s]: %ld\n", sname(info), info->offset);
+
+ info->state = SendEof;
+ info->timeout = 60;
+ return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info);
+}
+
+int FQTermZmodem::GotSendNak(ZModem *info) {
+ info->offset = info->zrposOffset;
+
+ fseek(info->file, info->offset, 0);
+
+ /* TODO: what if fseek fails? Send EOF? */
+
+ zmodemlog("GotSendNak[%s]: %ld\n", sname(info), info->offset);
+
+ return SendMoreFileData(info);
+}
+
+int FQTermZmodem::GotSendWaitAck(ZModem *info) {
+
+ ulong offset;
+ int err;
+
+ offset = ZDec4(info->hdrData + 1);
+
+ FQ_TRACE("zmodem", 10) << "last = " << info->lastOffset
+ << ", now = " << offset;
+
+ if (offset > info->lastOffset) {
+ info->lastOffset = offset;
+ }
+
+ //receiver return -1 without setting this flag, kingson 00:07 14-07-04
+ info->waitflag = 1;
+
+ zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset);
+
+ if ((err = ZXmitHdr(ZDATA, ZBIN, info->hdrData + 1, info))) {
+ return err;
+ }
+ return SendMoreFileData(info);
+}
+
+int FQTermZmodem::SkipFile(ZModem *info) {
+ zmodemlog("SkipFile[%s]\n", sname(info));
+ ZStatus(FileEnd, 0, info->rfilename);
+ fclose(info->file);
+
+ // stupid SMTH doesnt send further command, kick
+ // lets send files in the list
+ info->state = TStart;
+ return GotRinit(info);
+}
+
+int FQTermZmodem::GotSendPos(ZModem *info) {
+ ZStatus(DataErr, ++info->errCount, NULL);
+ info->waitflag = 1; /* next pkt should wait, to resync */
+ FQ_TRACE("zmodem", 9) << "GotSendPos, offset = " << info->offset;
+ zmodemlog("GotSendPos[%s] %lx\n", sname(info), info->offset);
+ return startFileData(info);
+}
+
+int FQTermZmodem::SendFileData(ZModem *info) {
+ info->waitflag = 0;
+ return startFileData(info);
+}
+
+int FQTermZmodem::ResendEof(ZModem *info) {
+ return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info);
+}
+
+int FQTermZmodem::OverAndOut(ZModem *info) {
+ zmodemlog("OverAndOut[%s]\n", sname(info));
+
+ ZXmitStr((uchar*)"OO", 2, info);
+
+ transferstate = transferstop; // transfer complete
+
+ ZmodemReset(info); //Tranfer complete, zmodem return to receive state
+
+ return ZmDone;
+}
+
+int FQTermZmodem::startFileData(ZModem *info) {
+ int err;
+
+ info->offset = info->lastOffset = info->zrposOffset = ZDec4(info->hdrData + 1);
+
+ fseek(info->file, info->offset, 0);
+
+ /* TODO: what if fseek fails? Send EOF? */
+
+ zmodemlog("startFileData[%s]: %lx\n", sname(info), info->offset);
+
+ if ((err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info))) {
+ return err;
+ }
+ return SendMoreFileData(info);
+}
+
+uchar *FQTermZmodem::putHex(uchar *ptr, uchar c) {
+ *ptr++ = hexChars[(c >> 4) &0xf];
+ *ptr++ = hexChars[c &0xf];
+ return ptr;
+}
+
+
+int FQTermZmodem::ZmodemReset(ZModem *info) {
+ zmodemlog("\nZmodemReset\n");
+
+ ZmodemRInit(info);
+
+ return 0;
+}
+
+
+void FQTermZmodem::zmodemlog(const char *fmt, ...) {
+ // only for debug
+#ifdef FQTERM_ZMODEM_DEBUG
+ va_list ap;
+ struct timeval tv;
+ struct tm *tm;
+ static int do_ts = 1;
+
+ zmodemlogfile = fopen("zmodem.log", "a");
+
+ if (zmodemlogfile == NULL) {
+ return ;
+ }
+
+ if (do_ts) {
+ gettimeofday(&tv, NULL);
+ tm = localtime(&tv.tv_sec);
+ fprintf(zmodemlogfile, "%2.2d:%2.2d:%2.2d.%2.2ld: ", tm->tm_hour, tm->tm_min,
+ tm->tm_sec, tv.tv_usec / 10000);
+ }
+ do_ts = strchr(fmt, '\n') != NULL;
+
+ va_start(ap, fmt);
+ vfprintf(zmodemlogfile, fmt, ap);
+ va_end(ap);
+
+ fclose(zmodemlogfile);
+#endif
+
+}
+
+void FQTermZmodem::zmodemCancel() {
+ ZmodemAbort(&info);
+}
+
+} // namespace FQTerm
+
+#include "fqterm_zmodem.moc"
+
diff --git a/src/terminal/internal/fqterm_zmodem.h b/src/terminal/internal/fqterm_zmodem.h
new file mode 100644
index 0000000..bd1f4f4
--- /dev/null
+++ b/src/terminal/internal/fqterm_zmodem.h
@@ -0,0 +1,623 @@
+/***************************************************************************
+ * 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. *
+ ***************************************************************************/
+
+#ifndef FQTERM_ZMODEM_H
+#define FQTERM_ZMODEM_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <QByteArray>
+#include <QObject>
+#include <QTimer>
+#include <QFile>
+#include <QStringList>
+
+namespace FQTerm {
+
+/* PARAMETERS
+ *
+ * The following #defines control the behavior of the Zmodem
+ * package. Note that these may be replaced with variables
+ * if you like. For example, "#define DoInitRZ" may be replaced
+ * with "extern int DoInitRz" to use a global variable, or with
+ * "#define DoInitRZ (info->doInitRz)" to use a variable you
+ * add to the ZModem structure.
+ *
+ * It is assumed that the compiler is good enough to optimize
+ * "if( 0 )" and "if( 1 )" cases. If not, you may wish to modify
+ * the source code to use #ifdef instead.
+ */
+
+#define DoInitRZ 1 /* send initial "rz\r" when transmitting */
+#define AllowCommand 0 /* allow remote end to execute commands */
+#define SendSample 1 /* sender can sample reverse channel */
+#define SendAttn 1 /* sender can be interrupted with Attn signal */
+#define ResponseTime 10 /* reasonable response time for sender to
+ * respond to requests from receiver */
+#define SerialNo 1 /* receiver serial # */
+#define MaxNoise 64 /* max "noise" characters before transmission
+ * pauses */
+#define MaxErrs 30 /* Max receive errors before cancel */
+#define AlwaysSinit 1 /* always send ZSINIT header, even if not
+ * needed, this makes protocol more robust */
+
+#define SendOnly 0 /* compiles smaller version for send only */
+#define RcvOnly 0 /* compiles smaller version for receive only */
+
+enum enum_InputState {
+ Idle, Padding, Inhdr, Indata, Finish, Ysend, Yrcv
+};
+enum enum_Protocol {
+ XMODEM, YMODEM, ZMODEM
+};
+enum enum_Streaming {
+ Full, StrWindow, SlidingWindow, Segmented
+};
+enum enum_transferstate {
+ notransfer, transferstart, transferstop
+};
+
+
+/* Internal State */
+
+typedef enum zmstate {
+ /* receive */
+ RStart, /* sent RINIT, waiting for ZFILE or SINIT */
+ RSinitWait, /* got SINIT, waiting for data */
+ RFileName, /* got ZFILE, waiting for filename & info */
+ RCrc, /* got filename, want crc too */
+ RFile, /* got filename, ready to read */
+ RData, /* reading data */
+ RDataErr, /* encountered error, ignoring input */
+ RFinish, /* sent ZFIN, waiting for 'OO' */
+
+ /* transmit */
+ TStart, /* waiting for INIT frame from other end */
+ TInit, /* received INIT, sent INIT, waiting for ZACK */
+ FileWait, /* sent file header, waiting for ZRPOS */
+ CrcWait, /* sent file crc, waiting for ZRPOS */
+ Sending, /* sending data subpackets, ready for int */
+ SendWait, /* waiting for ZACK */
+ SendDone, /* file finished, need to send EOF */
+ SendEof, /* sent EOF, waiting for ZACK */
+ TFinish, /* sent ZFIN, waiting for ZFIN */
+
+ /* general */
+ CommandData, /* waiting for command data */
+ CommandWait, /* waiting for command to execute */
+ StderrData, /* waiting for stderr data */
+ Done,
+
+ /* x/ymodem transmit */
+ YTStart, /* waiting for 'G', 'C' or NAK */
+ YTFile, /* sent filename, waiting for ACK */
+ YTDataWait, /* ready to send data, waiting for 'C' */
+ YTData, /* sent data, waiting for ACK */
+ YTEOF, /* sent eof, waiting for ACK */
+ YTFin, /* sent null filename, waiting for ACK */
+
+ /* x/ymodem receive */
+ YRStart, /* sent 'C', waiting for filename */
+ YRDataWait, /* received filename, waiting for data */
+ YRData, /* receiving filename or data */
+ YREOF /* received first EOT, waiting for 2nd */
+
+} ZMState;
+
+
+
+
+typedef struct {
+ int ifd; /* input fd, for use by caller's routines */
+ int ofd; /* output fd, for use by caller's routines */
+ FILE *file; /* file being transfered */
+ int zrinitflags; /* receiver capabilities, see below */
+ int zsinitflags; /* sender capabilities, see below */
+ char *attn; /* attention string, see below */
+ int timeout; /* timeout value, in seconds */
+ int bufsize; /* receive buffer size, bytes */
+ int packetsize; /* preferred transmit packet size */
+ int windowsize; /* max window size */
+
+ /* file attributes: read-only */
+
+ int filesRem, bytesRem;
+ uchar f0, f1, f2, f3; /* file flags */
+ int len, mode, fileType; /* file flags */
+ ulong date; /* file date */
+
+ /* From here down, internal to Zmodem package */
+
+ ZMState state; /* protocol internal state */
+ char *filename; /* filename */
+ char *rfilename; /* remote filename */
+ int crc32; /* use 32-bit crc */
+ int pktLen; /* length of this packet */
+ int DataType; /* input data type */
+ int PacketType; /* type of this packet */
+ int rcvlen;
+ int chrCount; /* chars received in current header/buffer */
+ int crcCount; /* crc characters remaining at end of buffer */
+ int canCount; /* how many CAN chars received? */
+ int noiseCount; /* how many noise chars received? */
+ int errorFlush; /* ignore incoming data because of error */
+ uchar *buffer; /* data buffer */
+ ulong offset; /* file offset */
+ ulong lastOffset; /* last acknowledged offset */
+ ulong zrposOffset; /* last offset specified w/zrpos */
+ int ylen, bufp; /* len,location of last Ymodem packet */
+ int fileEof; /* file eof reached */
+ int packetCount; /* # packets received */
+ int errCount; /* how many data errors? */
+ int timeoutCount; /* how many times timed out? */
+ int windowCount; /* how much data sent in current window */
+ int atSign; /* last char was '@' */
+ int lastCR; /* last char was CR */
+ int escCtrl; /* other end requests ctrl chars be escaped */
+ int escHibit; /* other end requests hi bit be escaped */
+ int escape; /* next character is escaped */
+ int interrupt; /* received attention signal */
+ int waitflag; /* next send should wait */
+ /* parser state */
+ // enum enum_InputState {Idle, Padding, Inhdr, Indata, Finish, Ysend, Yrcv} InputState ;
+ enum_InputState InputState;
+ // enum enum_Protocol {XMODEM, YMODEM, ZMODEM} Protocol ;
+ enum_Protocol Protocol;
+ uchar hdrData[9]; /* header type and data */
+ uchar fileFlags[4]; /* file xfer flags */
+ ulong crc; /* crc of incoming header/data */
+ // enum enum_Streaming {Full, StrWindow, SlidingWindow, Segmented} Streaming ;
+ enum_Streaming Streaming;
+} ZModem;
+
+
+
+/* ZRINIT flags. Describe receiver capabilities */
+
+#define CANFDX 1 /* Rx is Full duplex */
+#define CANOVIO 2 /* Rx can overlap I/O */
+#define CANBRK 4 /* Rx can send a break */
+#define CANCRY 010 /* Rx can decrypt */
+#define CANLZW 020 /* Rx can uncompress */
+#define CANFC32 040 /* Rx can use 32-bit crc */
+#define ESCCTL 0100 /* Rx needs control chars escaped */
+#define ESC8 0200 /* Rx needs 8th bit escaped. */
+
+/* ZSINIT flags. Describe sender capabilities */
+
+#define TESCCTL 0100 /* Tx needs control chars escaped */
+#define TESC8 0200 /* Tx needs 8th bit escaped. */
+
+
+/* ZFILE transfer flags */
+
+/* F0 */
+#define ZCBIN 1 /* binary transfer */
+#define ZCNL 2 /* convert NL to local eol convention */
+#define ZCRESUM 3 /* resume interrupted file xfer, or append to a
+ growing file. */
+
+/* F1 */
+#define ZMNEWL 1 /* transfer if source newer or longer */
+#define ZMCRC 2 /* transfer if different CRC or length */
+#define ZMAPND 3 /* append to existing file, if any */
+#define ZMCLOB 4 /* replace existing file */
+#define ZMNEW 5 /* transfer if source is newer */
+#define ZMDIFF 6 /* transfer if dates or lengths different */
+#define ZMPROT 7 /* protect: transfer only if dest doesn't exist */
+#define ZMCHNG 8 /* change filename if destination exists */
+#define ZMMASK 037 /* mask for above. */
+#define ZMSKNOLOC 0200 /* skip if not present at Rx end */
+
+/* F2 */
+#define ZTLZW 1 /* lzw compression */
+#define ZTRLE 3 /* run-length encoding */
+
+/* F3 */
+#define ZCANVHDR 1 /* variable headers ok */
+#define ZRWOVR 4 /* byte position for receive window override/256 */
+#define ZXSPARS 64 /* encoding for sparse file ops. */
+
+
+
+/* ATTN string special characters. All other characters sent verbose */
+
+#define ATTNBRK '\335' /* send break signal */
+#define ATTNPSE '\336' /* pause for one second */
+
+/* error code definitions [O] means link still open */
+
+#define ZmDone -1 /* done */
+#define ZmErrInt -2 /* internal error */
+#define ZmErrSys -3 /* system error, see errno */
+#define ZmErrNotOpen -4 /* communication channel not open */
+#define ZmErrCantOpen -5 /* can't open file, see errno [O] */
+#define ZmFileTooLong -6 /* remote filename too long [O] */
+#define ZmFileCantWrite -7 /* could not write file, see errno */
+#define ZmDataErr -8 /* too many data errors */
+#define ZmErrInitTo -10 /* transmitter failed to respond to init req. */
+#define ZmErrSequence -11 /* packet received out of sequence */
+#define ZmErrCancel -12 /* cancelled by remote end */
+#define ZmErrRcvTo -13 /* remote receiver timed out during transfer */
+#define ZmErrSndTo -14 /* remote sender timed out during transfer */
+#define ZmErrCmdTo -15 /* remote command timed out */
+
+
+
+/* ZModem character definitions */
+
+#define ZDLE 030 /* zmodem escape is CAN */
+#define ZPAD '*' /* pad */
+#define ZBIN 'A' /* introduces 16-bit crc binary header */
+#define ZHEX 'B' /* introduces 16-bit crc hex header */
+#define ZBIN32 'C' /* introduces 32-bit crc binary header */
+#define ZBINR32 'D' /* introduces RLE packed binary frame w/32-bit crc */
+#define ZVBIN 'a' /* alternate ZBIN */
+#define ZVHEX 'b' /* alternate ZHEX */
+#define ZVBIN32 'c' /* alternate ZBIN32 */
+#define ZVBINR32 'd' /* alternate ZBINR32 */
+#define ZRESC 0177 /* RLE flag/escape character */
+
+
+
+/* ZModem header type codes */
+
+#define ZRQINIT 0 /* request receive init */
+#define ZRINIT 1 /* receive init */
+#define ZSINIT 2 /* send init sequence, define Attn */
+#define ZACK 3 /* ACK */
+#define ZFILE 4 /* file name, from sender */
+#define ZSKIP 5 /* skip file command, from receiver */
+#define ZNAK 6 /* last packet was garbled */
+#define ZABORT 7 /* abort */
+#define ZFIN 8 /* finish session */
+#define ZRPOS 9 /* resume file from this position, from receiver */
+#define ZDATA 10 /* data packets to follow, from sender */
+#define ZEOF 11 /* end of file, from sender */
+#define ZFERR 12 /* fatal i/o error, from receiver */
+#define ZCRC 13 /* request for file crc, from receiver */
+#define ZCHALLENGE 14 /* "send this number back to me", from receiver */
+#define ZCOMPL 15 /* request is complete */
+#define ZCAN 16 /* other end cancelled with CAN-CAN-CAN-CAN-CAN */
+#define ZFREECNT 17 /* request for free bytes on filesystem */
+#define ZCOMMAND 18 /* command, from sending program */
+#define ZSTDERR 19 /* output this message to stderr */
+
+
+/* ZDLE escape sequences */
+
+
+#define ZCRCE 'h' /* CRC next, frame ends, header follows */
+#define ZCRCG 'i' /* CRC next, frame continues nonstop */
+#define ZCRCQ 'j' /* CRC next, send ZACK, frame continues nonstop */
+#define ZCRCW 'k' /* CRC next, send ZACK, frame ends */
+#define ZRUB0 'l' /* translate to 0177 */
+#define ZRUB1 'm' /* translate to 0377 */
+
+
+/* ascii definitions */
+
+#define SOH 1 /* ^A */
+#define STX 2 /* ^B */
+#define EOT 4 /* ^D */
+#define ACK 6 /* ^F */
+#define DLE 16 /* ^P */
+#define XON 17 /* ^Q */
+#define XOFF 19 /* ^S */
+#define NAK 21 /* ^U */
+#define SYN 22 /* ^V */
+#define CAN 24 /* ^X */
+
+
+/* state table entry. There is one row of the table per
+ * possible state. Each row is a row of all reasonable
+ * inputs for this state. The entries are sorted so that
+ * the most common inputs occur first, to reduce search time
+ * Unexpected input is reported and ignored, as it might be
+ * caused by echo or something.
+ *
+ * Extra ZRINIT headers are the receiver trying to resync.
+ */
+class FQTermConfig;
+
+class FQTermZmodem;
+
+class FQTermTelnet;
+
+class FQTermFileDialog;
+
+typedef int(FQTermZmodem:: *ActionFunc)(ZModem*);
+
+typedef struct {
+ int type; /* frame type */
+ // int (*func)(ZModem *) ; /* transition function */
+ ActionFunc func;
+ int IFlush; /* flag: flush input first */
+ int OFlush; /* flag: flush output first */
+ ZMState newstate; /* new state. May be overridden by func */
+} StateTable;
+
+//class FQTermTelnet;
+
+class FQTermZmodem: public QObject {
+ Q_OBJECT;
+ public:
+ FQTermZmodem(FQTermConfig *config, FQTermTelnet *netinterface, int type, int serverEncoding);
+ ~FQTermZmodem();
+
+ /* zmodem-supplied functions: */
+ int ZmodemTInit(ZModem *info);
+ int ZmodemTFile(char *file, char *rmtname, uint f0, uint f1, uint f2, uint f3,
+ int filesRem, int bytesRem, ZModem *info);
+ int ZmodemTFinish(ZModem *info);
+ int ZmodemAbort(ZModem *info);
+ int ZmodemRInit(ZModem *info);
+ int ZmodemRcv(uchar *str, int len, ZModem *info, int &consumed_bytes);
+ int ZmodemAttention(ZModem *info);
+
+ int ZmodemReset(ZModem *info);
+
+ int YmodemTInit(ZModem *info);
+ int XmodemTInit(ZModem *info);
+ int YmodemRInit(ZModem *info);
+ int XmodemRInit(ZModem *info);
+
+ ulong FileCrc(char *name);
+ const char *sname(ZModem*);
+ const char *sname2(ZMState);
+
+ /* caller-supplied functions: */
+ int ZXmitChr(uchar c, ZModem *info);
+ int ZXmitStr(const uchar *str, int len, ZModem *info);
+ void ZIFlush(ZModem *info);
+ void ZOFlush(ZModem *info);
+ int ZAttn(ZModem *info);
+ void ZStatus(int type, int value, const char *status);
+ FILE *ZOpenFile(char *name, ulong crc, ZModem *info);
+
+ int ZWriteFile(uchar *buffer, int len, FILE *, ZModem*);
+ int ZCloseFile(ZModem *info);
+ void ZFlowControl(int onoff, ZModem *info);
+
+ /* end caller-supplied functions */
+
+ int ZXmitHdr(int type, int format, const uchar data[4], ZModem *info);
+ int ZXmitHdrHex(int type, const uchar data[4], ZModem *info);
+ int ZXmitHdrBin(int type, const uchar data[4], ZModem *info);
+ int ZXmitHdrBin32(int type, const uchar data[4], ZModem *info);
+ uchar *putZdle(uchar *ptr, uchar c, ZModem *info);
+ uchar *putHex(uchar *ptr, uchar c);
+
+ uchar *ZEnc4(ulong n);
+ ulong ZDec4(const uchar buf[4]);
+
+ int YrcvChar(char c, register ZModem *info);
+ int YrcvTimeout(register ZModem *info);
+ void ZIdleStr(uchar *buffer, int len, ZModem *info);
+
+ /* LEXICAL BOX: handle characters received from remote end.
+ * These may be header, data or noise.
+ *
+ * This section is a finite state machine for parsing headers
+ * and reading input data. The info->chrCount field is effectively
+ * the state variable.
+ */
+
+ int FinishChar(char c, register ZModem *info);
+ int DataChar(uchar c, register ZModem *info);
+ int HdrChar(uchar c, register ZModem *info);
+ int IdleChar(uchar c, register ZModem *info);
+
+ int YsendChar(char c, ZModem *info);
+
+ int ZPF(ZModem *info);
+ int Ignore(ZModem *info);
+ int AnswerChallenge(register ZModem *info);
+ int GotAbort(register ZModem *info);
+ int GotCancel(ZModem *info);
+ int GotCommand(ZModem *info);
+ int GotStderr(register ZModem *info);
+ int RetDone(ZModem *info);
+ int GotCommandData(register ZModem *info, int crcGood);
+ int GotStderrData(register ZModem *info, int crcGood);
+
+ int GotFileName(ZModem *info, int crcGood);
+ int ResendCrcReq(ZModem *info);
+ int GotSinitData(ZModem *info, int crcGood);
+ int ResendRpos(ZModem *info);
+ int GotFileData(ZModem *info, int crcGood);
+ int SendRinit(ZModem *info);
+
+ int GotSinit(ZModem *info);
+ int GotFile(ZModem *info);
+ int GotFin(ZModem *info);
+ int GotData(ZModem *info);
+ int GotEof(ZModem *info);
+ int GotFreecnt(ZModem *info);
+ int GotFileCrc(ZModem *info);
+
+ int GotRinit(ZModem*);
+ int SendZSInit(ZModem*);
+ int SendFileCrc(ZModem*);
+ int GotSendAck(ZModem*);
+ int GotSendDoneAck(ZModem*);
+ int GotSendNak(ZModem*);
+ int GotSendWaitAck(ZModem*);
+ int SkipFile(ZModem*);
+ int GotSendPos(ZModem*);
+
+ int requestFile(ZModem *info, ulong crc);
+ void parseFileName(ZModem *info, char *fileinfo);
+ int fileError(ZModem *info, int type, int data);
+
+ int ProcessPacket(ZModem *info);
+ int rejectPacket(ZModem *info);
+ int acceptPacket(ZModem *info);
+
+ int calcCrc(uchar *str, int len);
+
+ char *strdup(const char *str);
+
+ int SendMoreFileData(ZModem *info);
+
+ uint rcvHex(uint i, char c);
+
+ int ZDataReceived(register ZModem *info, int crcGood);
+ int dataSetup(register ZModem *info);
+ int ZProtocol(register ZModem *info);
+
+ int ZXmitData(int, int, uchar, uchar *data, ZModem*);
+ int YXmitData(uchar *, int, ZModem*);
+
+ int YSendFilename(ZModem*);
+ int YSendData(ZModem*);
+ int YSendFin(ZModem*);
+
+ int sendFilename(ZModem*);
+ int SendFileData(ZModem*);
+ int ResendEof(ZModem*);
+
+ int OverAndOut(ZModem*);
+
+ int startFileData(ZModem *info);
+
+ void zmodemlog(const char *fmt, ...);
+
+ // interface function
+ // all not completed
+ void transferTimeOut(void*);
+ void upload(char*);
+ void uploadNext(char*);
+ void transferFinish();
+ void xferCancel();
+
+ /* data received from remote, pass it to Zmodem funcs */
+ void transferSendData(char *, int);
+ void TransferCancel();
+
+#if 1
+ static StateTable RStartOps[];
+ static StateTable RSinitWaitOps[];
+ static StateTable RFileNameOps[];
+ static StateTable RCrcOps[];
+ static StateTable RFileOps[];
+ static StateTable RDataOps[];
+ static StateTable RFinishOps[];
+
+
+ static StateTable TStartOps[];
+ static StateTable TInitOps[];
+ static StateTable FileWaitOps[];
+ static StateTable CrcWaitOps[];
+ static StateTable SendingOps[];
+ static StateTable SendDoneOps[];
+ static StateTable SendWaitOps[];
+ static StateTable SendEofOps[];
+ static StateTable TFinishOps[];
+
+ static StateTable CommandDataOps[];
+ static StateTable CommandWaitOps[];
+ static StateTable StderrDataOps[];
+ static StateTable DoneOps[];
+
+ static StateTable *tables[];
+ static const char *hdrnames[];
+#endif
+
+#if 0
+ StateTable RStartOps[];
+ StateTable RSinitWaitOps[];
+ StateTable RFileNameOps[];
+ StateTable RCrcOps[];
+ StateTable RFileOps[];
+ StateTable RDataOps[];
+ StateTable RFinishOps[];
+
+
+ StateTable TStartOps[];
+ StateTable TInitOps[];
+ StateTable FileWaitOps[];
+ StateTable CrcWaitOps[];
+ StateTable SendingOps[];
+ StateTable SendDoneOps[];
+ StateTable SendWaitOps[];
+ StateTable SendEofOps[];
+ StateTable TFinishOps[];
+
+ StateTable CommandDataOps[];
+ StateTable CommandWaitOps[];
+ StateTable StderrDataOps[];
+ StateTable DoneOps[];
+
+ StateTable *tables[];
+ char *hdrnames[];
+#endif
+ FQTermTelnet *telnet_;
+ FQTermConfig *config_;
+ // Dialog
+ // QDialog *zmodemDialog, uploadListDialog;
+
+ bool sending;
+
+ enum_transferstate transferstate;
+
+ ZModem info;
+
+ // connection type, e.g. telnet or ssh , 0=telnet, 1=ssh
+
+ int connectionType;
+
+ // Timer
+ QTimer *zmodemTimer;
+
+ //log file
+ FILE *zmodemlogfile;
+
+ int zerrno;
+ uchar lastPullByte;
+
+ QStringList strFileList;
+ QStringList::Iterator itFile;
+
+ int serverEncodingID;
+
+ QString lastDownloadedFilename_;
+
+ // end Member
+ signals:
+ void ZmodemState(int, int, const char *);
+
+ public slots:
+ void zmodemCancel();
+ int ZmodemTimeout();
+};
+
+} // namespace FQTerm
+
+#endif // FQTERM_ZMODEM_H