#include "configdialog.h"
#include "office.h"
#include "listview.h"
+#include "sslexception.h"
#include <QByteArray>
#include <QCheckBox>
#include <QMenu>
#include <QMenuBar>
#include <QMessageBox>
+#include <QNetworkAccessManager>
+#include <QNetworkProxy>
+#include <QNetworkReply>
#include <QPushButton>
#include <QSettings>
#include <QSpinBox>
#include <QStandardItemModel>
+#include <QTableView>
+#include <QTimer>
+#include <QUrl>
MConfigDialog::MConfigDialog()
{
setWindowTitle(tr("Magic Smoke Configuration"));
oldrow=-1;
+ sslexcept=0;
//main layout
QHBoxLayout*hl;
QVBoxLayout*vl;
m->addAction(tr("Set &Default Label Font..."),this,SLOT(setDefaultFont()));
mb->addMenu(MApplication::helpMenu());
- //create central widget
- QGridLayout*gl;
+ //create list view
hl->addWidget(profiles=new MListView);
profilemodel=new QStandardItemModel(this);
profiles->setModel(profilemodel);
profiles->setEditTriggers(QListView::NoEditTriggers);
connect(profiles,SIGNAL(clicked(const QModelIndex&)),this,SLOT(loadProfile()));
connect(profiles,SIGNAL(activated(const QModelIndex&)),this,SLOT(loadProfile()));
- hl->addLayout(gl=new QGridLayout,10);
+ //create tabs
+ QTabWidget*tab;
+ QWidget*w;
+ hl->addWidget(tab=new QTabWidget,10);
+ tab->addTab(w=new QWidget,tr("Connection"));
+ QGridLayout*gl;
+ w->setLayout(gl=new QGridLayout);
QLabel*lab;
int lctr=0;
- gl->addWidget(lab=new QLabel(tr("Hostname:")),++lctr,0);
- lab->setAlignment(Qt::AlignRight);
- gl->addWidget(hostname=new QLineEdit,lctr,1);
- gl->addWidget(lab=new QLabel(tr("Hostkey:")),++lctr,0);
- lab->setAlignment(Qt::AlignRight);
- gl->addWidget(hostkey=new QLineEdit,lctr,1);
gl->addWidget(lab=new QLabel(tr("Server URL:")),++lctr,0);
lab->setAlignment(Qt::AlignRight);
gl->addLayout(hl=new QHBoxLayout,lctr,1);
proxypass->setEchoMode(QLineEdit::Password);
connect(useproxy,SIGNAL(toggled(bool)),proxyuser,SLOT(setEnabled(bool)));
connect(useproxy,SIGNAL(toggled(bool)),proxypass,SLOT(setEnabled(bool)));
+
+ tab->addTab(w=new QWidget,tr("Authentication"));
+ w->setLayout(gl=new QGridLayout);
+ lctr=0;
+ gl->addWidget(lab=new QLabel(tr("Hostname:")),++lctr,0);
+ lab->setAlignment(Qt::AlignRight);
+ gl->addWidget(hostname=new QLineEdit,lctr,1);
+ gl->addWidget(lab=new QLabel(tr("Hostkey:")),++lctr,0);
+ lab->setAlignment(Qt::AlignRight);
+ gl->addWidget(hostkey=new QLineEdit,lctr,1);
gl->addWidget(lab=new QLabel(tr("Default Username:")),++lctr,0);
lab->setAlignment(Qt::AlignRight);
gl->addWidget(username=new QLineEdit,lctr,1);
gl->setRowStretch(++lctr,10);
gl->addLayout(hl=new QHBoxLayout,++lctr,0,1,2);
+ tab->addTab(w=new QWidget,tr("SSL Exceptions"));
+ w->setLayout(vl=new QVBoxLayout);
+ vl->addWidget(new QLabel(tr("List of non-fatal SSL exceptions:")),0);
+ vl->addWidget(ssltable=new QTableView,10);
+ ssltable->setModel(sslmodel=new QStandardItemModel(this));
+ ssltable->setEditTriggers(QListView::NoEditTriggers);
+ vl->addSpacing(10);
+ vl->addLayout(hl=new QHBoxLayout,0);
+ hl->addStretch(10);
+ QPushButton*pb;
+ hl->addWidget(pb=new QPushButton(tr("Clear")));
+ connect(pb,SIGNAL(clicked()),this,SLOT(clearSslExceptions()));
+ hl->addWidget(pb=new QPushButton(tr("Probe Server")));
+ connect(pb,SIGNAL(clicked()),this,SLOT(serverProbe()));
+
initProfiles();
loadProfile();
}
{
//save old
saveProfile();
+ if(sslexcept)delete sslexcept;
+ sslexcept=0;
//get new
QModelIndex idx=profiles->currentIndex();
if(!idx.isValid())return;
proxyuser->setEnabled(useproxy->isChecked());
proxypass->setEnabled(useproxy->isChecked());
username->setText(set.value("username").toString());
+ sslexcept=new MSslExceptions(::dataDir+"/profile."+key+"/sslexceptions.xml");
+ sslFillModel();
}
void MConfigDialog::saveProfile()
set.setValue("proxyuser",proxyuser->text());
set.setValue("proxypass",proxypass->text());
set.setValue("username",username->text());
+ if(sslexcept)sslexcept->savesslexcept();
}
void MConfigDialog::newProfile()
void MConfigDialog::generateKey()
{
+ //ask nicely
+ if(hostkey->text()!="")
+ if(QMessageBox::question(this,tr("Generate Hostkey"), tr("Do you really want to generate a new host key for this profile? This may disable all accounts from this host."), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
+ return;
+ //actually create new key
QString name;
MKeyGen mkg(this);
QString k=mkg.getKey();
if(df.isEmpty())return;
QSettings().setValue("defaultfont",df);
}
+
+void MConfigDialog::serverProbe()
+{
+ //sanity check
+ if(!sslexcept)return;
+ sslexcept->clearRecorded();
+ //set up mini-environment
+ QEventLoop loop;
+ QNetworkAccessManager man;
+ connect(&man,SIGNAL(finished(QNetworkReply*)),&loop,SLOT(quit()));
+ QTimer::singleShot(30000,&loop,SLOT(quit()));
+ connect(&man,SIGNAL(sslErrors(QNetworkReply*,const QList<QSslError>&)), this,SLOT(sslErrors(QNetworkReply*,const QList<QSslError>&)));
+ if(useproxy->isChecked())
+ man.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy,proxyname->text(),proxyport->value(),proxyuser->text(),proxypass->text()));
+ //query server
+ QNetworkRequest rq(QUrl("https://"+serverurl->text()));
+ QNetworkReply *rp=man.get(rq);
+ //wait for result
+ setEnabled(false);
+ loop.exec();
+ setEnabled(true);
+ //probe result
+ if(rp->error()==QNetworkReply::NoError){
+ QMessageBox::information(this,tr("Server Probe"),tr("The request finished without errors."));
+ }else{
+ QMessageBox::warning(this,tr("Server Probe"),tr("The request finished with an error: %1").arg(rp->errorString()));
+ }
+ delete rp;
+}
+
+void MConfigDialog::sslErrors(QNetworkReply*rp,const QList<QSslError>&errs)
+{
+ if(!sslexcept)return;
+ //check/record/clear
+ if(sslexcept->checksslexcept(errs))rp->ignoreSslErrors();
+ //convert to string
+ QString err=tr("SSL Errors encountered:\n");
+ for(int i=0;i<errs.size();i++){
+ QSslCertificate c=errs[i].certificate();
+ err+=tr("Certificate \"%1\"\n Fingerprint (sha1): %2\n Error: %3\n")
+ .arg(c.subjectInfo(QSslCertificate::CommonName))
+ .arg(QString::fromAscii(c.digest(QCryptographicHash::Sha1).toHex()))
+ .arg(errs[i].errorString());
+ }
+ err+=tr("Accept connection anyway?");
+ if(QMessageBox::warning(this,tr("SSL Warning"),err,QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes){
+ rp->ignoreSslErrors();
+ sslexcept->acceptRecorded();
+ }
+ sslFillModel();
+}
+
+void MConfigDialog::clearSslExceptions()
+{
+ if(sslexcept)sslexcept->clear();
+ sslmodel->clear();
+}
+
+void MConfigDialog::sslFillModel()
+{
+ sslmodel->clear();
+ if(!sslexcept)return;
+ sslmodel->insertColumns(0,3);
+ sslmodel->setHorizontalHeaderLabels(QStringList()<<tr("Common Name")<<tr("SHA-1 Digest")<<tr("Error Type"));
+ QList<QPair<QSslCertificate,int> >lst=sslexcept->nonFatalExceptions();
+ sslmodel->insertRows(0,lst.size());
+ for(int i=0;i<lst.size();i++){
+ sslmodel->setData(sslmodel->index(i,0),lst[i].first.subjectInfo(QSslCertificate::CommonName));
+ sslmodel->setData(sslmodel->index(i,1),QString::fromAscii(lst[i].first.digest(QCryptographicHash::Sha1).toHex()));
+ sslmodel->setData(sslmodel->index(i,2),QSslError((QSslError::SslError)lst[i].second).errorString());
+ }
+}
#include <QDomElement>
#include <QDomNodeList>
#include <QFile>
-#include <QSslCertificate>
MSslExceptions::MSslExceptions(QString p)
{
if(!doc.setContent(&fd))return;
fd.close();
QDomElement root=doc.documentElement();
+ QDomNodeList nl=root.elementsByTagName("SSL-Exception");
+ for(int i=0;i<nl.size();i++){
+ QDomElement el=nl.at(i).toElement();
+ int e=el.attribute("errorId",0).toInt();
+ if(e<=0)continue;
+ QSslCertificate c(el.text().toAscii());
+ if(c.isNull())continue;
+ sslexcept.append(QPair<QSslCertificate,int>(c,e));
+ }
}
}
-void MSslExceptions::savesslexcept(){}
+void MSslExceptions::savesslexcept()
+{
+ QDomDocument doc;
+ QDomElement root=doc.createElement("SSL-Exceptions");
+ for(int i=0;i<sslexcept.size();i++){
+ QDomElement el=doc.createElement("SSL-Exception");
+ el.setAttribute("errorId",sslexcept[i].second);
+ el.appendChild(doc.createTextNode(sslexcept[i].first.toPem()));
+ root.appendChild(el);
+ }
+ doc.appendChild(root);
+ QFile fd(path);
+ if(fd.open(QIODevice::WriteOnly|QIODevice::Truncate)){
+ fd.write(doc.toByteArray());
+ fd.close();
+ }
+}
+
+void MSslExceptions::clear()
+{
+ sslexcept.clear();
+ sslrecord.clear();
+}
-void MSslExceptions::showdialog(QWidget*){}
+void MSslExceptions::acceptRecorded()
+{
+ for(int i=0;i<sslrecord.size();i++){
+ bool known=false;
+ for(int j=0;j<sslexcept.size();j++){
+ if(sslrecord[i] == sslexcept[j]){
+ known=true;
+ break;
+ }
+ }
+ if(!known)sslexcept.append(sslrecord[i]);
+ }
+}
bool MSslExceptions::checksslexcept(const QList<QSslError>&errs)
{
- qDebug("!!!!!!!!!!!!!!!!!!! %i SSL Exceptions!",errs.size());
- return true;
+ //stage 1: record unknown exceptions
+ for(int i=0;i<errs.size();i++){
+ QPair<QSslCertificate,int>p(errs[i].certificate(),errs[i].error());
+ bool known=false;
+ for(int j=0;j<sslrecord.size();j++){
+ if(p==sslrecord[j]){
+ known=true;
+ break;
+ }
+ }
+ if(!known)sslrecord.append(p);
+ }
+ //stage 2: compare to acceptable exceptions
for(int i=0;i<errs.size();i++){
- QByteArray cert=errs[i].certificate().toPem();
- if(!sslexcept.contains(cert))return false;
- if(!sslexcept[cert].contains(errs[i].error()))return false;
+ QPair<QSslCertificate,int>p(errs[i].certificate(),errs[i].error());
+ bool known=false;
+ for(int j=0;j<sslexcept.size();j++){
+ if(p==sslexcept[j]){
+ known=true;
+ break;
+ }
+ }
+ if(!known)return false;
}
return true;
}