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"