2
* Copyright (C) 2006,2007 Justin Karneges <justin@affinix.com>
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.
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.
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
21
#include "qca_support.h"
24
#include "qca_safeobj.h"
33
# include <sys/termios.h>
41
#define CONSOLEPROMPT_INPUT_MAX 56
43
Q_DECLARE_METATYPE(QCA::SecureArray)
47
//----------------------------------------------------------------------------
49
//----------------------------------------------------------------------------
50
class ConsoleWorker : public QObject
56
QByteArray in_left, out_left;
59
ConsoleWorker(QObject *parent = 0) : QObject(parent), in(this), out(this)
69
void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
73
if(in_id != INVALID_Q_PIPE_ID)
75
in.take(in_id, QPipeDevice::Read);
76
connect(&in, SIGNAL(readyRead()), SLOT(in_readyRead()));
77
connect(&in, SIGNAL(closed()), SLOT(in_closed()));
78
connect(&in, SIGNAL(error(QCA::QPipeEnd::Error)), SLOT(in_error(QCA::QPipeEnd::Error)));
82
if(out_id != INVALID_Q_PIPE_ID)
84
out.take(out_id, QPipeDevice::Write);
85
connect(&out, SIGNAL(bytesWritten(int)), SLOT(out_bytesWritten(int)));
86
connect(&out, SIGNAL(closed()), SLOT(out_closed()));
99
in.finalizeAndRelease();
104
out_left = out.takeBytesToWrite();
115
void setSecurityEnabled(bool enabled)
118
in.setSecurityEnabled(enabled);
120
out.setSecurityEnabled(enabled);
123
QByteArray read(int bytes = -1)
125
return in.read(bytes);
128
void write(const QByteArray &a)
133
QCA::SecureArray readSecure(int bytes = -1)
135
return in.readSecure(bytes);
138
void writeSecure(const QCA::SecureArray &a)
148
int bytesAvailable() const
150
return in.bytesAvailable();
153
int bytesToWrite() const
155
return in.bytesToWrite();
159
QByteArray takeBytesToRead()
161
QByteArray a = in_left;
166
QByteArray takeBytesToWrite()
168
QByteArray a = out_left;
175
void bytesWritten(int bytes);
185
void out_bytesWritten(int bytes)
187
emit bytesWritten(bytes);
195
void in_error(QCA::QPipeEnd::Error)
206
//----------------------------------------------------------------------------
208
//----------------------------------------------------------------------------
209
class ConsoleThread : public SyncThread
213
ConsoleWorker *worker;
214
Q_PIPE_ID _in_id, _out_id;
215
QByteArray in_left, out_left;
218
ConsoleThread(QObject *parent = 0) : SyncThread(parent)
220
qRegisterMetaType<SecureArray>("QCA::SecureArray");
228
void start(Q_PIPE_ID in_id, Q_PIPE_ID out_id)
240
QVariant mycall(QObject *obj, const char *method, const QVariantList &args = QVariantList())
246
ret = call(obj, method, args, &ok);
252
fprintf(stderr, "QCA: ConsoleWorker call [%s] failed.\n", method);
261
return mycall(worker, "isValid").toBool();
264
void setSecurityEnabled(bool enabled)
266
mycall(worker, "setSecurityEnabled", QVariantList() << enabled);
269
QByteArray read(int bytes = -1)
271
return mycall(worker, "read", QVariantList() << bytes).toByteArray();
274
void write(const QByteArray &a)
276
mycall(worker, "write", QVariantList() << a);
279
SecureArray readSecure(int bytes = -1)
281
return qVariantValue<SecureArray>(mycall(worker, "readSecure", QVariantList() << bytes));
284
void writeSecure(const SecureArray &a)
286
mycall(worker, "writeSecure", QVariantList() << qVariantFromValue<SecureArray>(a));
291
mycall(worker, "closeOutput");
296
return mycall(worker, "bytesAvailable").toInt();
301
return mycall(worker, "bytesToWrite").toInt();
304
QByteArray takeBytesToRead()
306
QByteArray a = in_left;
311
QByteArray takeBytesToWrite()
313
QByteArray a = out_left;
320
void bytesWritten(int);
325
virtual void atStart()
327
worker = new ConsoleWorker;
329
// use direct connections here, so that the emits come from
330
// the other thread. we can also connect to our own
331
// signals to avoid having to make slots just to emit.
332
connect(worker, SIGNAL(readyRead()), SIGNAL(readyRead()), Qt::DirectConnection);
333
connect(worker, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)), Qt::DirectConnection);
334
connect(worker, SIGNAL(inputClosed()), SIGNAL(inputClosed()), Qt::DirectConnection);
335
connect(worker, SIGNAL(outputClosed()), SIGNAL(outputClosed()), Qt::DirectConnection);
337
worker->start(_in_id, _out_id);
342
in_left = worker->takeBytesToRead();
343
out_left = worker->takeBytesToWrite();
348
//----------------------------------------------------------------------------
350
//----------------------------------------------------------------------------
351
class ConsolePrivate : public QObject
359
Console::ChannelMode cmode;
360
Console::TerminalMode mode;
361
ConsoleThread *thread;
362
ConsoleReference *ref;
368
struct termios old_term_attr;
371
ConsolePrivate(Console *_q) : QObject(_q), q(_q)
374
mode = Console::Default;
375
thread = new ConsoleThread(this);
382
setInteractive(Console::Default);
385
void setInteractive(Console::TerminalMode m)
391
if(m == Console::Interactive)
394
GetConsoleMode(in_id, &old_mode);
395
SetConsoleMode(in_id, old_mode & (~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT));
399
tcgetattr(fd, &attr);
400
old_term_attr = attr;
402
attr.c_lflag &= ~(ECHO); // turn off the echo flag
403
attr.c_lflag &= ~(ICANON); // no wait for a newline
404
attr.c_cc[VMIN] = 1; // read at least 1 char
405
attr.c_cc[VTIME] = 0; // set wait time to zero
407
// set the new attributes
408
tcsetattr(fd, TCSAFLUSH, &attr);
414
SetConsoleMode(in_id, old_mode);
417
tcsetattr(fd, TCSANOW, &old_term_attr);
425
static Console *g_tty_console = 0, *g_stdio_console = 0;
427
Console::Console(Type type, ChannelMode cmode, TerminalMode tmode, QObject *parent)
432
Q_ASSERT(g_tty_console == 0);
433
g_tty_console = this;
437
Q_ASSERT(g_stdio_console == 0);
438
g_stdio_console = this;
441
d = new ConsolePrivate(this);
445
Q_PIPE_ID in = INVALID_Q_PIPE_ID;
446
Q_PIPE_ID out = INVALID_Q_PIPE_ID;
451
in = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE,
452
FILE_SHARE_READ | FILE_SHARE_WRITE, false,
453
OPEN_EXISTING, 0, NULL);
457
in = GetStdHandle(STD_INPUT_HANDLE);
462
in = open("/dev/tty", O_RDONLY);
469
if(cmode == ReadWrite)
474
out = CreateFileA("CONOUT$",
475
GENERIC_READ | GENERIC_WRITE,
476
FILE_SHARE_READ | FILE_SHARE_WRITE, false,
477
OPEN_EXISTING, 0, NULL);
481
out = GetStdHandle(STD_OUTPUT_HANDLE);
486
out = open("/dev/tty", O_WRONLY);
496
d->setInteractive(tmode);
497
d->thread->start(in, out);
503
Console::Type type = d->type;
511
Console::Type Console::type() const
516
Console::ChannelMode Console::channelMode() const
521
Console::TerminalMode Console::terminalMode() const
526
bool Console::isStdinRedirected()
529
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
531
if(GetConsoleMode(h, &mode))
535
return (isatty(0) ? false : true); // 0 == stdin
539
bool Console::isStdoutRedirected()
542
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
544
if(GetConsoleMode(h, &mode))
548
return (isatty(1) ? false : true); // 1 == stdout
552
Console *Console::ttyInstance()
554
return g_tty_console;
557
Console *Console::stdioInstance()
559
return g_stdio_console;
562
void Console::release()
567
QByteArray Console::bytesLeftToRead()
569
return d->thread->takeBytesToRead();
572
QByteArray Console::bytesLeftToWrite()
574
return d->thread->takeBytesToWrite();
577
//----------------------------------------------------------------------------
579
//----------------------------------------------------------------------------
580
class ConsoleReferencePrivate : public QObject
587
ConsoleThread *thread;
588
ConsoleReference::SecurityMode smode;
589
SafeTimer lateTrigger;
590
bool late_read, late_close;
592
ConsoleReferencePrivate(ConsoleReference *_q) : QObject(_q), q(_q), lateTrigger(this)
596
connect(&lateTrigger, SIGNAL(timeout()), SLOT(doLate()));
597
lateTrigger.setSingleShot(true);
603
QPointer<QObject> self = this;
609
emit q->inputClosed();
613
ConsoleReference::ConsoleReference(QObject *parent)
616
d = new ConsoleReferencePrivate(this);
619
ConsoleReference::~ConsoleReference()
625
bool ConsoleReference::start(Console *console, SecurityMode mode)
627
// make sure this reference isn't using a console already
628
Q_ASSERT(!d->console);
630
// one console reference at a time
631
Q_ASSERT(console->d->ref == 0);
634
d->console = console;
635
d->thread = d->console->d->thread;
636
d->console->d->ref = this;
638
bool valid = d->thread->isValid();
639
int avail = d->thread->bytesAvailable();
641
// pipe already closed and no data? consider this an error
642
if(!valid && avail == 0)
644
d->console->d->ref = 0;
650
// enable security? it will last for this active session only
652
if(mode == SecurityEnabled)
653
d->thread->setSecurityEnabled(true);
655
connect(d->thread, SIGNAL(readyRead()), SIGNAL(readyRead()));
656
connect(d->thread, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)));
657
connect(d->thread, SIGNAL(inputClosed()), SIGNAL(inputClosed()));
658
connect(d->thread, SIGNAL(outputClosed()), SIGNAL(outputClosed()));
660
d->late_read = false;
661
d->late_close = false;
667
d->late_close = true;
669
if(d->late_read || d->late_close)
670
d->lateTrigger.start();
675
void ConsoleReference::stop()
680
d->lateTrigger.stop();
682
disconnect(d->thread, 0, this, 0);
684
// automatically disable security when we go inactive
685
d->thread->setSecurityEnabled(false);
687
d->console->d->ref = 0;
692
Console *ConsoleReference::console() const
697
ConsoleReference::SecurityMode ConsoleReference::securityMode() const
702
QByteArray ConsoleReference::read(int bytes)
704
return d->thread->read(bytes);
707
void ConsoleReference::write(const QByteArray &a)
712
SecureArray ConsoleReference::readSecure(int bytes)
714
return d->thread->readSecure(bytes);
717
void ConsoleReference::writeSecure(const SecureArray &a)
719
d->thread->writeSecure(a);
722
void ConsoleReference::closeOutput()
724
d->thread->closeOutput();
727
int ConsoleReference::bytesAvailable() const
729
return d->thread->bytesAvailable();
732
int ConsoleReference::bytesToWrite() const
734
return d->thread->bytesToWrite();
737
//----------------------------------------------------------------------------
739
//----------------------------------------------------------------------------
740
class ConsolePrompt::Private : public QObject
749
ConsoleReference console;
757
QTextCodec::ConverterState *encstate, *decstate;
759
Private(ConsolePrompt *_q) : QObject(_q), q(_q), sync(_q), console(this)
761
connect(&console, SIGNAL(readyRead()), SLOT(con_readyRead()));
762
connect(&console, SIGNAL(inputClosed()), SLOT(con_inputClosed()));
769
codec = QTextCodec::codecForMib(106); // UTF-8
771
codec = QTextCodec::codecForLocale();
798
bool start(bool _charMode)
801
con = Console::ttyInstance();
804
con = new Console(Console::Tty, Console::ReadWrite, Console::Interactive);
811
charMode = _charMode;
813
encstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
814
decstate = new QTextCodec::ConverterState(QTextCodec::IgnoreHeader);
816
if(!console.start(con, ConsoleReference::SecurityEnabled))
819
fprintf(stderr, "Console input not available or closed\n");
824
writeString(promptStr + ": ");
829
void writeString(const QString &str)
831
console.writeSecure(codec->fromUnicode(str.unicode(), str.length(), encstate));
834
// process each char. internally store the result as utf16, which
835
// is easier to edit (e.g. backspace)
836
bool processChar(QChar c)
845
if(c == '\r' || c == '\n')
852
if(c == '\b' || c == 0x7f)
857
writeString("\b \b");
858
result.resize(at * sizeof(ushort));
865
if(at >= CONSOLEPROMPT_INPUT_MAX)
874
void appendChar(QChar c)
876
if((at + 1) * (int)sizeof(ushort) > result.size())
877
result.resize((at + 1) * sizeof(ushort));
878
ushort *p = (ushort *)result.data();
879
p[at++] = c.unicode();
884
// convert result from utf16 to utf8, securely
885
QTextCodec *codec = QTextCodec::codecForMib(106);
886
QTextCodec::ConverterState cstate(QTextCodec::IgnoreHeader);
888
ushort *ustr = (ushort *)result.data();
889
int len = result.size() / sizeof(ushort);
890
for(int n = 0; n < len; ++n)
893
out += codec->fromUnicode(&c, 1, &cstate);
901
while(console.bytesAvailable() > 0)
903
SecureArray buf = console.readSecure(1);
907
// convert to unicode and process
908
QString str = codec->toUnicode(buf.data(), 1, decstate);
910
for(int n = 0; n < str.length(); ++n)
912
if(!processChar(str[n]))
934
void con_inputClosed()
936
fprintf(stderr, "Console input closed\n");
951
ConsolePrompt::ConsolePrompt(QObject *parent)
954
d = new Private(this);
957
ConsolePrompt::~ConsolePrompt()
962
void ConsolePrompt::getHidden(const QString &promptStr)
966
d->promptStr = promptStr;
969
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
974
void ConsolePrompt::getChar()
980
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
985
void ConsolePrompt::waitForFinished()
987
// reparent the Console under us (for Synchronizer)
988
QObject *orig_parent = d->con->parent();
989
d->con->setParent(this);
991
// block while prompting
993
d->sync.waitForCondition();
996
// restore parent (if con still exists)
998
d->con->setParent(orig_parent);
1001
SecureArray ConsolePrompt::result() const
1006
QChar ConsolePrompt::resultChar() const
1008
QString str = QString::fromUtf8(d->result.toByteArray());
1010
// this will never happen if getChar completes
1019
#include "console.moc"