1 /*
2 * Copyright (C) 2006,2007 Justin Karneges <justin@affinix.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301 USA
18 *
19 */
20
21 #include "qca_support.h"
22
23 #include "qca_safeobj.h"
24 #include "qpipe.h"
25
26 #include <QMutex>
27 #include <QPointer>
28 #include <QTextCodec>
29
30 #ifdef Q_OS_WIN
31 #include <windows.h>
32 #else
33 #if defined(Q_OS_ANDROID) || defined(Q_OS_FREEBSD)
34 #include <termios.h>
35 #else
36 #include <sys/termios.h>
37 #endif
38 #include <fcntl.h>
39 #include <unistd.h>
40 #endif
41
42 #include <cstdio>
43 #include <cstdlib>
44
45 #define CONSOLEPROMPT_INPUT_MAX 56
46
47 Q_DECLARE_METATYPE(QCA::SecureArray)
48
49 namespace QCA {
50
51 //----------------------------------------------------------------------------
52 // ConsoleWorker
53 //----------------------------------------------------------------------------
54 class ConsoleWorker : public QObject
55 {
56 Q_OBJECT
57 private:
58 QPipeEnd in, out;
59 bool started;
60 QByteArray in_left, out_left;
61
62 public:
ConsoleWorker(QObject * parent=nullptr)63 ConsoleWorker(QObject *parent = nullptr)
64 : QObject(parent)
65 , in(this)
66 , out(this)
67 {
68 started = false;
69 }
70
~ConsoleWorker()71 ~ConsoleWorker() override
72 {
73 stop();
74 }
75
start(Q_PIPE_ID in_id,Q_PIPE_ID out_id)76 void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
77 {
78 Q_ASSERT(!started);
79
80 if (in_id != INVALID_Q_PIPE_ID) {
81 in.take(in_id, QPipeDevice::Read);
82 connect(&in, &QPipeEnd::readyRead, this, &ConsoleWorker::in_readyRead);
83 connect(&in, &QPipeEnd::closed, this, &ConsoleWorker::in_closed);
84 connect(&in, &QPipeEnd::error, this, &ConsoleWorker::in_error);
85 in.enable();
86 }
87
88 if (out_id != INVALID_Q_PIPE_ID) {
89 out.take(out_id, QPipeDevice::Write);
90 connect(&out, &QPipeEnd::bytesWritten, this, &ConsoleWorker::out_bytesWritten);
91 connect(&out, &QPipeEnd::closed, this, &ConsoleWorker::out_closed);
92 out.enable();
93 }
94
95 started = true;
96 }
97
stop()98 void stop()
99 {
100 if (!started)
101 return;
102
103 if (in.isValid())
104 in.finalizeAndRelease();
105 if (out.isValid())
106 out.release();
107
108 in_left = in.read();
109 out_left = out.takeBytesToWrite();
110
111 started = false;
112 }
113
isValid() const114 Q_INVOKABLE bool isValid() const
115 {
116 return in.isValid();
117 }
118
bytesAvailable() const119 Q_INVOKABLE int bytesAvailable() const
120 {
121 return in.bytesAvailable();
122 }
123
bytesToWrite() const124 Q_INVOKABLE int bytesToWrite() const
125 {
126 return in.bytesToWrite();
127 }
128
129 public Q_SLOTS:
130
setSecurityEnabled(bool enabled)131 void setSecurityEnabled(bool enabled)
132 {
133 if (in.isValid())
134 in.setSecurityEnabled(enabled);
135 if (out.isValid())
136 out.setSecurityEnabled(enabled);
137 }
138
read(int bytes=-1)139 QByteArray read(int bytes = -1)
140 {
141 return in.read(bytes);
142 }
143
write(const QByteArray & a)144 void write(const QByteArray &a)
145 {
146 out.write(a);
147 }
148
readSecure(int bytes=-1)149 QCA::SecureArray readSecure(int bytes = -1)
150 {
151 return in.readSecure(bytes);
152 }
153
writeSecure(const QCA::SecureArray & a)154 void writeSecure(const QCA::SecureArray &a)
155 {
156 out.writeSecure(a);
157 }
158
closeOutput()159 void closeOutput()
160 {
161 out.close();
162 }
163
164 public:
takeBytesToRead()165 QByteArray takeBytesToRead()
166 {
167 QByteArray a = in_left;
168 in_left.clear();
169 return a;
170 }
171
takeBytesToWrite()172 QByteArray takeBytesToWrite()
173 {
174 QByteArray a = out_left;
175 out_left.clear();
176 return a;
177 }
178
179 Q_SIGNALS:
180 void readyRead();
181 void bytesWritten(int bytes);
182 void inputClosed();
183 void outputClosed();
184
185 private Q_SLOTS:
in_readyRead()186 void in_readyRead()
187 {
188 emit readyRead();
189 }
190
out_bytesWritten(int bytes)191 void out_bytesWritten(int bytes)
192 {
193 emit bytesWritten(bytes);
194 }
195
in_closed()196 void in_closed()
197 {
198 emit inputClosed();
199 }
200
in_error(QCA::QPipeEnd::Error)201 void in_error(QCA::QPipeEnd::Error)
202 {
203 emit inputClosed();
204 }
205
out_closed()206 void out_closed()
207 {
208 emit outputClosed();
209 }
210 };
211
212 //----------------------------------------------------------------------------
213 // ConsoleThread
214 //----------------------------------------------------------------------------
215 class ConsoleThread : public SyncThread
216 {
217 Q_OBJECT
218 public:
219 ConsoleWorker *worker;
220 Q_PIPE_ID _in_id, _out_id;
221 QByteArray in_left, out_left;
222 QMutex call_mutex;
223
ConsoleThread(QObject * parent=nullptr)224 ConsoleThread(QObject *parent = nullptr)
225 : SyncThread(parent)
226 {
227 qRegisterMetaType<SecureArray>("QCA::SecureArray");
228 }
229
~ConsoleThread()230 ~ConsoleThread() override
231 {
232 stop();
233 }
234
start(Q_PIPE_ID in_id,Q_PIPE_ID out_id)235 void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
236 {
237 _in_id = in_id;
238 _out_id = out_id;
239 SyncThread::start();
240 }
241
stop()242 void stop()
243 {
244 SyncThread::stop();
245 }
246
mycall(QObject * obj,const char * method,const QVariantList & args=QVariantList ())247 QVariant mycall(QObject *obj, const char *method, const QVariantList &args = QVariantList())
248 {
249 QVariant ret;
250 bool ok;
251
252 call_mutex.lock();
253 ret = call(obj, method, args, &ok);
254 call_mutex.unlock();
255
256 Q_ASSERT(ok);
257 if (!ok) {
258 fprintf(stderr, "QCA: ConsoleWorker call [%s] failed.\n", method);
259 abort();
260 return QVariant();
261 }
262 return ret;
263 }
264
isValid()265 bool isValid()
266 {
267 return mycall(worker, "isValid").toBool();
268 }
269
setSecurityEnabled(bool enabled)270 void setSecurityEnabled(bool enabled)
271 {
272 mycall(worker, "setSecurityEnabled", QVariantList() << enabled);
273 }
274
read(int bytes=-1)275 QByteArray read(int bytes = -1)
276 {
277 return mycall(worker, "read", QVariantList() << bytes).toByteArray();
278 }
279
write(const QByteArray & a)280 void write(const QByteArray &a)
281 {
282 mycall(worker, "write", QVariantList() << a);
283 }
284
readSecure(int bytes=-1)285 SecureArray readSecure(int bytes = -1)
286 {
287 return mycall(worker, "readSecure", QVariantList() << bytes).value<SecureArray>();
288 }
289
writeSecure(const SecureArray & a)290 void writeSecure(const SecureArray &a)
291 {
292 mycall(worker, "writeSecure", QVariantList() << QVariant::fromValue<SecureArray>(a));
293 }
294
closeOutput()295 void closeOutput()
296 {
297 mycall(worker, "closeOutput");
298 }
299
bytesAvailable()300 int bytesAvailable()
301 {
302 return mycall(worker, "bytesAvailable").toInt();
303 }
304
bytesToWrite()305 int bytesToWrite()
306 {
307 return mycall(worker, "bytesToWrite").toInt();
308 }
309
takeBytesToRead()310 QByteArray takeBytesToRead()
311 {
312 QByteArray a = in_left;
313 in_left.clear();
314 return a;
315 }
316
takeBytesToWrite()317 QByteArray takeBytesToWrite()
318 {
319 QByteArray a = out_left;
320 out_left.clear();
321 return a;
322 }
323
324 Q_SIGNALS:
325 void readyRead();
326 void bytesWritten(int);
327 void inputClosed();
328 void outputClosed();
329
330 protected:
atStart()331 void atStart() override
332 {
333 worker = new ConsoleWorker;
334
335 // use direct connections here, so that the emits come from
336 // the other thread. we can also connect to our own
337 // signals to avoid having to make slots just to emit.
338 connect(worker, &ConsoleWorker::readyRead, this, &ConsoleThread::readyRead, Qt::DirectConnection);
339 connect(worker, &ConsoleWorker::bytesWritten, this, &ConsoleThread::bytesWritten, Qt::DirectConnection);
340 connect(worker, &ConsoleWorker::inputClosed, this, &ConsoleThread::inputClosed, Qt::DirectConnection);
341 connect(worker, &ConsoleWorker::outputClosed, this, &ConsoleThread::outputClosed, Qt::DirectConnection);
342
343 worker->start(_in_id, _out_id);
344 }
345
atEnd()346 void atEnd() override
347 {
348 in_left = worker->takeBytesToRead();
349 out_left = worker->takeBytesToWrite();
350 delete worker;
351 }
352 };
353
354 //----------------------------------------------------------------------------
355 // Console
356 //----------------------------------------------------------------------------
357 class ConsolePrivate : public QObject
358 {
359 Q_OBJECT
360 public:
361 Console *q;
362
363 bool started;
364 Console::Type type;
365 Console::ChannelMode cmode;
366 Console::TerminalMode mode;
367 ConsoleThread * thread;
368 ConsoleReference * ref;
369 Q_PIPE_ID in_id;
370
371 #ifdef Q_OS_WIN
372 DWORD old_mode;
373 #else
374 struct termios old_term_attr;
375 #endif
376
ConsolePrivate(Console * _q)377 ConsolePrivate(Console *_q)
378 : QObject(_q)
379 , q(_q)
380 {
381 started = false;
382 mode = Console::Default;
383 thread = new ConsoleThread(this);
384 ref = nullptr;
385 }
386
~ConsolePrivate()387 ~ConsolePrivate() override
388 {
389 delete thread;
390 setInteractive(Console::Default);
391 }
392
setInteractive(Console::TerminalMode m)393 void setInteractive(Console::TerminalMode m)
394 {
395 // no change
396 if (m == mode)
397 return;
398
399 if (m == Console::Interactive) {
400 #ifdef Q_OS_WIN
401 GetConsoleMode(in_id, &old_mode);
402 SetConsoleMode(in_id, old_mode & (~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT));
403 #else
404 int fd = in_id;
405 struct termios attr;
406 tcgetattr(fd, &attr);
407 old_term_attr = attr;
408
409 attr.c_lflag &= ~(ECHO); // turn off the echo flag
410 attr.c_lflag &= ~(ICANON); // no wait for a newline
411 attr.c_cc[VMIN] = 1; // read at least 1 char
412 attr.c_cc[VTIME] = 0; // set wait time to zero
413
414 // set the new attributes
415 tcsetattr(fd, TCSAFLUSH, &attr);
416 #endif
417 } else {
418 #ifdef Q_OS_WIN
419 SetConsoleMode(in_id, old_mode);
420 #else
421 int fd = in_id;
422 tcsetattr(fd, TCSANOW, &old_term_attr);
423 #endif
424 }
425
426 mode = m;
427 }
428 };
429
430 static Console *g_tty_console = nullptr, *g_stdio_console = nullptr;
431
Console(Type type,ChannelMode cmode,TerminalMode tmode,QObject * parent)432 Console::Console(Type type, ChannelMode cmode, TerminalMode tmode, QObject *parent)
433 : QObject(parent)
434 {
435 if (type == Tty) {
436 Q_ASSERT(g_tty_console == nullptr);
437 g_tty_console = this;
438 } else {
439 Q_ASSERT(g_stdio_console == nullptr);
440 g_stdio_console = this;
441 }
442
443 d = new ConsolePrivate(this);
444 d->type = type;
445 d->cmode = cmode;
446
447 Q_PIPE_ID in = INVALID_Q_PIPE_ID;
448 Q_PIPE_ID out = INVALID_Q_PIPE_ID;
449
450 #ifdef Q_OS_WIN
451 if (type == Tty) {
452 in = CreateFileA(
453 "CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
454 } else {
455 in = GetStdHandle(STD_INPUT_HANDLE);
456 }
457 #else
458 if (type == Tty) {
459 in = open("/dev/tty", O_RDONLY);
460 } else {
461 in = 0; // stdin
462 }
463 #endif
464 if (cmode == ReadWrite) {
465 #ifdef Q_OS_WIN
466 if (type == Tty) {
467 out = CreateFileA("CONOUT$",
468 GENERIC_READ | GENERIC_WRITE,
469 FILE_SHARE_READ | FILE_SHARE_WRITE,
470 NULL,
471 OPEN_EXISTING,
472 0,
473 NULL);
474 } else {
475 out = GetStdHandle(STD_OUTPUT_HANDLE);
476 }
477 #else
478 if (type == Tty) {
479 out = open("/dev/tty", O_WRONLY);
480 } else {
481 out = 1; // stdout
482 }
483 #endif
484 }
485
486 d->in_id = in;
487 d->setInteractive(tmode);
488 d->thread->start(in, out);
489 }
490
~Console()491 Console::~Console()
492 {
493 release();
494 Console::Type type = d->type;
495 delete d;
496 if (type == Tty)
497 g_tty_console = nullptr;
498 else
499 g_stdio_console = nullptr;
500 }
501
type() const502 Console::Type Console::type() const
503 {
504 return d->type;
505 }
506
channelMode() const507 Console::ChannelMode Console::channelMode() const
508 {
509 return d->cmode;
510 }
511
terminalMode() const512 Console::TerminalMode Console::terminalMode() const
513 {
514 return d->mode;
515 }
516
isStdinRedirected()517 bool Console::isStdinRedirected()
518 {
519 #ifdef Q_OS_WIN
520 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
521 DWORD mode;
522 if (GetConsoleMode(h, &mode))
523 return false;
524 return true;
525 #else
526 return (isatty(0) ? false : true); // 0 == stdin
527 #endif
528 }
529
isStdoutRedirected()530 bool Console::isStdoutRedirected()
531 {
532 #ifdef Q_OS_WIN
533 HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
534 DWORD mode;
535 if (GetConsoleMode(h, &mode))
536 return false;
537 return true;
538 #else
539 return (isatty(1) ? false : true); // 1 == stdout
540 #endif
541 }
542
ttyInstance()543 Console *Console::ttyInstance()
544 {
545 return g_tty_console;
546 }
547
stdioInstance()548 Console *Console::stdioInstance()
549 {
550 return g_stdio_console;
551 }
552
release()553 void Console::release()
554 {
555 d->thread->stop();
556 }
557
bytesLeftToRead()558 QByteArray Console::bytesLeftToRead()
559 {
560 return d->thread->takeBytesToRead();
561 }
562
bytesLeftToWrite()563 QByteArray Console::bytesLeftToWrite()
564 {
565 return d->thread->takeBytesToWrite();
566 }
567
568 //----------------------------------------------------------------------------
569 // ConsoleReference
570 //----------------------------------------------------------------------------
571 class ConsoleReferencePrivate : public QObject
572 {
573 Q_OBJECT
574 public:
575 ConsoleReference *q;
576
577 Console * console;
578 ConsoleThread * thread;
579 ConsoleReference::SecurityMode smode;
580 SafeTimer lateTrigger;
581 bool late_read, late_close;
582
ConsoleReferencePrivate(ConsoleReference * _q)583 ConsoleReferencePrivate(ConsoleReference *_q)
584 : QObject(_q)
585 , q(_q)
586 , lateTrigger(this)
587 {
588 console = nullptr;
589 thread = nullptr;
590 connect(&lateTrigger, &SafeTimer::timeout, this, &ConsoleReferencePrivate::doLate);
591 lateTrigger.setSingleShot(true);
592 }
593
594 private Q_SLOTS:
doLate()595 void doLate()
596 {
597 QPointer<QObject> self = this;
598 if (late_read)
599 emit q->readyRead();
600 if (!self)
601 return;
602 if (late_close)
603 emit q->inputClosed();
604 }
605 };
606
ConsoleReference(QObject * parent)607 ConsoleReference::ConsoleReference(QObject *parent)
608 : QObject(parent)
609 {
610 d = new ConsoleReferencePrivate(this);
611 }
612
~ConsoleReference()613 ConsoleReference::~ConsoleReference()
614 {
615 stop();
616 delete d;
617 }
618
start(Console * console,SecurityMode mode)619 bool ConsoleReference::start(Console *console, SecurityMode mode)
620 {
621 // make sure this reference isn't using a console already
622 Q_ASSERT(!d->console);
623
624 // one console reference at a time
625 Q_ASSERT(console->d->ref == nullptr);
626
627 // let's take it
628 d->console = console;
629 d->thread = d->console->d->thread;
630 d->console->d->ref = this;
631
632 const bool valid = d->thread->isValid();
633 const int avail = d->thread->bytesAvailable();
634
635 // pipe already closed and no data? consider this an error
636 if (!valid && avail == 0) {
637 d->console->d->ref = nullptr;
638 d->thread = nullptr;
639 d->console = nullptr;
640 return false;
641 }
642
643 // enable security? it will last for this active session only
644 d->smode = mode;
645 if (mode == SecurityEnabled)
646 d->thread->setSecurityEnabled(true);
647
648 connect(d->thread, &ConsoleThread::readyRead, this, &ConsoleReference::readyRead);
649 connect(d->thread, &ConsoleThread::bytesWritten, this, &ConsoleReference::bytesWritten);
650 connect(d->thread, &ConsoleThread::inputClosed, this, &ConsoleReference::inputClosed);
651 connect(d->thread, &ConsoleThread::outputClosed, this, &ConsoleReference::outputClosed);
652
653 d->late_read = false;
654 d->late_close = false;
655
656 if (avail > 0)
657 d->late_read = true;
658
659 if (!valid)
660 d->late_close = true;
661
662 if (d->late_read || d->late_close)
663 d->lateTrigger.start();
664
665 return true;
666 }
667
stop()668 void ConsoleReference::stop()
669 {
670 if (!d->console)
671 return;
672
673 d->lateTrigger.stop();
674
675 disconnect(d->thread, nullptr, this, nullptr);
676
677 // automatically disable security when we go inactive
678 d->thread->setSecurityEnabled(false);
679
680 d->console->d->ref = nullptr;
681 d->thread = nullptr;
682 d->console = nullptr;
683 }
684
console() const685 Console *ConsoleReference::console() const
686 {
687 return d->console;
688 }
689
securityMode() const690 ConsoleReference::SecurityMode ConsoleReference::securityMode() const
691 {
692 return d->smode;
693 }
694
read(int bytes)695 QByteArray ConsoleReference::read(int bytes)
696 {
697 return d->thread->read(bytes);
698 }
699
write(const QByteArray & a)700 void ConsoleReference::write(const QByteArray &a)
701 {
702 d->thread->write(a);
703 }
704
readSecure(int bytes)705 SecureArray ConsoleReference::readSecure(int bytes)
706 {
707 return d->thread->readSecure(bytes);
708 }
709
writeSecure(const SecureArray & a)710 void ConsoleReference::writeSecure(const SecureArray &a)
711 {
712 d->thread->writeSecure(a);
713 }
714
closeOutput()715 void ConsoleReference::closeOutput()
716 {
717 d->thread->closeOutput();
718 }
719
bytesAvailable() const720 int ConsoleReference::bytesAvailable() const
721 {
722 return d->thread->bytesAvailable();
723 }
724
bytesToWrite() const725 int ConsoleReference::bytesToWrite() const
726 {
727 return d->thread->bytesToWrite();
728 }
729
730 //----------------------------------------------------------------------------
731 // ConsolePrompt
732 //----------------------------------------------------------------------------
733 class ConsolePrompt::Private : public QObject
734 {
735 Q_OBJECT
736 public:
737 ConsolePrompt *q;
738
739 Synchronizer sync;
740 Console * con;
741 bool own_con;
742 ConsoleReference console;
743 QString promptStr;
744 SecureArray result;
745 bool waiting;
746 int at;
747 bool done;
748 bool charMode;
749 QTextCodec * codec;
750 QTextCodec::ConverterState *encstate, *decstate;
751
Private(ConsolePrompt * _q)752 Private(ConsolePrompt *_q)
753 : QObject(_q)
754 , q(_q)
755 , sync(_q)
756 , console(this)
757 {
758 connect(&console, &ConsoleReference::readyRead, this, &Private::con_readyRead);
759 connect(&console, &ConsoleReference::inputClosed, this, &Private::con_inputClosed);
760
761 con = nullptr;
762 own_con = false;
763 waiting = false;
764
765 #ifdef Q_OS_WIN
766 codec = QTextCodec::codecForMib(106); // UTF-8
767 #else
768 codec = QTextCodec::codecForLocale();
769 #endif
770 encstate = nullptr;
771 decstate = nullptr;
772 }
773
~Private()774 ~Private() override
775 {
776 reset();
777 }
778
reset()779 void reset()
780 {
781 delete encstate;
782 encstate = nullptr;
783 delete decstate;
784 decstate = nullptr;
785
786 console.stop();
787 if (own_con) {
788 delete con;
789 con = nullptr;
790 own_con = false;
791 }
792 }
793
start(bool _charMode)794 bool start(bool _charMode)
795 {
796 own_con = false;
797 con = Console::ttyInstance();
798 if (!con) {
799 con = new Console(Console::Tty, Console::ReadWrite, Console::Interactive);
800 own_con = true;
801 }
802
803 result.clear();
804 at = 0;
805 done = false;
806 charMode = _charMode;
807
808 encstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
809 decstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
810
811 if (!console.start(con, ConsoleReference::SecurityEnabled)) {
812 reset();
813 fprintf(stderr, "Console input not available or closed\n");
814 return false;
815 }
816
817 if (!charMode)
818 writeString(promptStr + QStringLiteral(": "));
819
820 return true;
821 }
822
writeString(const QString & str)823 void writeString(const QString &str)
824 {
825 console.writeSecure(codec->fromUnicode(str.unicode(), str.length(), encstate));
826 }
827
828 // process each char. internally store the result as utf16, which
829 // is easier to edit (e.g. backspace)
processChar(QChar c)830 bool processChar(QChar c)
831 {
832 if (charMode) {
833 appendChar(c);
834 done = true;
835 return false;
836 }
837
838 if (c == QLatin1Char('\r') || c == QLatin1Char('\n')) {
839 writeString(QStringLiteral("\n"));
840 done = true;
841 return false;
842 }
843
844 if (c == QLatin1Char('\b') || c.unicode() == 0x7f) {
845 if (at > 0) {
846 --at;
847 writeString(QStringLiteral("\b \b"));
848 result.resize(at * sizeof(ushort));
849 }
850 return true;
851 } else if (c.unicode() < 0x20)
852 return true;
853
854 if (at >= CONSOLEPROMPT_INPUT_MAX)
855 return true;
856
857 appendChar(c);
858
859 writeString(QStringLiteral("*"));
860 return true;
861 }
862
appendChar(QChar c)863 void appendChar(QChar c)
864 {
865 if ((at + 1) * (int)sizeof(ushort) > result.size())
866 result.resize((at + 1) * sizeof(ushort));
867 ushort *p = reinterpret_cast<ushort *>(result.data());
868 p[at++] = c.unicode();
869 }
870
convertToUtf8()871 void convertToUtf8()
872 {
873 // convert result from utf16 to utf8, securely
874 QTextCodec * codec = QTextCodec::codecForMib(106);
875 QTextCodec::ConverterState cstate(QTextCodec::IgnoreHeader);
876 SecureArray out;
877 const ushort * ustr = reinterpret_cast<ushort *>(result.data());
878 const int len = result.size() / sizeof(ushort);
879 for (int n = 0; n < len; ++n) {
880 QChar c(ustr[n]);
881 out += codec->fromUnicode(&c, 1, &cstate);
882 }
883 result = out;
884 }
885
886 private Q_SLOTS:
con_readyRead()887 void con_readyRead()
888 {
889 while (console.bytesAvailable() > 0) {
890 SecureArray buf = console.readSecure(1);
891 if (buf.isEmpty())
892 break;
893
894 // convert to unicode and process
895 const QString str = codec->toUnicode(buf.data(), 1, decstate);
896 bool quit = false;
897 for (const QChar &c : str) {
898 if (!processChar(c)) {
899 quit = true;
900 break;
901 }
902 }
903 if (quit)
904 break;
905 }
906
907 if (done) {
908 convertToUtf8();
909
910 reset();
911 if (waiting)
912 sync.conditionMet();
913 else
914 emit q->finished();
915 }
916 }
917
con_inputClosed()918 void con_inputClosed()
919 {
920 fprintf(stderr, "Console input closed\n");
921 if (!done) {
922 done = true;
923 result.clear();
924
925 reset();
926 if (waiting)
927 sync.conditionMet();
928 else
929 emit q->finished();
930 }
931 }
932 };
933
ConsolePrompt(QObject * parent)934 ConsolePrompt::ConsolePrompt(QObject *parent)
935 : QObject(parent)
936 {
937 d = new Private(this);
938 }
939
~ConsolePrompt()940 ConsolePrompt::~ConsolePrompt()
941 {
942 delete d;
943 }
944
getHidden(const QString & promptStr)945 void ConsolePrompt::getHidden(const QString &promptStr)
946 {
947 d->reset();
948
949 d->promptStr = promptStr;
950 if (!d->start(false)) {
951 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
952 return;
953 }
954 }
955
getChar()956 void ConsolePrompt::getChar()
957 {
958 d->reset();
959
960 if (!d->start(true)) {
961 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
962 return;
963 }
964 }
965
waitForFinished()966 void ConsolePrompt::waitForFinished()
967 {
968 // reparent the Console under us (for Synchronizer)
969 QObject *orig_parent = d->con->parent();
970 d->con->setParent(this);
971
972 // block while prompting
973 d->waiting = true;
974 d->sync.waitForCondition();
975 d->waiting = false;
976
977 // restore parent (if con still exists)
978 if (d->con)
979 d->con->setParent(orig_parent);
980 }
981
result() const982 SecureArray ConsolePrompt::result() const
983 {
984 return d->result;
985 }
986
resultChar() const987 QChar ConsolePrompt::resultChar() const
988 {
989 const QString str = QString::fromUtf8(d->result.toByteArray());
990
991 // this will never happen if getChar completes
992 if (str.isEmpty())
993 return QChar();
994
995 return str[0];
996 }
997
998 }
999
1000 #include "console.moc"
1001