File indexing completed on 2024-12-08 07:21:04

0001 /*
0002  * Copyright (C) 2006,2007  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017  * 02110-1301  USA
0018  *
0019  */
0020 
0021 #include "qca_support.h"
0022 
0023 #include "qca_safeobj.h"
0024 #include "qpipe.h"
0025 
0026 #include <QMutex>
0027 #include <QPointer>
0028 #include <QTextCodec>
0029 
0030 #ifdef Q_OS_WIN
0031 #include <windows.h>
0032 #else
0033 #include <fcntl.h>
0034 #include <termios.h>
0035 #include <unistd.h>
0036 #endif
0037 
0038 #include <cstdio>
0039 #include <cstdlib>
0040 
0041 #define CONSOLEPROMPT_INPUT_MAX 56
0042 
0043 Q_DECLARE_METATYPE(QCA::SecureArray)
0044 
0045 namespace QCA {
0046 
0047 //----------------------------------------------------------------------------
0048 // ConsoleWorker
0049 //----------------------------------------------------------------------------
0050 class ConsoleWorker : public QObject
0051 {
0052     Q_OBJECT
0053 private:
0054     QPipeEnd   in, out;
0055     bool       started;
0056     QByteArray in_left, out_left;
0057 
0058 public:
0059     ConsoleWorker(QObject *parent = nullptr)
0060         : QObject(parent)
0061         , in(this)
0062         , out(this)
0063     {
0064         started = false;
0065     }
0066 
0067     ~ConsoleWorker() override
0068     {
0069         stop();
0070     }
0071 
0072     void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
0073     {
0074         Q_ASSERT(!started);
0075 
0076         if (in_id != INVALID_Q_PIPE_ID) {
0077             in.take(in_id, QPipeDevice::Read);
0078             connect(&in, &QPipeEnd::readyRead, this, &ConsoleWorker::in_readyRead);
0079             connect(&in, &QPipeEnd::closed, this, &ConsoleWorker::in_closed);
0080             connect(&in, &QPipeEnd::error, this, &ConsoleWorker::in_error);
0081             in.enable();
0082         }
0083 
0084         if (out_id != INVALID_Q_PIPE_ID) {
0085             out.take(out_id, QPipeDevice::Write);
0086             connect(&out, &QPipeEnd::bytesWritten, this, &ConsoleWorker::out_bytesWritten);
0087             connect(&out, &QPipeEnd::closed, this, &ConsoleWorker::out_closed);
0088             out.enable();
0089         }
0090 
0091         started = true;
0092     }
0093 
0094     void stop()
0095     {
0096         if (!started)
0097             return;
0098 
0099         if (in.isValid())
0100             in.finalizeAndRelease();
0101         if (out.isValid())
0102             out.release();
0103 
0104         in_left  = in.read();
0105         out_left = out.takeBytesToWrite();
0106 
0107         started = false;
0108     }
0109 
0110     Q_INVOKABLE bool isValid() const
0111     {
0112         return in.isValid();
0113     }
0114 
0115     Q_INVOKABLE int bytesAvailable() const
0116     {
0117         return in.bytesAvailable();
0118     }
0119 
0120     Q_INVOKABLE int bytesToWrite() const
0121     {
0122         return in.bytesToWrite();
0123     }
0124 
0125 public Q_SLOTS:
0126 
0127     void setSecurityEnabled(bool enabled)
0128     {
0129         if (in.isValid())
0130             in.setSecurityEnabled(enabled);
0131         if (out.isValid())
0132             out.setSecurityEnabled(enabled);
0133     }
0134 
0135     QByteArray read(int bytes = -1)
0136     {
0137         return in.read(bytes);
0138     }
0139 
0140     void write(const QByteArray &a)
0141     {
0142         out.write(a);
0143     }
0144 
0145     QCA::SecureArray readSecure(int bytes = -1)
0146     {
0147         return in.readSecure(bytes);
0148     }
0149 
0150     void writeSecure(const QCA::SecureArray &a)
0151     {
0152         out.writeSecure(a);
0153     }
0154 
0155     void closeOutput()
0156     {
0157         out.close();
0158     }
0159 
0160 public:
0161     QByteArray takeBytesToRead()
0162     {
0163         QByteArray a = in_left;
0164         in_left.clear();
0165         return a;
0166     }
0167 
0168     QByteArray takeBytesToWrite()
0169     {
0170         QByteArray a = out_left;
0171         out_left.clear();
0172         return a;
0173     }
0174 
0175 Q_SIGNALS:
0176     void readyRead();
0177     void bytesWritten(int bytes);
0178     void inputClosed();
0179     void outputClosed();
0180 
0181 private Q_SLOTS:
0182     void in_readyRead()
0183     {
0184         emit readyRead();
0185     }
0186 
0187     void out_bytesWritten(int bytes)
0188     {
0189         emit bytesWritten(bytes);
0190     }
0191 
0192     void in_closed()
0193     {
0194         emit inputClosed();
0195     }
0196 
0197     void in_error(QCA::QPipeEnd::Error)
0198     {
0199         emit inputClosed();
0200     }
0201 
0202     void out_closed()
0203     {
0204         emit outputClosed();
0205     }
0206 };
0207 
0208 //----------------------------------------------------------------------------
0209 // ConsoleThread
0210 //----------------------------------------------------------------------------
0211 class ConsoleThread : public SyncThread
0212 {
0213     Q_OBJECT
0214 public:
0215     ConsoleWorker *worker;
0216     Q_PIPE_ID      _in_id, _out_id;
0217     QByteArray     in_left, out_left;
0218     QMutex         call_mutex;
0219 
0220     ConsoleThread(QObject *parent = nullptr)
0221         : SyncThread(parent)
0222     {
0223         qRegisterMetaType<SecureArray>("QCA::SecureArray");
0224     }
0225 
0226     ~ConsoleThread() override
0227     {
0228         stop();
0229     }
0230 
0231     void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
0232     {
0233         _in_id  = in_id;
0234         _out_id = out_id;
0235         SyncThread::start();
0236     }
0237 
0238     void stop()
0239     {
0240         SyncThread::stop();
0241     }
0242 
0243     QVariant mycall(QObject *obj, const char *method, const QVariantList &args = QVariantList())
0244     {
0245         QVariant ret;
0246         bool     ok;
0247 
0248         call_mutex.lock();
0249         ret = call(obj, method, args, &ok);
0250         call_mutex.unlock();
0251 
0252         Q_ASSERT(ok);
0253         if (!ok) {
0254             fprintf(stderr, "QCA: ConsoleWorker call [%s] failed.\n", method);
0255             abort();
0256             return QVariant();
0257         }
0258         return ret;
0259     }
0260 
0261     bool isValid()
0262     {
0263         return mycall(worker, "isValid").toBool();
0264     }
0265 
0266     void setSecurityEnabled(bool enabled)
0267     {
0268         mycall(worker, "setSecurityEnabled", QVariantList() << enabled);
0269     }
0270 
0271     QByteArray read(int bytes = -1)
0272     {
0273         return mycall(worker, "read", QVariantList() << bytes).toByteArray();
0274     }
0275 
0276     void write(const QByteArray &a)
0277     {
0278         mycall(worker, "write", QVariantList() << a);
0279     }
0280 
0281     SecureArray readSecure(int bytes = -1)
0282     {
0283         return mycall(worker, "readSecure", QVariantList() << bytes).value<SecureArray>();
0284     }
0285 
0286     void writeSecure(const SecureArray &a)
0287     {
0288         mycall(worker, "writeSecure", QVariantList() << QVariant::fromValue<SecureArray>(a));
0289     }
0290 
0291     void closeOutput()
0292     {
0293         mycall(worker, "closeOutput");
0294     }
0295 
0296     int bytesAvailable()
0297     {
0298         return mycall(worker, "bytesAvailable").toInt();
0299     }
0300 
0301     int bytesToWrite()
0302     {
0303         return mycall(worker, "bytesToWrite").toInt();
0304     }
0305 
0306     QByteArray takeBytesToRead()
0307     {
0308         QByteArray a = in_left;
0309         in_left.clear();
0310         return a;
0311     }
0312 
0313     QByteArray takeBytesToWrite()
0314     {
0315         QByteArray a = out_left;
0316         out_left.clear();
0317         return a;
0318     }
0319 
0320 Q_SIGNALS:
0321     void readyRead();
0322     void bytesWritten(int);
0323     void inputClosed();
0324     void outputClosed();
0325 
0326 protected:
0327     void atStart() override
0328     {
0329         worker = new ConsoleWorker;
0330 
0331         // use direct connections here, so that the emits come from
0332         //   the other thread.  we can also connect to our own
0333         //   signals to avoid having to make slots just to emit.
0334         connect(worker, &ConsoleWorker::readyRead, this, &ConsoleThread::readyRead, Qt::DirectConnection);
0335         connect(worker, &ConsoleWorker::bytesWritten, this, &ConsoleThread::bytesWritten, Qt::DirectConnection);
0336         connect(worker, &ConsoleWorker::inputClosed, this, &ConsoleThread::inputClosed, Qt::DirectConnection);
0337         connect(worker, &ConsoleWorker::outputClosed, this, &ConsoleThread::outputClosed, Qt::DirectConnection);
0338 
0339         worker->start(_in_id, _out_id);
0340     }
0341 
0342     void atEnd() override
0343     {
0344         in_left  = worker->takeBytesToRead();
0345         out_left = worker->takeBytesToWrite();
0346         delete worker;
0347     }
0348 };
0349 
0350 //----------------------------------------------------------------------------
0351 // Console
0352 //----------------------------------------------------------------------------
0353 class ConsolePrivate : public QObject
0354 {
0355     Q_OBJECT
0356 public:
0357     Console *q;
0358 
0359     bool                  started;
0360     Console::Type         type;
0361     Console::ChannelMode  cmode;
0362     Console::TerminalMode mode;
0363     ConsoleThread        *thread;
0364     ConsoleReference     *ref;
0365     Q_PIPE_ID             in_id;
0366 
0367 #ifdef Q_OS_WIN
0368     DWORD old_mode;
0369 #else
0370     struct termios old_term_attr;
0371 #endif
0372 
0373     ConsolePrivate(Console *_q)
0374         : QObject(_q)
0375         , q(_q)
0376     {
0377         started = false;
0378         mode    = Console::Default;
0379         thread  = new ConsoleThread(this);
0380         ref     = nullptr;
0381     }
0382 
0383     ~ConsolePrivate() override
0384     {
0385         delete thread;
0386         setInteractive(Console::Default);
0387     }
0388 
0389     void setInteractive(Console::TerminalMode m)
0390     {
0391         // no change
0392         if (m == mode)
0393             return;
0394 
0395         if (m == Console::Interactive) {
0396 #ifdef Q_OS_WIN
0397             GetConsoleMode(in_id, &old_mode);
0398             SetConsoleMode(in_id, old_mode & (~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT));
0399 #else
0400             int            fd = in_id;
0401             struct termios attr;
0402             tcgetattr(fd, &attr);
0403             old_term_attr = attr;
0404 
0405             attr.c_lflag &= ~(ECHO);   // turn off the echo flag
0406             attr.c_lflag &= ~(ICANON); // no wait for a newline
0407             attr.c_cc[VMIN]  = 1;      // read at least 1 char
0408             attr.c_cc[VTIME] = 0;      // set wait time to zero
0409 
0410             // set the new attributes
0411             tcsetattr(fd, TCSAFLUSH, &attr);
0412 #endif
0413         } else {
0414 #ifdef Q_OS_WIN
0415             SetConsoleMode(in_id, old_mode);
0416 #else
0417             int fd = in_id;
0418             tcsetattr(fd, TCSANOW, &old_term_attr);
0419 #endif
0420         }
0421 
0422         mode = m;
0423     }
0424 };
0425 
0426 static Console *g_tty_console = nullptr, *g_stdio_console = nullptr;
0427 
0428 Console::Console(Type type, ChannelMode cmode, TerminalMode tmode, QObject *parent)
0429     : QObject(parent)
0430 {
0431     if (type == Tty) {
0432         Q_ASSERT(g_tty_console == nullptr);
0433         g_tty_console = this;
0434     } else {
0435         Q_ASSERT(g_stdio_console == nullptr);
0436         g_stdio_console = this;
0437     }
0438 
0439     d        = new ConsolePrivate(this);
0440     d->type  = type;
0441     d->cmode = cmode;
0442 
0443     Q_PIPE_ID in  = INVALID_Q_PIPE_ID;
0444     Q_PIPE_ID out = INVALID_Q_PIPE_ID;
0445 
0446 #ifdef Q_OS_WIN
0447     if (type == Tty) {
0448         in = CreateFileA(
0449             "CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
0450     } else {
0451         in = GetStdHandle(STD_INPUT_HANDLE);
0452     }
0453 #else
0454     if (type == Tty) {
0455         in = open("/dev/tty", O_RDONLY);
0456     } else {
0457         in = 0; // stdin
0458     }
0459 #endif
0460     if (cmode == ReadWrite) {
0461 #ifdef Q_OS_WIN
0462         if (type == Tty) {
0463             out = CreateFileA("CONOUT$",
0464                               GENERIC_READ | GENERIC_WRITE,
0465                               FILE_SHARE_READ | FILE_SHARE_WRITE,
0466                               NULL,
0467                               OPEN_EXISTING,
0468                               0,
0469                               NULL);
0470         } else {
0471             out = GetStdHandle(STD_OUTPUT_HANDLE);
0472         }
0473 #else
0474         if (type == Tty) {
0475             out = open("/dev/tty", O_WRONLY);
0476         } else {
0477             out = 1; // stdout
0478         }
0479 #endif
0480     }
0481 
0482     d->in_id = in;
0483     d->setInteractive(tmode);
0484     d->thread->start(in, out);
0485 }
0486 
0487 Console::~Console()
0488 {
0489     release();
0490     Console::Type type = d->type;
0491     delete d;
0492     if (type == Tty)
0493         g_tty_console = nullptr;
0494     else
0495         g_stdio_console = nullptr;
0496 }
0497 
0498 Console::Type Console::type() const
0499 {
0500     return d->type;
0501 }
0502 
0503 Console::ChannelMode Console::channelMode() const
0504 {
0505     return d->cmode;
0506 }
0507 
0508 Console::TerminalMode Console::terminalMode() const
0509 {
0510     return d->mode;
0511 }
0512 
0513 bool Console::isStdinRedirected()
0514 {
0515 #ifdef Q_OS_WIN
0516     HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
0517     DWORD  mode;
0518     if (GetConsoleMode(h, &mode))
0519         return false;
0520     return true;
0521 #else
0522     return (isatty(0) ? false : true); // 0 == stdin
0523 #endif
0524 }
0525 
0526 bool Console::isStdoutRedirected()
0527 {
0528 #ifdef Q_OS_WIN
0529     HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
0530     DWORD  mode;
0531     if (GetConsoleMode(h, &mode))
0532         return false;
0533     return true;
0534 #else
0535     return (isatty(1) ? false : true); // 1 == stdout
0536 #endif
0537 }
0538 
0539 Console *Console::ttyInstance()
0540 {
0541     return g_tty_console;
0542 }
0543 
0544 Console *Console::stdioInstance()
0545 {
0546     return g_stdio_console;
0547 }
0548 
0549 void Console::release()
0550 {
0551     d->thread->stop();
0552 }
0553 
0554 QByteArray Console::bytesLeftToRead()
0555 {
0556     return d->thread->takeBytesToRead();
0557 }
0558 
0559 QByteArray Console::bytesLeftToWrite()
0560 {
0561     return d->thread->takeBytesToWrite();
0562 }
0563 
0564 //----------------------------------------------------------------------------
0565 // ConsoleReference
0566 //----------------------------------------------------------------------------
0567 class ConsoleReferencePrivate : public QObject
0568 {
0569     Q_OBJECT
0570 public:
0571     ConsoleReference *q;
0572 
0573     Console                       *console;
0574     ConsoleThread                 *thread;
0575     ConsoleReference::SecurityMode smode;
0576     SafeTimer                      lateTrigger;
0577     bool                           late_read, late_close;
0578 
0579     ConsoleReferencePrivate(ConsoleReference *_q)
0580         : QObject(_q)
0581         , q(_q)
0582         , lateTrigger(this)
0583     {
0584         console = nullptr;
0585         thread  = nullptr;
0586         connect(&lateTrigger, &SafeTimer::timeout, this, &ConsoleReferencePrivate::doLate);
0587         lateTrigger.setSingleShot(true);
0588     }
0589 
0590 private Q_SLOTS:
0591     void doLate()
0592     {
0593         QPointer<QObject> self = this;
0594         if (late_read)
0595             emit q->readyRead();
0596         if (!self)
0597             return;
0598         if (late_close)
0599             emit q->inputClosed();
0600     }
0601 };
0602 
0603 ConsoleReference::ConsoleReference(QObject *parent)
0604     : QObject(parent)
0605 {
0606     d = new ConsoleReferencePrivate(this);
0607 }
0608 
0609 ConsoleReference::~ConsoleReference()
0610 {
0611     stop();
0612     delete d;
0613 }
0614 
0615 bool ConsoleReference::start(Console *console, SecurityMode mode)
0616 {
0617     // make sure this reference isn't using a console already
0618     Q_ASSERT(!d->console);
0619 
0620     // one console reference at a time
0621     Q_ASSERT(console->d->ref == nullptr);
0622 
0623     // let's take it
0624     d->console         = console;
0625     d->thread          = d->console->d->thread;
0626     d->console->d->ref = this;
0627 
0628     const bool valid = d->thread->isValid();
0629     const int  avail = d->thread->bytesAvailable();
0630 
0631     // pipe already closed and no data?  consider this an error
0632     if (!valid && avail == 0) {
0633         d->console->d->ref = nullptr;
0634         d->thread          = nullptr;
0635         d->console         = nullptr;
0636         return false;
0637     }
0638 
0639     // enable security?  it will last for this active session only
0640     d->smode = mode;
0641     if (mode == SecurityEnabled)
0642         d->thread->setSecurityEnabled(true);
0643 
0644     connect(d->thread, &ConsoleThread::readyRead, this, &ConsoleReference::readyRead);
0645     connect(d->thread, &ConsoleThread::bytesWritten, this, &ConsoleReference::bytesWritten);
0646     connect(d->thread, &ConsoleThread::inputClosed, this, &ConsoleReference::inputClosed);
0647     connect(d->thread, &ConsoleThread::outputClosed, this, &ConsoleReference::outputClosed);
0648 
0649     d->late_read  = false;
0650     d->late_close = false;
0651 
0652     if (avail > 0)
0653         d->late_read = true;
0654 
0655     if (!valid)
0656         d->late_close = true;
0657 
0658     if (d->late_read || d->late_close)
0659         d->lateTrigger.start();
0660 
0661     return true;
0662 }
0663 
0664 void ConsoleReference::stop()
0665 {
0666     if (!d->console)
0667         return;
0668 
0669     d->lateTrigger.stop();
0670 
0671     disconnect(d->thread, nullptr, this, nullptr);
0672 
0673     // automatically disable security when we go inactive
0674     d->thread->setSecurityEnabled(false);
0675 
0676     d->console->d->ref = nullptr;
0677     d->thread          = nullptr;
0678     d->console         = nullptr;
0679 }
0680 
0681 Console *ConsoleReference::console() const
0682 {
0683     return d->console;
0684 }
0685 
0686 ConsoleReference::SecurityMode ConsoleReference::securityMode() const
0687 {
0688     return d->smode;
0689 }
0690 
0691 QByteArray ConsoleReference::read(int bytes)
0692 {
0693     return d->thread->read(bytes);
0694 }
0695 
0696 void ConsoleReference::write(const QByteArray &a)
0697 {
0698     d->thread->write(a);
0699 }
0700 
0701 SecureArray ConsoleReference::readSecure(int bytes)
0702 {
0703     return d->thread->readSecure(bytes);
0704 }
0705 
0706 void ConsoleReference::writeSecure(const SecureArray &a)
0707 {
0708     d->thread->writeSecure(a);
0709 }
0710 
0711 void ConsoleReference::closeOutput()
0712 {
0713     d->thread->closeOutput();
0714 }
0715 
0716 int ConsoleReference::bytesAvailable() const
0717 {
0718     return d->thread->bytesAvailable();
0719 }
0720 
0721 int ConsoleReference::bytesToWrite() const
0722 {
0723     return d->thread->bytesToWrite();
0724 }
0725 
0726 //----------------------------------------------------------------------------
0727 // ConsolePrompt
0728 //----------------------------------------------------------------------------
0729 class ConsolePrompt::Private : public QObject
0730 {
0731     Q_OBJECT
0732 public:
0733     ConsolePrompt *q;
0734 
0735     Synchronizer                sync;
0736     Console                    *con;
0737     bool                        own_con;
0738     ConsoleReference            console;
0739     QString                     promptStr;
0740     SecureArray                 result;
0741     bool                        waiting;
0742     int                         at;
0743     bool                        done;
0744     bool                        charMode;
0745     QTextCodec                 *codec;
0746     QTextCodec::ConverterState *encstate, *decstate;
0747 
0748     Private(ConsolePrompt *_q)
0749         : QObject(_q)
0750         , q(_q)
0751         , sync(_q)
0752         , console(this)
0753     {
0754         connect(&console, &ConsoleReference::readyRead, this, &Private::con_readyRead);
0755         connect(&console, &ConsoleReference::inputClosed, this, &Private::con_inputClosed);
0756 
0757         con     = nullptr;
0758         own_con = false;
0759         waiting = false;
0760 
0761 #ifdef Q_OS_WIN
0762         codec = QTextCodec::codecForMib(106); // UTF-8
0763 #else
0764         codec = QTextCodec::codecForLocale();
0765 #endif
0766         encstate = nullptr;
0767         decstate = nullptr;
0768     }
0769 
0770     ~Private() override
0771     {
0772         reset();
0773     }
0774 
0775     void reset()
0776     {
0777         delete encstate;
0778         encstate = nullptr;
0779         delete decstate;
0780         decstate = nullptr;
0781 
0782         console.stop();
0783         if (own_con) {
0784             delete con;
0785             con     = nullptr;
0786             own_con = false;
0787         }
0788     }
0789 
0790     bool start(bool _charMode)
0791     {
0792         own_con = false;
0793         con     = Console::ttyInstance();
0794         if (!con) {
0795             con     = new Console(Console::Tty, Console::ReadWrite, Console::Interactive);
0796             own_con = true;
0797         }
0798 
0799         result.clear();
0800         at       = 0;
0801         done     = false;
0802         charMode = _charMode;
0803 
0804         encstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
0805         decstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
0806 
0807         if (!console.start(con, ConsoleReference::SecurityEnabled)) {
0808             reset();
0809             fprintf(stderr, "Console input not available or closed\n");
0810             return false;
0811         }
0812 
0813         if (!charMode)
0814             writeString(promptStr + QStringLiteral(": "));
0815 
0816         return true;
0817     }
0818 
0819     void writeString(const QString &str)
0820     {
0821         console.writeSecure(codec->fromUnicode(str.unicode(), str.length(), encstate));
0822     }
0823 
0824     // process each char.  internally store the result as utf16, which
0825     //   is easier to edit (e.g. backspace)
0826     bool processChar(QChar c)
0827     {
0828         if (charMode) {
0829             appendChar(c);
0830             done = true;
0831             return false;
0832         }
0833 
0834         if (c == QLatin1Char('\r') || c == QLatin1Char('\n')) {
0835             writeString(QStringLiteral("\n"));
0836             done = true;
0837             return false;
0838         }
0839 
0840         if (c == QLatin1Char('\b') || c.unicode() == 0x7f) {
0841             if (at > 0) {
0842                 --at;
0843                 writeString(QStringLiteral("\b \b"));
0844                 result.resize(at * sizeof(ushort));
0845             }
0846             return true;
0847         } else if (c.unicode() < 0x20)
0848             return true;
0849 
0850         if (at >= CONSOLEPROMPT_INPUT_MAX)
0851             return true;
0852 
0853         appendChar(c);
0854 
0855         writeString(QStringLiteral("*"));
0856         return true;
0857     }
0858 
0859     void appendChar(QChar c)
0860     {
0861         if ((at + 1) * (int)sizeof(ushort) > result.size())
0862             result.resize((at + 1) * sizeof(ushort));
0863         ushort *p = reinterpret_cast<ushort *>(result.data());
0864         p[at++]   = c.unicode();
0865     }
0866 
0867     void convertToUtf8()
0868     {
0869         // convert result from utf16 to utf8, securely
0870         QTextCodec                *codec = QTextCodec::codecForMib(106);
0871         QTextCodec::ConverterState cstate(QTextCodec::IgnoreHeader);
0872         SecureArray                out;
0873         const ushort              *ustr = reinterpret_cast<ushort *>(result.data());
0874         const int                  len  = result.size() / sizeof(ushort);
0875         for (int n = 0; n < len; ++n) {
0876             QChar c(ustr[n]);
0877             out += codec->fromUnicode(&c, 1, &cstate);
0878         }
0879         result = out;
0880     }
0881 
0882 private Q_SLOTS:
0883     void con_readyRead()
0884     {
0885         while (console.bytesAvailable() > 0) {
0886             SecureArray buf = console.readSecure(1);
0887             if (buf.isEmpty())
0888                 break;
0889 
0890             // convert to unicode and process
0891             const QString str  = codec->toUnicode(buf.data(), 1, decstate);
0892             bool          quit = false;
0893             for (const QChar &c : str) {
0894                 if (!processChar(c)) {
0895                     quit = true;
0896                     break;
0897                 }
0898             }
0899             if (quit)
0900                 break;
0901         }
0902 
0903         if (done) {
0904             convertToUtf8();
0905 
0906             reset();
0907             if (waiting)
0908                 sync.conditionMet();
0909             else
0910                 emit q->finished();
0911         }
0912     }
0913 
0914     void con_inputClosed()
0915     {
0916         fprintf(stderr, "Console input closed\n");
0917         if (!done) {
0918             done = true;
0919             result.clear();
0920 
0921             reset();
0922             if (waiting)
0923                 sync.conditionMet();
0924             else
0925                 emit q->finished();
0926         }
0927     }
0928 };
0929 
0930 ConsolePrompt::ConsolePrompt(QObject *parent)
0931     : QObject(parent)
0932 {
0933     d = new Private(this);
0934 }
0935 
0936 ConsolePrompt::~ConsolePrompt()
0937 {
0938     delete d;
0939 }
0940 
0941 void ConsolePrompt::getHidden(const QString &promptStr)
0942 {
0943     d->reset();
0944 
0945     d->promptStr = promptStr;
0946     if (!d->start(false)) {
0947         QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
0948         return;
0949     }
0950 }
0951 
0952 void ConsolePrompt::getChar()
0953 {
0954     d->reset();
0955 
0956     if (!d->start(true)) {
0957         QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
0958         return;
0959     }
0960 }
0961 
0962 void ConsolePrompt::waitForFinished()
0963 {
0964     // reparent the Console under us (for Synchronizer)
0965     QObject *orig_parent = d->con->parent();
0966     d->con->setParent(this);
0967 
0968     // block while prompting
0969     d->waiting = true;
0970     d->sync.waitForCondition();
0971     d->waiting = false;
0972 
0973     // restore parent (if con still exists)
0974     if (d->con)
0975         d->con->setParent(orig_parent);
0976 }
0977 
0978 SecureArray ConsolePrompt::result() const
0979 {
0980     return d->result;
0981 }
0982 
0983 QChar ConsolePrompt::resultChar() const
0984 {
0985     const QString str = QString::fromUtf8(d->result.toByteArray());
0986 
0987     // this will never happen if getChar completes
0988     if (str.isEmpty())
0989         return QChar();
0990 
0991     return str[0];
0992 }
0993 
0994 }
0995 
0996 #include "console.moc"