package SslClient; use strict; use warnings; use QtCore4; use QtGui4; use QtNetwork4; use QtCore4::isa qw( Qt::Widget ); use QtCore4::slots updateEnabledState => [], secureConnect => [], socketStateChanged => ['QAbstractSocket::SocketState'], socketEncrypted => [], socketReadyRead => [], sendData => [], sslErrors => ['const QList<QSslError> &'], displayCertificateInfo => []; use CertificateInfo; use Ui_Form; use Ui_SslErrors; sub socket() { return this->{socket}; } sub padLock() { return this->{padLock}; } sub form() { return this->{form}; } sub executingDialog() { return this->{executingDialog}; } sub NEW { my ($class, $parent) = @_; $class->SUPER::NEW($parent); this->{executingDialog} = 0; this->{form} = Ui_Form->setupUi(this); form->hostNameEdit->setSelection(0, length form->hostNameEdit->text()); form->sessionOutput->setHtml(this->tr('<not connected>')); this->connect(form->hostNameEdit, SIGNAL 'textChanged(QString)', this, SLOT 'updateEnabledState()'); this->connect(form->connectButton, SIGNAL 'clicked()', this, SLOT 'secureConnect()'); this->connect(form->sendButton, SIGNAL 'clicked()', this, SLOT 'sendData()'); } sub updateEnabledState { my $unconnected = !defined this->socket() || this->socket()->state() == Qt::AbstractSocket::UnconnectedState(); form->hostNameEdit->setReadOnly(!$unconnected); form->hostNameEdit->setFocusPolicy($unconnected ? Qt::StrongFocus() : Qt::NoFocus()); form->hostNameLabel->setEnabled($unconnected); form->portBox->setEnabled($unconnected); form->portLabel->setEnabled($unconnected); form->connectButton->setEnabled($unconnected && form->hostNameEdit->text()); my $connected = defined this->socket() && this->socket->state() == Qt::AbstractSocket::ConnectedState(); form->sessionBox->setEnabled($connected); form->sessionOutput->setEnabled($connected); form->sessionInput->setEnabled($connected); form->sessionInputLabel->setEnabled($connected); form->sendButton->setEnabled($connected); } sub secureConnect { if (!defined this->socket()) { this->{socket} = Qt::SslSocket(this); this->connect(this->socket, SIGNAL 'stateChanged(QAbstractSocket::SocketState)', this, SLOT 'socketStateChanged(QAbstractSocket::SocketState)'); this->connect(this->socket, SIGNAL 'encrypted()', this, SLOT 'socketEncrypted()'); this->connect(this->socket, SIGNAL 'sslErrors(QList<QSslError>)', this, SLOT 'sslErrors(QList<QSslError>)'); this->connect(this->socket, SIGNAL 'readyRead()', this, SLOT 'socketReadyRead()'); } this->socket()->connectToHostEncrypted(form->hostNameEdit->text(), form->portBox->value()); updateEnabledState(); } sub socketStateChanged { my ($state) = @_; if (executingDialog) { return; } updateEnabledState(); if ($state == Qt::AbstractSocket::UnconnectedState()) { form->hostNameEdit->setPalette(Qt::Palette()); form->hostNameEdit->setFocus(); form->cipherLabel->setText(this->tr('<none>')); if (defined padLock()) { padLock->hide(); } this->socket->deleteLater(); this->{socket} = undef; } } sub socketEncrypted { if (!defined this->socket) { return; # might have disconnected already } form->sessionOutput->clear(); form->sessionInput->setFocus(); my $palette = Qt::Palette(); $palette->setColor(Qt::Palette::Base(), Qt::Color(255, 255, 192)); form->hostNameEdit->setPalette($palette); my $ciph = this->socket->sessionCipher(); my $cipher = sprintf '%s, %s (%s/%s)', $ciph->authenticationMethod(), $ciph->name(), $ciph->usedBits(), $ciph->supportedBits(); form->cipherLabel->setText($cipher); if (!defined padLock) { this->{padLock} = Qt::ToolButton(); padLock->setIcon(Qt::Icon(':/encrypted.png')); padLock->setCursor(Qt::Cursor(Qt::ArrowCursor())); padLock->setToolTip(this->tr('Display encryption details.')); my $extent = form->hostNameEdit->height() - 2; padLock->resize($extent, $extent); padLock->setSizePolicy(Qt::SizePolicy::Fixed(), Qt::SizePolicy::Ignored()); my $layout = Qt::HBoxLayout(form->hostNameEdit); $layout->setMargin(form->hostNameEdit->style()->pixelMetric(Qt::Style::PM_DefaultFrameWidth())); $layout->setSpacing(0); $layout->addStretch(); $layout->addWidget(padLock); form->hostNameEdit->setLayout($layout); this->connect(padLock, SIGNAL 'clicked()', this, SLOT 'displayCertificateInfo()'); } else { padLock->show(); } } sub socketReadyRead { my $readAll = this->socket()->readAll(); utf8::encode($readAll); appendString($readAll); } sub sendData { my $input = form->sessionInput->text(); appendString($input . "\n"); utf8::decode($input); this->socket->write($input . "\r\n"); form->sessionInput->clear(); } sub sslErrors { my ($errors) = @_; my $errorDialog = Qt::Dialog(this); my $ui = Ui_SslErrors->setupUi($errorDialog); this->connect($ui->certificateChainButton, SIGNAL 'clicked()', this, SLOT 'displayCertificateInfo()'); foreach my $error ( @{$errors} ) { $ui->sslErrorList->addItem($error->errorString()); } this->{executingDialog} = 1; if ($errorDialog->exec() == Qt::Dialog::Accepted()) { this->socket->ignoreSslErrors(); } this->{executingDialog} = 0; # did the socket state change? if (this->socket->state() != Qt::AbstractSocket::ConnectedState()) { socketStateChanged(this->socket->state()); } } sub displayCertificateInfo { my $info = CertificateInfo(this); $info->setCertificateChain(this->socket->peerCertificateChain()); $info->exec(); #$info->deleteLater(); } sub appendString { my ($line) = @_; my $cursor = Qt::TextCursor(form->sessionOutput->textCursor()); $cursor->movePosition(Qt::TextCursor::End()); $cursor->insertText($line); form->sessionOutput->verticalScrollBar()->setValue(form->sessionOutput->verticalScrollBar()->maximum()); } 1;