1
/***************************************************************************
2
* fqterm, a terminal emulator for both BBS and *nix. *
3
* Copyright (C) 2008 fqterm development group. *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. *
19
***************************************************************************/
38
#include <QtAlgorithms>
44
#include "fqterm_trace.h"
45
#include "fqterm_session.h"
46
#include "fqterm_buffer.h"
47
#include "fqterm_text_line.h"
48
#include "fqterm_telnet.h"
49
#include "fqterm_decode.h"
50
#include "fqterm_zmodem.h"
54
const QString FQTermSession::endOfUrl[] = {
55
"ac","ad","ae","af","ag","ai","al","am","an","ao","aq","ar","as","at",
56
"au","aw","az","ba","bb","bd","be","bf","bg","bh","bi","bj","bm","bn","bo","br",
57
"bs","bt","bv","bw","by","bz","ca","cc","cd","cf","cg","ch","ci","ck","cl","cm",
58
"cn","co","uk","com","cr","cs","cu","cv","cx","cy","cz","de","dj","dk","dm",
59
"do","dz","ec","edu","ee","eg","eh","er","es","et","fi","fj","fk","fm","fo",
60
"fr","ga","gd","ge","gf","gg","gh","gi","gl","gm","gn","gov","gp","gq","aero",
61
"asia","biz","coop","eu","info","museum","name","pro","travel","gr","gs","gt",
62
"gu","gw","gy","hk","hm","hn","hr","ht","hu","id","ie","il","im","in","int",
63
"io","iq","ir","is","it","je","jm","jo","jp","ke","kg","kh","ki","km","kn","kp",
64
"kr","kw","ky","kz","la","lb","lc","li","lk","lr","ls","lt","lu","lv","ly","ma",
65
"mc","md","mg","mh","mil","mk","ml","mm","mn","mo","mp","mq","mr","ms","mt",
66
"mu","mv","mw","mx","my","mz","na","nc","ne","net","nf","ng","ni","nl","no",
67
"np","nr","nt","nu","nz","om","org","pa","pe","pf","pg","ph","pk","pl","pm",
68
"pn","pr","ps","pt","pw","py","qa","re","ro","ru","rw","sa","sb","sc","sd","se",
69
"sg","sh","si","sj","sk","sl","sm","sn","so","sr","sv","st","sy","sz","tc","td",
70
"tf","tg","th","tj","tk","tm","tn","to","tp","tr","tt","tv","tw","tz","ua","ug",
71
"uk","um","us","uy","uz","va","vc","ve","vg","vi","vn","vu","wf","ws","ye","yt",
75
FQTermSession::FQTermSession(FQTermConfig *config, FQTermParam param, bool isBeep,
76
int serverEncodingID, const QString &zmodemDir) {
78
termBuffer_ = new FQTermBuffer(param_.numColumns_,
80
param_.numScrollLines_,
81
param_.hostType_ == 0);
83
if (param.protocolType_ == 0) {
84
telnet_ = new FQTermTelnet(param_.virtualTermType_.toLatin1(),
85
param_.numRows_, param_.numColumns_, false);
87
#if defined(_NO_SSH_COMPILED)
88
QMessageBox::warning(this, "sorry",
89
"SSH support is not compiled, "
90
"FQTerm can only use Telnet!");
91
telnet_ = new FQTermTelnet(param_.virtualTermType_.toUtf8(),
92
param_.numRows_, param_.numColumns_, false);
94
telnet_ = new FQTermTelnet(param_.virtualTermType_.toUtf8(),
95
param_.numRows_, param_.numColumns_, true,
96
param_.sshUserName_.toUtf8(),
97
param_.sshPassword_.toUtf8());
101
zmodem_ = new FQTermZmodem(config, telnet_, param.protocolType_, zmodemDir);
102
decoder_ = new FQTermDecode(termBuffer_, telnet_, param.serverEncodingID_);
104
// isColorCopy_ = param.isColorCopy_;
105
// isRectangleCopy_ = param.isRectSelect_;
106
// isAutoCopy_ = param.isAutoCopy_;
109
isAutoReply_ = param_.isAutoReply_;
111
isMouseSupported_ = true;
112
isAutoReconnect_ = param_.isAutoReconnect_;
113
isConnected_ = false;
114
#ifndef _NO_SSH_COMPILED
115
if (param.protocolType_ != 0) {
124
isSendingMessage_ = false;
127
serverEncodingID_ = serverEncodingID;
129
idleTimer_ = new QTimer;
130
autoReplyTimer_ = new QTimer;
131
reconnectTimer_ = new QTimer;
133
acThread_ = new ArticleCopyThread(*this, waitCondition_);
135
FQ_VERIFY(connect(reconnectTimer_, SIGNAL(timeout()),
136
this, SLOT(reconnect())));
137
FQ_VERIFY(connect(decoder_, SIGNAL(mouseMode(bool)),
138
this, SLOT(setMouseMode(bool))));
139
FQ_VERIFY(connect(telnet_, SIGNAL(readyRead(int, int)),
140
this, SLOT(readReady(int, int))));
141
FQ_VERIFY(connect(telnet_, SIGNAL(TelnetState(int)),
142
this, SLOT(changeTelnetState(int))));
143
FQ_VERIFY(connect(telnet_, SIGNAL(errorMessage(const char *)),
144
this, SIGNAL(errorMessage(const char *))));
145
FQ_VERIFY(connect(telnet_, SIGNAL(requestUserPwd(QString*, QString*, bool*)),
146
this, SIGNAL(requestUserPwd(QString*, QString*, bool*))));
148
FQ_VERIFY(connect(termBuffer_, SIGNAL(termSizeChanged(int, int)),
149
telnet_, SLOT(windowSizeChanged(int, int))));
151
FQ_VERIFY(connect(zmodem_, SIGNAL(ZmodemState(int, int, const char *)),
152
this, SIGNAL(zmodemStateChanged(int, int, const char *))));
154
FQ_VERIFY(connect(idleTimer_, SIGNAL(timeout()), this, SLOT(onIdle())));
155
FQ_VERIFY(connect(autoReplyTimer_, SIGNAL(timeout()),
156
this, SLOT(onAutoReply())));
158
FQ_VERIFY(connect(acThread_, SIGNAL(articleCopied(int, const QString)),
159
this, SIGNAL(articleCopied(int, const QString))));
161
setAntiIdle(isAntiIdle_);
164
FQTermSession::~FQTermSession() {
166
delete autoReplyTimer_;
167
delete reconnectTimer_;
175
const QString FQTermSession::protocolPrefix[] = {
176
"http://", "https://", "mms://", "rstp://", "ftp://", "mailto:", "telnet://"
179
void FQTermSession::setScreenStart(int nStart) {
180
screenStartLineNumber_ = nStart;
183
bool FQTermSession::setCursorPos(const QPoint &pt, QRect &rc) {
184
QRect rectOld = getSelectRect();
188
QRect rectNew = getSelectRect();
190
rc = rectOld | rectNew;
192
return rectOld != rectNew;
195
QString FQTermSession::getMessage() {
196
const FQTermTextLine *line;
197
LineColorInfo colorInfo;
200
getLineColorInfo(termBuffer_->getTextLineInTerm(0), &colorInfo);
201
if (!colorInfo.uniBackgroundColor) {
206
termBuffer_->getTextLineInTerm(0)->getAllPlainText(message);
208
line = termBuffer_->getTextLineInTerm(i);
209
getLineColorInfo(line, &colorInfo);
210
while (colorInfo.uniBackgroundColor && colorInfo.hasBackgroundColor) {
212
line->getAllPlainText(message);
214
line = termBuffer_->getTextLineInTerm(i);
215
getLineColorInfo(line, &colorInfo);
221
void FQTermSession::detectPageState() {
223
pageState_ = Undefined;
225
const FQTermTextLine *line[4];
226
LineColorInfo colorInfo[4];
227
int lineIndex[4] = {0, 1, 2};
228
lineIndex[3] = termBuffer_->getNumRows() - 1;
229
for (int i = 0; i < 4; ++i) {
230
line[i] = termBuffer_->getTextLineInTerm(lineIndex[i]);
231
getLineColorInfo(line[i], colorInfo + i);
234
//TODO: Detect PageState in a clearer way.
235
if (!colorInfo[0].hasBackgroundColor) {
236
if (colorInfo[3].foregroundColorIndex.count() != 0 &&
237
colorInfo[3].hasBackgroundColor){
238
if (colorInfo[3].uniBackgroundColor) {
240
line[3]->getAllPlainText(text);
241
if (text[0] != L'\x3010') {
246
} else if (colorInfo[3].backgroundColorIndex.count() == 2 &&
247
colorInfo[3].backgroundColorIndex.at(0) == 4 &&
248
colorInfo[3].backgroundColorIndex.at(1) == 0){
255
if (colorInfo[0].backgroundColorIndex.at(0) != 4
256
|| !(colorInfo[3].hasBackgroundColor && colorInfo[3].backgroundColorIndex.at(0) == 4)) {
257
if (!colorInfo[3].hasBackgroundColor &&
258
colorInfo[0].foregroundColorIndex.count() == 4 &&
259
colorInfo[0].foregroundColorIndex.at(1) == 4 &&
260
colorInfo[0].foregroundColorIndex.at(2) == 7 &&
261
colorInfo[0].foregroundColorIndex.at(3) == 4){
267
if (colorInfo[1].hasBackgroundColor && colorInfo[1].uniBackgroundColor) {
268
pageState_ = Message;
272
if (colorInfo[0].uniBackgroundColor) {
273
if (!colorInfo[2].hasBackgroundColor ||
274
colorInfo[2].backgroundColorIndex.at(0) != 4 ||
275
colorInfo[2].backgroundColorIndex.count() > 3) {
281
if (!colorInfo[0].uniBackgroundColor &&
282
colorInfo[0].backgroundColorIndex.at(0) == 4 &&
283
!colorInfo[3].uniBackgroundColor) {
284
//announce, elite root
285
pageState_ = EliteArticleList;
289
if (colorInfo[2].hasBackgroundColor &&
290
!colorInfo[2].uniBackgroundColor &&
291
colorInfo[2].backgroundColorIndex.at(0) == 4 &&
292
!colorInfo[3].uniBackgroundColor) {
293
pageState_ = EliteArticleList;
297
if (colorInfo[0].foregroundColorIndex.indexOf(14) != -1) {
298
pageState_ = ArticleList;
302
if (colorInfo[2].foregroundColorIndex.indexOf(7) != -1) {
304
line[2]->getAllPlainText(text);
305
if (text[0] == ' ') {
306
pageState_ = BoardList;
308
pageState_ = MailMenu;
314
FQTermSession::CursorType FQTermSession::getCursorType(const QPoint &pt) {
315
if (screenStartLineNumber_ !=
316
(termBuffer_->getNumLines() - termBuffer_->getNumRows())) {
320
QRect rc = getSelectRect();
321
CursorType nCursorType = kNormal;
322
switch (pageState_) {
323
case Undefined: // not recognized
324
nCursorType = kNormal;
332
} else if (rc.contains(pt)) {
334
nCursorType = kRight;
336
nCursorType = kNormal;
341
case EliteArticleList:
347
} else if (pt.y() - screenStartLineNumber_ < 3) {
350
} else if (pt.y() == termBuffer_->getNumLines() - 1) {
353
} else if (pt.x() > termBuffer_->getNumColumns() - 16 &&
354
pt.y() - screenStartLineNumber_ <= termBuffer_->getNumRows() / 2) {
356
nCursorType = kPageUp;
357
} else if (pt.x() > termBuffer_->getNumColumns() - 16
358
&& pt.y() - screenStartLineNumber_ >
359
termBuffer_->getNumRows() / 2) {
361
nCursorType = kPageDown;
362
} else if (rc.contains(pt)) {
364
nCursorType = kRight;
366
nCursorType = kNormal;
374
} else if (pt.x() > (termBuffer_->getNumColumns() - 16) &&
375
(pt.y() - screenStartLineNumber_)
376
<= termBuffer_->getNumRows() / 2) {
378
nCursorType = kPageUp;
379
} else if (pt.x() > (termBuffer_->getNumColumns() - 16) &&
380
(pt.y() - screenStartLineNumber_)
381
> termBuffer_->getNumRows() / 2) {
383
nCursorType = kPageDown;
385
nCursorType = kNormal;
389
// TODO: add action for kEdit state.
394
} else if (rc.contains(pt)){
395
nCursorType = kRight;
399
FQ_TRACE("error", 2) << "Error, wrong PageState.";
406
bool FQTermSession::isSelected(int line) {
407
// TODO: possible performance issue
408
QRect rect = getSelectRect();
415
return line >= rect.bottom() && line <= rect.top();
418
bool FQTermSession::isSelected(const QPoint &pt) {
419
// TODO: possible performance issue
420
QRect rect = getSelectRect();
427
return rect.contains(pt);
430
FQTermSession::PageState FQTermSession::getPageState() {
434
char FQTermSession::getMenuChar() {
438
QRect FQTermSession::getSelectRect() {
439
QRect rect(0, 0, 0, 0);
441
// current screen scrolled
442
if (screenStartLineNumber_ !=
443
(termBuffer_->getNumLines() - termBuffer_->getNumRows())) {
447
const FQTermTextLine *line;
448
int menuStartLine = 8;
449
switch (pageState_) {
450
case Undefined: break;
454
if (cursorPoint_.y() - screenStartLineNumber_ >= menuStartLine &&
455
cursorPoint_.x() > 5) {
456
line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
458
int cell_end = line->getCellEnd(qMin(cursorPoint_.x(),
459
(int)line->getWidth()));
460
line->getPlainText(0, cell_end, cstr);
462
int base = cstr.lastIndexOf(" ");
467
QRegExp reg("[a-zA-Z0-9][).\\]]");
468
char indexChar = cstr.indexOf(reg, base);
469
if (indexChar != -1) {
470
menuChar_ = cstr.at(indexChar).toLatin1();
472
QString strTmp = cstr.left(cstr.indexOf(reg, base));
473
int nMenuStart = get_str_width((const UTF16 *)strTmp.data(),
475
if (cstr[indexChar - 1] == '(' || cstr[indexChar - 1] == '[') {
480
line->getAllPlainText(cstr);
483
reg = QRegExp("[^ ]");
485
int nMenuBaseLength = 20;
488
qMin(nMenuStart + nMenuBaseLength, (int)line->getWidth() - 1));
489
int char_begin = line->getCharBegin(cell_begin);
490
//last [^ ] until char_begin
491
strTmp = cstr.left(cstr.lastIndexOf(reg, char_begin) + 1);
494
get_str_width((const UTF16 *)strTmp.data(), strTmp.size())
496
if (nMenuLength == nMenuBaseLength + 1) {
497
strTmp = cstr.left(cstr.indexOf(" ", char_begin) + 1);
499
get_str_width((const UTF16 *)strTmp.data(), strTmp.size())
500
- nMenuStart; //base length is not enough
503
if (cursorPoint_.x() >= nMenuStart && cursorPoint_.x() <=
504
nMenuStart + nMenuLength) {
505
rect.setX(nMenuStart);
506
rect.setY(cursorPoint_.y());
507
rect.setWidth(nMenuLength);
516
case EliteArticleList:
517
if ((cursorPoint_.y() - screenStartLineNumber_) >= 3
518
&& (cursorPoint_.y() - screenStartLineNumber_)
519
< termBuffer_->getNumRows() -1
520
&& cursorPoint_.x() >= 12
521
&& cursorPoint_.x() <= termBuffer_->getNumColumns() - 16) {
522
line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
523
//QString str = line->getText();
525
line->getAllPlainText(str);
527
if (str.count(" ") != (int)str.length()) {
529
rect.setY(cursorPoint_.y());
530
rect.setWidth(line->getWidth());
542
int ln = cursorPoint_.y() - screenStartLineNumber_;
543
if (ln >= 3 && (ln & 1) && ln <= 21 &&
544
cursorPoint_.x() >= 12 &&
545
cursorPoint_.x() <= termBuffer_->getNumColumns() - 8){
546
line = termBuffer_->getTextLineInBuffer(cursorPoint_.y());
549
line->getAllPlainText(str);
551
if (str.count(" ") != (int)str.length()) {
555
menuChar_ = ((ln - 1) >> 1) + '0';
559
rect.setY(cursorPoint_.y());
560
rect.setWidth(line->getWidth() - 20);
573
bool FQTermSession::isIllChar(char ch) {
574
static char illChars[] = ",;'\"()[]<>^";
575
return ch > '~' || ch < '!' || strchr(illChars, ch) != NULL;
578
bool FQTermSession::isUrl(QRect &rcUrl, QRect &rcOld) {
579
return checkUrl(rcUrl, rcOld, false);
582
bool FQTermSession::isIP(QRect &rcUrl, QRect &rcOld) {
583
return checkUrl(rcUrl, rcOld, true);
586
QString FQTermSession::expandUrl(const QPoint& pt, QPair<int, int>& range)
591
const FQTermTextLine *textLine = termBuffer_->getTextLineInBuffer(pt.y());
592
if (textLine == NULL || pt.x() > (int)textLine->getWidth()) {
597
textLine->getAllPlainText(text);
599
int cell_begin = textLine->getCellBegin(pt.x());
600
int at = textLine->getCharBegin(cell_begin);
601
if (at >= text.length()) {
607
for (start = at; start >= 0 && !isIllChar(text.at(start).toLatin1());
611
for (end = at; end < text.length() && !isIllChar(text.at(end).toLatin1());
614
range.first = get_str_width((const UTF16 *)text.data(), start);
615
range.second = get_str_width((const UTF16 *)text.data(), end);
616
return text.mid(start, end - start);
620
bool FQTermSession::checkUrl(QRect &rcUrl, QRect &rcOld, bool checkIP) {
624
QPoint pt = cursorPoint_;
628
if (at > urlRect_.left() && at < urlRect_.right() && urlRect_.y() == pt.y()) {
629
if ( (checkIP && !ip_.isEmpty()) ||
630
(!checkIP && !url_.isEmpty()) ) {
637
QPoint urlStartPoint;
639
urlStartPoint_ = QPoint();
640
urlEndPoint_ = QPoint();
641
urlRect_ = QRect(0, 0, -1, -1);
649
//phase 1: find all consecutive legal chars
650
QPair<int, int> range;
651
urlText = expandUrl(pt, range);
652
if (range.first < 0 || range.first >= range.second) {
657
QRect urlRect = QRect(range.first, pt.y(), range.second - range.first, 1);
658
urlStartPoint = QPoint(range.first, pt.y());
659
urlEndPoint = QPoint(range.second, pt.y());
661
if (range.second == termBuffer_->getNumColumns()) {
662
for (int i = pt.y() + 1; i < termBuffer_->getNumLines(); ++i) {
663
QString lineText = expandUrl(QPoint(0, i), range);
664
if (range.first == 0) {
666
urlEndPoint.setY(urlEndPoint.y() + 1);
667
urlEndPoint.setX(range.second);
668
if (range.second == termBuffer_->getNumColumns()) {
676
//phase 2: find protocol prefix for url
677
//if none, prefix to add is set to "mailto:" if '@' is found
678
//otherwise, the prefix is "http://" by default.
681
for (protocolIndex = 0; protocolIndex < ProtocolSupported; ++protocolIndex) {
682
int begin = urlText.indexOf(protocolPrefix[protocolIndex],
683
Qt::CaseInsensitive);
684
if (begin != -1 && begin <= at) {
685
prefixToAdd = protocolPrefix[protocolIndex];
686
urlText = urlText.mid(begin + protocolPrefix[protocolIndex].length());
687
urlStartPoint.setX(urlStartPoint.x() + begin);
691
if (protocolIndex == ProtocolSupported) {
692
int begin = urlText.indexOf("://");
696
if (urlText.indexOf('@') != -1) {
697
prefixToAdd = protocolPrefix[Mailto];
699
prefixToAdd = protocolPrefix[Http];
703
//no point or duplicated points
704
if (urlText.indexOf("..") != -1 || urlText.indexOf('.') == -1) {
708
//phase 3: (for check ip) if there is a ':' before ip, there must be a '@'
709
int host_begin = qMax(urlText.lastIndexOf('@') + 1, 0);
710
int host_end = urlText.indexOf(':', host_begin);
711
if (host_end == -1) {
712
host_end = urlText.indexOf('/', host_begin);
714
if (host_end == -1) {
715
host_end = urlText.length();
717
if (host_begin >= host_end) {
720
if (checkIP && urlText[host_end - 1] == '*') {
721
urlText[host_end - 1] = '1';
724
for (int index = 0; index < urlText.length() && urlText.at(index) != '/';
726
QChar cur(urlText.at(index));
727
if (!cur.isLetterOrNumber() && !QString("-_~:@.").contains(cur)) {
733
newIP = urlText.mid(host_begin, host_end - host_begin);
734
QStringList ipv4 = newIP.split(QLatin1String("."));
736
if (ipv4.count() != 4){
739
foreach(QString domain, ipv4) {
741
int num = domain.toInt(&ok);
742
//won't tolerant spaces.
743
if (!ok || num < 0 || num > 255 || domain.length() > 3) {
750
QString lastName = ipv4.back();
752
int lastCharIndex = lastName.lastIndexOf(QRegExp("[a-zA-Z]"));
753
if (lastCharIndex == -1) {
756
} else if (urlText == newIP) {
760
const QString *begin = endOfUrl;
761
const QString *end = endOfUrl + sizeof(endOfUrl) / sizeof(QString *);
762
if (qFind(begin, end, lastName) == end) {
766
url_ = prefixToAdd + urlText;
776
urlStartPoint_ = urlStartPoint;
777
urlEndPoint_ = urlEndPoint;
781
QString FQTermSession::getUrl() {
785
QString FQTermSession::getIP() {
789
bool FQTermSession::isPageComplete() {
790
return termBuffer_->getCaretRow() == (termBuffer_->getNumRows() - 1)
791
&& termBuffer_->getCaretColumn() == (termBuffer_->getNumColumns() - 1);
794
bool FQTermSession::isAntiIdle() {
798
void FQTermSession::setAntiIdle(bool antiIdle) {
799
isAntiIdle_ = antiIdle;
802
if (!isAntiIdle_ && idleTimer_->isActive()) {
808
if (idleTimer_->isActive()) {
811
idleTimer_->start(param_.maxIdleSeconds_ *1000);
815
void FQTermSession::autoReplyMessage() {
816
if (autoReplyTimer_->isActive()) {
817
autoReplyTimer_->stop();
819
if (pageState_ != Message) {
823
QByteArray cstrTmp = param_.replyKeyCombination_.toLocal8Bit();
824
QByteArray cstr = parseString(cstrTmp.isEmpty() ? QByteArray("^Z"): cstrTmp);
825
//cstr += m_param.m_strAutoReply.toLocal8Bit();
826
if (param_.serverEncodingID_ == 0) {
827
cstr += U2G(param_.autoReplyMessage_);
829
cstr += U2B(param_.autoReplyMessage_);
833
telnet_->write(cstr, cstr.length());
835
emit messageAutoReplied();
838
QByteArray FQTermSession::parseString(const QByteArray &cstr, int *len) {
839
QByteArray parsed = "";
845
for (uint i = 0; (long)i < cstr.length(); i++) {
846
if (cstr.at(i) == '^') {
848
if ((long)i < cstr.length()) {
849
parsed += FQTERM_CTRL(cstr.at(i));
854
} else if (cstr.at(i) == '\\') {
856
if ((long)i < cstr.length()) {
857
if (cstr.at(i) == 'n') {
859
} else if (cstr.at(i) == 'r') {
861
} else if (cstr.at(i) == 't') {
869
parsed += cstr.at(i);
879
void FQTermSession::reconnect() {
881
telnet_->connectHost(param_.hostAddress_, param_.port_);
885
void FQTermSession::reconnectProcess() {
886
static int retry = 0;
887
if (retry < param_.retryTimes_ || param_.retryTimes_ == -1) {
888
if (param_.reconnectInterval_ <= 0) {
891
reconnectTimer_->start(param_.reconnectInterval_ *1000);
897
void FQTermSession::setMouseMode(bool on) {
901
void FQTermSession::doAutoLogin() {
902
if (!param_.preLoginCommand_.isEmpty()) {
903
QByteArray temp = parseString(param_.preLoginCommand_.toLatin1());
904
telnet_->write((const char*)(temp), temp.length());
906
if (!param_.userName_.isEmpty()) {
907
QByteArray temp = param_.userName_.toLocal8Bit();
908
telnet_->write((const char*)(temp), temp.length());
910
telnet_->write(&ch, 1);
912
if (!param_.password_.isEmpty()) {
913
QByteArray temp = param_.password_.toLocal8Bit();
914
telnet_->write((const char*)(temp), temp.length());
916
telnet_->write(&ch, 1);
919
// smth ignore continous input, so sleep 1 sec :)
920
#if defined(_OS_WIN32_) || defined(Q_OS_WIN32)
926
if (!param_.postLoginCommand_.isEmpty()) {
927
QByteArray temp = parseString(param_.postLoginCommand_.toLatin1());
928
telnet_->write((const char*)(temp), temp.length());
934
void FQTermSession::readReady(int size, int raw_size) {
935
FQ_ASSERT(size > 0 || raw_size > 0); // maybe raw_size is greater than 0.
937
raw_data_.resize(raw_size);
938
telnet_->read_raw(&raw_data_[0], raw_size);
942
zmodem_->ZmodemRcv((uchar*)&raw_data_[0], raw_data_.size(), &(zmodem_->info),
945
if (zmodem_->transferstate == notransfer) {
948
int last_size = telnet_data_.size();
949
telnet_data_.resize(last_size + size);
950
telnet_->read(&telnet_data_[0] + last_size, size);
952
int processed = decoder_->decode(&telnet_data_[0], telnet_data_.size());
953
telnet_data_.erase(telnet_data_.begin(), telnet_data_.begin() + processed);
957
//FIXME: why these codes check those non-sense?
958
int n = termBuffer_->getCaretRow();
959
for (int y = n - 5; y < n + 5; y++) {
961
const FQTermTextLine *pTextLine = termBuffer_->getTextLineInTerm(y);
962
if (pTextLine == NULL) {
966
// QString str = pTextLine->getText();
968
pTextLine->getAllPlainText(str);
970
if (str.indexOf("guest") != -1 /*&& str.indexOf("new") != -1*/) {
976
// page complete when caret at the right corner
977
// this works for most but not for all
978
const FQTermTextLine *pTextLine =
979
termBuffer_->getTextLineInTerm(termBuffer_->getNumRows() - 1);
981
//QString strText = stripWhitespace(pTextLine->getText());
982
// TODO_UTF16: fixme! performance issue!
984
pTextLine->getAllPlainText(strText);
986
// TODO_UTF16: shall we disable trim?
987
strText = strText.trimmed();
989
if (termBuffer_->getCaretRow() == termBuffer_->getNumRows() - 1
990
&& termBuffer_->getCaretColumn() >= strText.length() - 1) {
991
waitCondition_.wakeAll();
994
//QToolTip::remove(this, screen_->mapToRect(m_rcUrl));
997
// 03/19/2003. the caret posion removed as a message judgement
998
// because smth.org changed
999
if (decoder_->bellReceive()) {
1001
emit bellReceived();
1003
if (isAutoReply()) {
1005
if (!pythonCallback("autoReply", Py_BuildValue("(l)", this))) {
1007
// TODO: save messages
1011
autoReplyTimer_->start(param_.maxIdleSeconds_ *1000 / 2);
1022
emit sessionUpdated();
1026
pythonCallback("dataEvent", Py_BuildValue("(l)", this));
1030
if (zmodem_->transferstate == transferstop) {
1031
zmodem_->transferstate = notransfer;
1035
QByteArray FQTermSession::stripWhitespace(const QByteArray &cstr) {
1036
QString cstrText = QString::fromLatin1(cstr);
1038
#if (QT_VERSION>=300)
1039
int pos = cstrText.lastIndexOf(QRegExp("[\\S]"));
1041
int pos = cstrText.lastIndexOf(QRegExp("[^\\s]"));
1047
cstrText.truncate(pos + 1);
1050
return cstrText.toLatin1();
1053
/* 0-left 1-middle 2-right 3-relsase 4/5-wheel
1056
//void FQTermWindow::sendMouseState( int num,
1057
// Qt::ButtonState btnstate, Qt::ButtonState keystate, const QPoint& pt )
1058
void FQTermSession::sendMouseState(int num, Qt::KeyboardModifier btnstate,
1059
Qt::KeyboardModifier keystate, const QPoint &pt) {
1064
QPoint ptc = screen_->mapToChar(pt);
1066
if(btnstate&Qt::LeftButton)
1068
else if(btnstate&Qt::MidButton)
1070
else if(btnstate&Qt::RightButton)
1074
if(keystate&Qt::ShiftModifier)
1076
if(keystate&Qt::MetaModifier)
1078
if(keystate&Qt::ControlModifier)
1081
// normal buttons are passed as 0x20 + button,
1082
// mouse wheel (buttons 4,5) as 0x5c + button
1083
if(num>3) num += 0x3c;
1086
sprintf(mouse, "\033[M%c%c%c",
1090
telnet_->write( mouse, strlen(mouse) );
1094
void FQTermSession::cancelZmodem() {
1095
zmodem_->zmodemCancel();
1098
void FQTermSession::disconnect() {
1100
finalizeConnection();
1103
void FQTermSession::finalizeConnection() {
1105
QString strMsg = "";
1106
strMsg += "\n\n\n\r\n\r";
1107
strMsg += "\x1b[17C\x1b[0m===========================================\n\r";
1109
"\x1b[17C Connection Closed, Press \x1b[1m\x1b[31;40mEnter\x1b[m\x1b[0m To Connect\n\r";
1110
strMsg += "\x1b[17C===========================================\n";
1111
decoder_->decode(strMsg.toLatin1(), strMsg.length());
1113
isConnected_ = false;
1115
if (autoReplyTimer_->isActive()) {
1116
autoReplyTimer_->stop();
1119
if (idleTimer_->isActive()) {
1123
emit connectionClosed();
1126
// telnet state slot
1127
void FQTermSession::changeTelnetState(int state) {
1133
case TSHOSTNOTFOUND:
1134
finalizeConnection();
1138
case TSHOSTCONNECTED:
1139
isConnected_ = true;
1140
if (param_.isAutoLogin_) {
1144
case TSPROXYCONNECTED:
1152
finalizeConnection();
1158
finalizeConnection();
1159
if (param_.isAutoReconnect_ && isAutoReconnect_) {
1164
finalizeConnection();
1166
case TSCONNECTVIAPROXY:
1168
case TSEGETHOSTBYNAME:
1169
finalizeConnection();
1172
finalizeConnection();
1181
// restart the idle timer
1182
if (idleTimer_->isActive()) {
1186
idleTimer_->start(param_.maxIdleSeconds_ *1000);
1194
emit telnetStateChanged(state);
1197
void FQTermSession::handleInput(const QString &text) {
1198
if (text.length() > 0) {
1199
QByteArray cstrTmp = unicode2bbs(text);
1200
telnet_->write(cstrTmp, cstrTmp.length());
1204
QByteArray FQTermSession::unicode2bbs(const QString &text) {
1207
if (serverEncodingID_ == 0) {
1209
if (param_.serverEncodingID_ == 1) {
1210
char *str = encodingConverter_.G2B(strTmp, strTmp.length());
1216
if (param_.serverEncodingID_ == 0) {
1217
char *str = encodingConverter_.B2G(strTmp, strTmp.length());
1227
bool FQTermSession::pythonCallback(const QString &func, PyObject *pArgs) {
1228
if (!isPythonScriptLoaded_) {
1234
// get the global lock
1235
PyEval_AcquireLock();
1236
// get a reference to the PyInterpreterState
1238
//Python thread references
1239
extern PyThreadState *mainThreadState;
1241
PyInterpreterState *mainInterpreterState = mainThreadState->interp;
1242
// create a thread state object for this thread
1243
PyThreadState *myThreadState = PyThreadState_New(mainInterpreterState);
1244
PyThreadState_Swap(myThreadState);
1246
PyObject *pF = PyString_FromString(func.toLatin1());
1247
PyObject *pFunc = PyDict_GetItem(pythonDict_, pF);
1250
if (pFunc && PyCallable_Check(pFunc)) {
1251
PyObject *pValue = PyObject_CallObject(pFunc, pArgs);
1253
if (pValue != NULL) {
1258
// QMessageBox::warning(this, "Python script error", getException());
1262
FQ_TRACE("session", 0) << "Cannot find python callback function: "
1266
// swap my thread state out of the interpreter
1267
PyThreadState_Swap(NULL);
1268
// clear out any cruft from thread state object
1269
PyThreadState_Clear(myThreadState);
1270
// delete my thread state object
1271
PyThreadState_Delete(myThreadState);
1273
PyEval_ReleaseLock();
1275
if (func == "autoReply") {
1277
// paveViewMessage_->display("You have messages", PageViewMessage::Info, 0);
1282
#endif //HAVE_PYTHON
1284
void FQTermSession::onIdle() {
1285
// do as autoreply when it is enabled
1286
if (autoReplyTimer_->isActive() && isAutoReply_) {
1293
// system script can handle that
1295
if (pythonCallback("antiIdle", Py_BuildValue("(l)", this))) {
1299
// the default function
1301
QByteArray cstr = parseString(param_.antiIdleMessage_.toLocal8Bit(), &length);
1302
telnet_->write(cstr, length);
1305
void FQTermSession::onAutoReply() {
1306
// if AutoReply still enabled, then autoreply
1310
// else just stop the timer
1311
autoReplyTimer_->stop();
1316
bool FQTermSession::isAutoReply() {
1317
return isAutoReply_;
1320
void FQTermSession::setAutoReply(bool on) {
1322
if (!isAutoReply_ && autoReplyTimer_->isActive()) {
1323
autoReplyTimer_->stop();
1327
int FQTermSession::write(const char *data, int len) {
1328
return telnet_->write(data, len);
1331
int FQTermSession::writeStr(const char *str) {
1332
return write(str, strlen(str));
1335
void FQTermSession::setProxy(int type, bool needAuth,
1336
const QString &hostName, quint16 portNumber,
1337
const QString &userName, const QString &password) {
1338
telnet_->setProxy(type, needAuth, hostName, portNumber, userName, password);
1341
void FQTermSession::connectHost(const QString &hostName, quint16 portNumber) {
1342
telnet_->connectHost(hostName, portNumber);
1345
void FQTermSession::close() {
1347
if (reconnectTimer_->isActive()) {
1348
reconnectTimer_->stop();
1350
finalizeConnection();
1353
void FQTermSession::clearLineChanged(int index) {
1354
termBuffer_->clearLineChanged(index);
1357
void FQTermSession::setLineAllChanged(int index) {
1358
termBuffer_->setLineAllChanged(index);
1361
void FQTermSession::setTermSize(int col, int row) {
1362
termBuffer_->setTermSize(col, row);
1365
const FQTermBuffer *FQTermSession::getBuffer() const {
1369
void FQTermSession::setSelect(const QPoint &pt1, const QPoint &pt2) {
1370
termBuffer_->setSelect(pt1, pt2);
1373
void FQTermSession::clearSelect() {
1374
termBuffer_->clearSelect();
1377
void FQTermSession::leaveIdle() {
1378
if (autoReplyTimer_->isActive()) {
1379
autoReplyTimer_->stop();
1382
if (idleTimer_->isActive()) {
1387
idleTimer_->start(param_.maxIdleSeconds_ * 1000);
1391
void FQTermSession::copyArticle() {
1392
if (!acThread_->isRunning()) {
1397
void FQTermSession::getLineColorInfo(const FQTermTextLine * line,
1398
LineColorInfo * colorInfo)
1400
colorInfo->backgroundColorIndex.clear();
1401
colorInfo->foregroundColorIndex.clear();
1402
colorInfo->hasBackgroundColor = false;
1403
colorInfo->uniBackgroundColor = true;
1404
colorInfo->uniForegroundColor = true;
1405
colorInfo->hasForegroundColor = false;
1407
if (line->getWidth() == 0) {
1411
const unsigned char *color = line->getColors();
1412
unsigned char background = GETBG(color[0]);
1413
unsigned char foreground = GETFG(color[0]);
1414
colorInfo->backgroundColorIndex.append(background);
1415
colorInfo->foregroundColorIndex.append(foreground);
1416
//for (int i = 0; i < color.length() / 2; i++) {
1417
// TODO_UTF16: why use color.length()/2 formerly?
1418
for (int i = 0; i < (int)line->getWidth(); i++) {
1419
if (GETBG(color[i]) != background) {
1420
colorInfo->uniBackgroundColor = false;
1421
background = GETBG(color[i]);
1422
colorInfo->backgroundColorIndex.append(background);
1424
if (GETBG(color[i]) != GETBG(NO_COLOR)) {
1425
colorInfo->hasBackgroundColor = true;
1427
if (GETFG(color[i]) != foreground) {
1428
colorInfo->uniForegroundColor = false;
1429
foreground = GETFG(color[i]);
1430
colorInfo->foregroundColorIndex.append(foreground);
1432
if (GETFG(color[i]) != GETFG(NO_COLOR)) {
1433
colorInfo->hasForegroundColor = true;
1438
ArticleCopyThread::ArticleCopyThread(
1439
FQTermSession &bbs, QWaitCondition &waitCondition)
1441
waitCondition_(waitCondition),
1442
mutex_(new QMutex()) {
1445
ArticleCopyThread::~ArticleCopyThread() {
1449
static void removeTrailSpace(QString &line) {
1450
for (int last_non_space = line.size() - 1;
1451
last_non_space >= 0; --last_non_space) {
1452
QChar a = line.at(last_non_space);
1454
line.resize(last_non_space + 1);
1458
if (last_non_space == 0) {
1464
///////////////////////////////////////
1465
//analyze last line when copy article
1466
static std::pair<int, int> ParseLastLine(QString str) {
1469
int infoStart = str.indexOf('(', str.indexOf('(') + 1);
1470
int infoEnd = str.indexOf(')', infoStart);
1471
int infoSplit = str.indexOf('-', infoStart);
1472
if (infoStart == -1 || infoEnd == -1 || infoSplit == -1) {
1473
return std::make_pair(-1, -1);
1476
startLine = str.mid(infoStart + 1, infoSplit - infoStart - 1).toInt(&ok);
1478
return std::make_pair(-1, -1);
1480
endLine = str.mid(infoSplit + 1, infoEnd - infoSplit - 1).toInt(&ok);
1482
return std::make_pair(-1, -1);
1484
return std::make_pair(startLine, endLine);
1486
///////////////////////////////////////
1489
void ArticleCopyThread::run() {
1492
typedef std::vector<QString> Page;
1493
const FQTermBuffer *buffer = session_.getBuffer();;
1495
bool MultiPage = false;
1497
QString firstPageLastLine;
1498
buffer->getTextLineInTerm(buffer->getNumRows() - 1)->getAllPlainText(firstPageLastLine);
1499
if (firstPageLastLine.indexOf("%") != -1) {
1501
session_.write("e", 1);
1502
if (!waitCondition_.wait(mutex_, 10000)) {
1503
emit articleCopied(DAE_TIMEOUT, "");
1506
QString LastPageLastLine;
1507
buffer->getTextLineInTerm(buffer->getNumRows() - 1)->getAllPlainText(LastPageLastLine);
1508
std::pair<int, int> range = ParseLastLine(LastPageLastLine);
1509
if (range.first == -1 || range.second == -1) {
1512
page.resize(range.second);
1513
for (int i = range.first; i <= range.second; ++i) {
1515
buffer->getTextLineInTerm(i - range.first)->getAllPlainText(strTemp);
1516
page[i - 1] = strTemp;
1518
session_.write("s", 1);
1519
if (!waitCondition_.wait(mutex_, 10000)) {
1520
emit articleCopied(DAE_TIMEOUT, "");
1525
for (int i = 0; i < buffer->getNumRows() - 1; ++i) {
1527
buffer->getTextLineInTerm(i)->getAllPlainText(strTemp);
1528
page.push_back(strTemp);
1533
// the end of article
1534
int lineIndex = buffer->getNumRows() - 1;
1535
//QByteArray lastLine = buffer->getLineInScreen(lineIndex)->getText();
1537
buffer->getTextLineInTerm(lineIndex)->getAllPlainText(lastLine);
1539
std::pair<int, int> range = ParseLastLine(lastLine);
1540
if (range.first == -1 || range.second == -1) {
1541
page.push_back(lastLine);
1545
// copy a page of lines exclude the last line.
1546
for (int i = range.first; i <= range.second; ++i) {
1548
buffer->getTextLineInTerm(i - range.first)->getAllPlainText(strTemp);
1549
page[i - 1] = strTemp;
1553
// wait for next page.
1554
session_.write(" ", 1);
1555
if (!waitCondition_.wait(mutex_, 10000)) {
1556
emit articleCopied(DAE_TIMEOUT, "");
1563
QString articleText;
1565
for (Page::iterator line = page.begin(); line != page.end(); ++line) {
1566
removeTrailSpace(*line);
1567
articleText += (*line);
1568
articleText += (OS_NEW_LINE);
1571
//qApp->postEvent(pWin, new QCustomEvent(DAE_FINISH));
1572
emit articleCopied(DAE_FINISH, articleText);
1576
} // namespace FQTerm
1578
#include "fqterm_session.moc"