File indexing completed on 2024-05-12 04:04:15

0001 /*
0002     This file is part of Knights, a chess board for KDE SC 4.
0003     SPDX-FileCopyrightText: 2009, 2010, 2011 Miha Čančula <miha@noughmad.eu>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "proto/ficsprotocol.h"
0009 #include "proto/ficsdialog.h"
0010 #include "proto/chatwidget.h"
0011 #include "settings.h"
0012 #include "knightsdebug.h"
0013 
0014 #include <KLocalizedString>
0015 
0016 #include <QDialog>
0017 #include <QDialogButtonBox>
0018 #include <QVBoxLayout>
0019 #include <QPushButton>
0020 #include <QAbstractSocket>
0021 #include <QTcpSocket>
0022 #include <QApplication>
0023 
0024 using namespace Knights;
0025 
0026 // TODO: Include optional [white]/[black], m, f in RegEx check
0027 
0028 const char* boolPattern = "([tf])";
0029 const char* ratedPattern = "([ru])";
0030 const char*  namePattern = "([a-zA-Z\\(\\)]+)";
0031 const char*  ratingPattern = "([0-9\\+\\-\\s]+)";
0032 const char* timePattern = "(\\d+)\\s+(\\d+)";
0033 const char* variantPattern = "([a-z]+)\\s+([a-z]+)";
0034 const char* argsPattern = "(.*)"; //TODO better
0035 const char*  idPattern = "(\\d+)";
0036 const char* pieces = "PRNBQKprnbqk";
0037 const char* coordinate = "[abdcdefgh][12345678]";
0038 const char* remainingTime = "\\d+ \\d+ (\\d+) \\d+ \\d+ (\\d+) (\\d+) (\\d+)";
0039 const char* currentPlayerPattern = "([WB]) \\-?\\d+ \\d+ \\d+ \\d+ \\d+ \\d+ \\d+";
0040 const char* offerPattern = "<pf> (\\d+) w=([a-zA-Z\\(\\)]+) t=([a-z]+) p=(.+)";
0041 
0042 FicsProtocol::FicsProtocol ( QObject* parent ) : TextProtocol ( parent ),
0043     movePattern(QStringLiteral("[%1]\\/(%2)\\-(%3)(=[%4])?")
0044                 .arg ( QLatin1String(pieces) )
0045                 .arg ( QLatin1String(coordinate) )
0046                 .arg ( QLatin1String(coordinate) )
0047                 .arg ( QLatin1String(pieces) ) ),
0048     seekExp ( QString ( QLatin1String("%1 w=%2 ti=%3 rt=%4[PE\\s] t=%5 i=%6 r=%7 tp=%8 c=%9 rr=%10 a=%11 f=%12") )
0049               .arg ( QLatin1String(idPattern) ) // %1 = index
0050               .arg ( QLatin1String(namePattern) ) // %2 = name
0051               .arg ( QLatin1String("([0-9a-f]{2,2})") ) // %3 = titles
0052               .arg ( QLatin1String("(\\d+)") ) // %4 = rating
0053               .arg ( QLatin1String("(\\d+)") ) // %5 = time
0054               .arg ( QLatin1String("(\\d+)") ) // %6 = increment
0055               .arg ( QLatin1String(ratedPattern) ) // %7 = rated ('r' or 'u')
0056               .arg ( QLatin1String("([a-z]+)") ) // %8 = type (standard, blitz, lightning, etc.)
0057               .arg ( QLatin1String("([?WB])") ) // %9 = color ('?', 'W' or 'B')
0058               .arg ( QLatin1String("(\\d+)\\-(\\d+)") ) // %10 = rating range (x-y)
0059               .arg ( QLatin1String(boolPattern) ) // %11 = automatic ( 't' or 'f' )
0060               .arg ( QLatin1String(boolPattern) )), // %12 = formula ( 't' or 'f' )
0061     challengeExp(QString ( QLatin1String("%1 w=%2 t=match p=%2 \\(%3\\) %2 \\(%3\\)"))
0062                  .arg ( QLatin1String( idPattern ) ) // %1 = index
0063                  .arg ( QLatin1String( namePattern ) ) // %2 = name
0064                  .arg ( QLatin1String( ratingPattern ) ) ), // %3 = rating
0065     moveStringExp ( movePattern ),
0066     moveRegExp ( QString ( QLatin1String("<12>.*%1.*%2 (none|o-o|o-o-o|%3)") )
0067                  .arg ( QLatin1String( currentPlayerPattern ) )
0068                  .arg ( QLatin1String( remainingTime ) )
0069                  .arg ( movePattern ) ),
0070     gameStartedExp ( QString ( QLatin1String("Creating: %1 \\(%2\\) %3 \\(%4\\) %5 %6") )
0071                      .arg ( QLatin1String( namePattern ) )
0072                      .arg ( QLatin1String( ratingPattern ) )
0073                      .arg ( QLatin1String( namePattern ) )
0074                      .arg ( QLatin1String( ratingPattern ) )
0075                      .arg ( QLatin1String( variantPattern ) )
0076                      .arg ( QLatin1String( timePattern ) ) ),
0077     offerExp ( QLatin1String( offerPattern ) ),
0078     m_stage(ConnectStage),
0079     sendPassword(false),
0080     m_widget(nullptr),
0081     m_seeking(false),
0082     m_chat(nullptr)
0083     {
0084     // FICS games are always time-limited
0085     setAttribute ( QStringLiteral("TimeLimitEnabled"), true );
0086 
0087     //TODO: this initialization is obsolete since the time control _should_ be set
0088     //when a new game is started and the values provided in the game dialog are used.
0089     if ( !Manager::self()->timeControlEnabled(color()) ) {
0090         TimeControl tc;
0091         tc.moves = 100;
0092         tc.baseTime = QTime().addSecs( 10 * 60 ); // A default time of 10 minutes with no increment
0093         tc.increment = 0;
0094         Manager::self()->setTimeControl(color(), tc);
0095     }
0096 }
0097 
0098 FicsProtocol::~FicsProtocol() = default;
0099 
0100 Protocol::Features FicsProtocol::supportedFeatures() {
0101     return TimeLimit | SetTimeLimit | UpdateTime | Pause | Adjourn | Resign | Abort;
0102 }
0103 
0104 void FicsProtocol::startGame() {
0105 
0106 }
0107 
0108 void FicsProtocol::move ( const Move& m ) {
0109     write(m.string(false));
0110 }
0111 
0112 void FicsProtocol::init (  ) {
0113     m_stage = ConnectStage;
0114 
0115     ChatWidget* console = createConsoleWidget();
0116     console->addExtraButton ( QStringLiteral("seek"), i18nc("Start searching for opponents", "Seek"), QStringLiteral("edit-find") );
0117     console->addExtraButton ( QStringLiteral("unseek"), i18nc("Stop searching for opponents", "Unseek"), QStringLiteral("edit-clear") );
0118     console->addExtraButton ( QStringLiteral("accept"), i18n("Accept"), QStringLiteral("dialog-ok-accept") );
0119     console->addExtraButton ( QStringLiteral("help"), i18n("Help"), QStringLiteral("help-contents") );
0120     connect ( console, &ChatWidget::sendText, this, &FicsProtocol::writeCheckMoves );
0121     setConsole ( console );
0122 
0123     QTcpSocket* socket = new QTcpSocket ( this );
0124     setDevice ( socket );
0125     QString address = attribute("server").toString();
0126     int port = attribute("port").toInt();
0127     if ( port == 0 )
0128         port = 5000;
0129     connect ( socket, &QTcpSocket::errorOccurred, this, &FicsProtocol::socketError );
0130 
0131     QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
0132     socket->connectToHost(address, port);
0133     if (socket->waitForConnected(5000))
0134         QApplication::restoreOverrideCursor();
0135 }
0136 
0137 QList< Protocol::ToolWidgetData > FicsProtocol::toolWidgets() {
0138     ToolWidgetData consoleData;
0139     consoleData.widget = console();
0140     consoleData.title = i18n("Server Console");
0141     consoleData.name = QStringLiteral("console");
0142     consoleData.type = ConsoleToolWidget;
0143     consoleData.owner = color();
0144 
0145     if ( !m_chat ) {
0146         m_chat = createChatWidget();
0147         connect ( m_chat, &ChatWidget::sendText, this, &FicsProtocol::sendChat );
0148     }
0149 
0150     ToolWidgetData chatData;
0151     chatData.widget = m_chat;
0152     chatData.title = i18n("Chat with %1", playerName());
0153     chatData.name = QStringLiteral("chat");
0154     chatData.type = ChatToolWidget;
0155 
0156     return {
0157         consoleData,
0158         chatData,
0159     };
0160 }
0161 
0162 void FicsProtocol::socketError() {
0163     QApplication::restoreOverrideCursor();
0164     Q_EMIT error( NetworkError, device()->errorString() );
0165 }
0166 
0167 void FicsProtocol::login ( const QString& username, const QString& password ) {
0168     otherPlayerName = username;
0169     write(username);
0170     sendPassword = true;
0171     this->password = password;
0172 }
0173 
0174 void FicsProtocol::setupOptions() {
0175     write("set style 12");
0176     write("iset seekremove 1");
0177     write("iset seekinfo 1");
0178     write("iset pendinfo 1");
0179     write("set seek 1");
0180 }
0181 
0182 void FicsProtocol::openGameDialog() {
0183     if ( m_widget ) {
0184         m_widget->setStatus(i18n("Login failed"), true);
0185         Settings::setAutoLogin(false);
0186         m_widget->setLoginEnabled(true);
0187         return;
0188     }
0189     QDialog* dialog = new QDialog ( qApp->activeWindow() );
0190     dialog->setWindowTitle(i18nc("@title:window", "Chess server"));
0191     auto mainLayout = new QVBoxLayout(dialog);
0192     dialog->setLayout(mainLayout);
0193     auto bBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
0194 
0195     bBox->button(QDialogButtonBox::Ok)->setText(i18n("Accept"));
0196     bBox->button(QDialogButtonBox::Ok)->setVisible(false);
0197     bBox->button(QDialogButtonBox::Ok)->setIcon(QIcon::fromTheme(QStringLiteral("dialog-ok-accept")));
0198 
0199     bBox->button(QDialogButtonBox::Cancel)->setText(i18n("Decline"));
0200     bBox->button(QDialogButtonBox::Cancel)->setVisible(false);
0201     bBox->button(QDialogButtonBox::Cancel)->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close")));
0202 
0203     m_widget = new FicsDialog ();
0204     m_widget->setServerName ( attribute( "server" ).toString());
0205     m_widget->setConsoleWidget ( console() );
0206     mainLayout->addWidget(m_widget);
0207     mainLayout->addWidget(bBox);
0208 
0209     connect ( bBox, &QDialogButtonBox::rejected, m_widget, &FicsDialog::decline );
0210     connect ( bBox, &QDialogButtonBox::accepted, m_widget, &FicsDialog::accept );
0211     connect ( m_widget, &FicsDialog::acceptButtonNeeded, bBox->button(QDialogButtonBox::Ok), &QPushButton::setVisible );
0212     connect ( m_widget, &FicsDialog::declineButtonNeeded, bBox->button(QDialogButtonBox::Cancel), &QPushButton::setVisible );
0213 
0214     connect ( m_widget, &FicsDialog::login, this, &FicsProtocol::login );
0215     connect ( m_widget, &FicsDialog::acceptSeek, this, &FicsProtocol::acceptSeek );
0216     connect ( m_widget, &FicsDialog::acceptChallenge, this, &FicsProtocol::acceptChallenge );
0217     connect ( m_widget, &FicsDialog::declineChallenge, this, &FicsProtocol::declineChallenge );
0218 
0219     connect ( this, &FicsProtocol::sessionStarted, m_widget, &FicsDialog::slotSessionStarted );
0220     connect ( this, &FicsProtocol::gameOfferReceived, m_widget, &FicsDialog::addGameOffer );
0221     connect ( this, &FicsProtocol::gameOfferRemoved, m_widget, &FicsDialog::removeGameOffer );
0222     connect ( this, &FicsProtocol::challengeReceived, m_widget, &FicsDialog::addChallenge );
0223     connect ( this, &FicsProtocol::gameOfferRemoved, m_widget, &FicsDialog::removeChallenge );
0224     connect ( m_widget, &FicsDialog::seekingChanged, this, &FicsProtocol::setSeeking );
0225 
0226     // connect ( dialog, &QDialog::accepted, this, &FicsProtocol::dialogAccepted );
0227     connect ( dialog, &QDialog::rejected, this, &FicsProtocol::dialogRejected );
0228 
0229     connect ( this, &FicsProtocol::initSuccesful, dialog, &QDialog::accept );
0230     /* TODO: The SLOT slotDialogAccepted() is not implemented. Need to recheck the intention */
0231     //connect ( this, &FicsProtocol::initSuccesful, m_widget, &FicsDialog::slotDialogAccepted );
0232     connect ( this, &FicsProtocol::error, dialog, &QDialog::deleteLater );
0233     if ( Settings::autoLogin() )
0234         m_widget->slotLogin();
0235     dialog->show();
0236 }
0237 
0238 bool FicsProtocol::parseStub(const QString& line) {
0239     Q_UNUSED(line);
0240     return false;
0241 }
0242 
0243 bool FicsProtocol::parseLine(const QString& line) {
0244     if ( line.isEmpty() || line.startsWith( QLatin1String("fics%") ) )
0245         return true;
0246     bool display = true;
0247     ChatWidget::MessageType type = ChatWidget::GeneralMessage;
0248     switch ( m_stage ) {
0249     case ConnectStage:
0250         if ( line.contains ( QLatin1String("login:") ) ) {
0251             type = ChatWidget::AccountMessage;
0252             openGameDialog();
0253         } else if ( line.contains ( QLatin1String("password:") ) ) {
0254             type = ChatWidget::AccountMessage;
0255             if ( sendPassword )
0256                 write(password);
0257             else
0258                 console()->setPasswordMode(true);
0259         } else if ( line.contains ( QLatin1String("Press return to enter the server") ) ) {
0260             type = ChatWidget::AccountMessage;
0261             write(QString());
0262         }
0263         // TODO: Check for incorrect logins
0264         else if ( line.contains ( QLatin1String("Starting FICS session") ) ) {
0265             type = ChatWidget::StatusMessage;
0266             m_stage = SeekStage;
0267             console()->setPasswordMode(false);
0268             setupOptions();
0269             QString name = line;
0270             name.remove ( 0, name.indexOf ( QLatin1String("session as ") ) + 11 );
0271             if ( name.contains ( QLatin1String("(U)") ) )
0272                 name.truncate ( name.indexOf ( QLatin1String("(U)") ) );
0273             else
0274                 name.truncate ( name.indexOf ( QLatin1Char ( ' ' ) ) );
0275             qCDebug(LOG_KNIGHTS) << QLatin1String("Your name is") << name;
0276             otherPlayerName = name;
0277             Q_EMIT sessionStarted();
0278         } else if ( line.contains ( QLatin1String("Invalid password") ) ) {
0279             m_widget->setLoginEnabled ( true );
0280             type = ChatWidget::AccountMessage;
0281             m_widget->setStatus(i18n("Invalid Password"), true);
0282         }
0283         break;
0284     case SeekStage:
0285         if ( line.startsWith( QLatin1String("<sc>") ) ) {
0286             display = false;
0287             Q_EMIT clearSeeks();
0288         } else if ( line.startsWith( QLatin1String("<sr>") ) ) {
0289             display = false;
0290             for ( const QString& str : line.split(QLatin1Char(' ') ) ) {
0291                 bool ok;
0292                 int id = str.toInt(&ok);
0293                 if ( ok )
0294                     Q_EMIT gameOfferRemoved(id);
0295             }
0296         } else if ( line.startsWith( QLatin1String("<s>") ) && seekExp.indexIn(line) > -1 ) {
0297             display = false;
0298             FicsGameOffer offer;
0299             int n = 1;
0300             offer.gameId = seekExp.cap(n++).toInt();
0301             offer.player.first = seekExp.cap(n++);
0302             n++; // Ignore titles for now, TODO
0303             offer.player.second = seekExp.cap(n++).toInt();
0304             offer.baseTime = seekExp.cap(n++).toInt();
0305             offer.timeIncrement = seekExp.cap(n++).toInt();
0306             offer.rated = ( !seekExp.cap(n).isEmpty() && seekExp.cap(n++)[0] == QLatin1Char('r') );
0307             offer.variant = seekExp.cap(n++);
0308             offer.color = parseColor(seekExp.cap(n++));
0309             offer.ratingRange.first = seekExp.cap(n++).toInt();
0310             offer.ratingRange.second = seekExp.cap(n++).toInt();
0311             offer.automatic = ( !seekExp.cap(n).isEmpty() && seekExp.cap(n++)[0] == QLatin1Char('t') );
0312             offer.formula = ( !seekExp.cap(n).isEmpty() && seekExp.cap(n++)[0] == QLatin1Char('t') );
0313             Q_EMIT gameOfferReceived ( offer );
0314         } else if ( line.startsWith( QLatin1String("<pf>") ) && challengeExp.indexIn ( line ) > -1 ) {
0315             display = false;
0316             FicsChallenge challenge;
0317             challenge.gameId = challengeExp.cap ( 1 ).toInt();
0318             challenge.player.first = challengeExp.cap ( 2 );
0319             int ratingPos = ( challengeExp.cap(2) == challengeExp.cap(3) ) ? 4 : 6;
0320             challenge.player.second = challengeExp.cap ( ratingPos ).toInt();
0321             Q_EMIT challengeReceived ( challenge );
0322         } else if ( line.startsWith( QLatin1String("<pr>") ) ) {
0323             display = false;
0324             for ( const QString& str : line.split( QLatin1Char(' ') ) ) {
0325                 bool ok;
0326                 int id = str.toInt(&ok);
0327                 if ( ok )
0328                     Q_EMIT challengeRemoved(id);
0329             }
0330         } else if ( gameStartedExp.indexIn ( line ) > -1 ) {
0331             qCDebug(LOG_KNIGHTS) << "Game Started" << line;
0332             type = ChatWidget::StatusMessage;
0333             QString player1 = gameStartedExp.cap ( 1 );
0334             QString player2 = gameStartedExp.cap ( 3 );
0335             Color color = NoColor;
0336             if ( player1 == otherPlayerName ) {
0337                 color = Black;
0338                 setPlayerName ( player2 );
0339             } else {
0340                 color = White;
0341                 setPlayerName ( player1 );
0342             }
0343             if ( byColor(color) != this ) {
0344                 qCDebug(LOG_KNIGHTS) << "Switching protocols";
0345                 // The color is different than was assigned at first
0346                 // We have to switch the protocols
0347                 Protocol* t = white();
0348                 setWhiteProtocol(black());
0349                 setBlackProtocol(t);
0350             }
0351             Protocol* opp = Protocol::byColor ( oppositeColor ( color ) );
0352             if ( opp->isLocal() )
0353                 opp->setPlayerName ( otherPlayerName );
0354 
0355             m_stage = PlayStage;
0356             initComplete();
0357         }
0358         break;
0359     case PlayStage:
0360         if ( moveRegExp.indexIn ( line ) > -1 ) {
0361             display = false;
0362             qCDebug(LOG_KNIGHTS) << moveRegExp.cap(1) << colorName(color());
0363             bool validMove = !( moveRegExp.cap ( 1 ) == QLatin1Char('W') && color() == White )
0364                              && !( moveRegExp.cap ( 1 ) == QLatin1Char('B') && color() == Black );
0365 
0366             const int whiteTimeLimit = moveRegExp.cap ( 3 ).toInt();
0367             const int blackTimeLimit = moveRegExp.cap ( 4 ).toInt();
0368             const QString  moveString = moveRegExp.cap ( 6 );
0369 
0370             qCDebug(LOG_KNIGHTS) << "Move:" << moveString;
0371 
0372             if ( moveString == QLatin1String("none") ) {
0373                 TimeControl tc;
0374                 tc.moves = 0;
0375                 tc.baseTime = QTime().addSecs(whiteTimeLimit);
0376                 tc.increment = moveRegExp.cap(2).toInt();
0377                 Manager::self()->setTimeControl(NoColor, tc);
0378                 break;
0379             }
0380 
0381             if ( validMove ) {
0382                 Move m;
0383                 if ( moveString == QLatin1String("o-o") ) {
0384                     // Short (king's rook) castling
0385                     m = Move::castling ( Move::KingSide, color() );
0386                 } else if ( moveString == QLatin1String("o-o-o") ) {
0387                     // Long (Queen's rock) castling
0388                     m = Move::castling ( Move::QueenSide, color() );
0389                 } else if ( moveStringExp.indexIn ( moveString ) > -1 ) {
0390                     m.setFrom( Pos(moveStringExp.cap(1)) );
0391                     m.setTo( Pos(moveStringExp.cap(2)) );
0392                     if ( !moveStringExp.cap(3).isEmpty() ) {
0393                         m.setFlag ( Move::Promote, true );
0394                         QChar typeChar = moveRegExp.cap ( 3 ).mid ( 1, 1 ) [0];
0395                         m.setPromotedType ( Piece::typeFromChar ( typeChar ) );
0396                     }
0397                 }
0398                 qCDebug(LOG_KNIGHTS) << "Valid move" << m;
0399                 Q_EMIT pieceMoved ( m );
0400             }
0401             Manager::self()->setCurrentTime ( White, QTime().addSecs ( whiteTimeLimit ) );
0402             Manager::self()->setCurrentTime ( Black, QTime().addSecs ( blackTimeLimit ) );
0403 
0404             if ( moveRegExp.cap(5).toInt() == 2 ) {
0405                 // TODO: Notify the manager that time is starting now
0406             }
0407         } else if ( offerExp.indexIn(line) > -1 ) {
0408             Offer offer;
0409             offer.id = offerExp.cap(1).toInt();
0410             offer.player = color();
0411             QString type = offerExp.cap(3);
0412             if ( type == QLatin1String("abort") )
0413                 offer.action = ActionAbort;
0414             else if ( type == QLatin1String("adjourn") )
0415                 offer.action = ActionAdjourn;
0416             else if ( type == QLatin1String("draw") )
0417                 offer.action = ActionDraw;
0418             else if ( type == QLatin1String("takeback") ) {
0419                 offer.action = ActionUndo;
0420                 offer.numberOfMoves = offerExp.cap(4).toInt();
0421             }
0422             m_offers.insert ( offer.id, offer );
0423             Manager::self()->sendOffer ( offer );
0424         } else if ( line.contains ( QLatin1String(" says:") ) ) {
0425             type = ChatWidget::ChatMessage;
0426             m_chat->addText ( line, type );
0427         } else if ( line.contains ( QLatin1String("lost contact or quit") ) )
0428             type = ChatWidget::AccountMessage;
0429         else if ( line.startsWith(QLatin1Char('{')) && line.contains(QLatin1Char('}'))) {
0430             if ( line.endsWith ( QLatin1String("1-0") ) ) {
0431                 type = ChatWidget::AccountMessage;
0432                 Q_EMIT gameOver ( White );
0433             } else if ( line.endsWith ( QLatin1String("1/2-1/2") ) || line.endsWith ( QLatin1Char('*') ) ) {
0434                 // Knights has no way of reporting aborted or unfinished games
0435                 // so we report aborted games as draws
0436                 type = ChatWidget::AccountMessage;
0437                 Q_EMIT gameOver ( NoColor );
0438             } else if ( line.endsWith ( QLatin1String("0-1") ) ) {
0439                 type = ChatWidget::AccountMessage;
0440                 Q_EMIT gameOver ( Black );
0441             }
0442         }
0443     }
0444 
0445     if ( display )
0446         writeToConsole ( line, type );
0447     return true;
0448 }
0449 
0450 Color FicsProtocol::parseColor ( QString str ) {
0451     if ( str.isEmpty() || str[0] == QLatin1Char('?') )
0452         return NoColor;
0453     if ( str[0] == QLatin1Char('W') )
0454         return White;
0455     if ( str[0] == QLatin1Char('B') )
0456         return Black;
0457     return NoColor;
0458 }
0459 
0460 void FicsProtocol::acceptSeek ( int id ) {
0461     write ( QLatin1String("play ") + QString::number(id) );
0462     m_seeking = false;
0463 }
0464 
0465 void FicsProtocol::acceptChallenge ( int id ) {
0466     write ( QLatin1String("accept ") + QString::number(id) );
0467     m_seeking = true;
0468 }
0469 
0470 void FicsProtocol::declineChallenge ( int id ) {
0471     write ( QLatin1String("decline ") + QString::number(id) );
0472 }
0473 
0474 void FicsProtocol::dialogRejected() {
0475     Q_EMIT error ( UserCancelled );
0476 }
0477 
0478 void FicsProtocol::setSeeking ( bool seek ) {
0479     m_seeking = seek;
0480     if ( seek ) {
0481         QByteArray seekText = "seek";
0482 
0483         TimeControl tc = Manager::self()->timeControl(color());
0484         seekText += ' ';
0485         seekText += QString::number ( 60 * tc.baseTime.hour() + tc.baseTime.minute() ).toLatin1();
0486         seekText += ' ';
0487         seekText += QString::number ( tc.increment ).toLatin1();
0488 
0489         seekText += m_widget->rated() ? " rated" : " unrated";
0490         /*
0491          * Commented out until I figure out a simple way to determine if a color should be forced.
0492         switch ( color() )
0493         {
0494             case White:
0495                 seekText += " white";
0496                 break;
0497             case Black:
0498                 seekText += " black";
0499                 break;
0500             default:
0501                 break;
0502         }
0503         */
0504         seekText += m_widget->autoAcceptChallenge() ? " auto" : " manual";
0505         qCDebug(LOG_KNIGHTS) << seekText;
0506         write(QLatin1String(seekText));
0507     } else
0508         write("unseek");
0509 }
0510 
0511 void FicsProtocol::resign() {
0512     write("resign");
0513 }
0514 
0515 void FicsProtocol::sendChat ( QString text ) {
0516     write ( QLatin1String("say ") + text );
0517 }
0518 
0519 void FicsProtocol::acceptOffer(const Offer& offer) {
0520     write ( QLatin1String("accept ") + QString::number(offer.id) );
0521 }
0522 
0523 void FicsProtocol::declineOffer(const Offer& offer) {
0524     write ( QLatin1String("decline ") + QString::number(offer.id) );
0525 }
0526 
0527 void FicsProtocol::makeOffer(const Offer& offer) {
0528     switch (offer.action) {
0529     case ActionDraw:
0530         write ( "draw" );
0531         break;
0532 
0533     case ActionPause:
0534         write ( "pause" );
0535         break;
0536 
0537     case ActionUndo:
0538         write ( QLatin1String("takeback ") + QString::number ( offer.numberOfMoves ) );
0539         break;
0540 
0541     case ActionResume:
0542         write ( "unpause" );
0543         break;
0544 
0545     case ActionAdjourn:
0546         write ( "adjourn" );
0547         break;
0548 
0549     case ActionAbort:
0550         write ( "abort" );
0551         break;
0552 
0553     default:
0554         break;
0555     }
0556 }
0557 
0558 #include "moc_ficsprotocol.cpp"