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

source code of qca/src/support/console.cpp