1 #include <QApplication>
2 #include <QTextEdit>
3 #include <QGroupBox>
4 #include <QLineEdit>
5 #include <QLabel>
6 #include <QCheckBox>
7 #include <QComboBox>
8 #include <QPushButton>
9 #include <QMessageBox>
10 #include <QInputDialog>
11 #include <QSpinBox>
12 #include <QTimer>
13 #include <QMenuBar>
14 #include <QMenu>
15 #include <QTabWidget>
16 #include <QTextBlock>
17 #include <qca.h>
18 #include "xmpp.h"
19 #include "im.h"
20
21 #include <stdlib.h>
22 #include <time.h>
23
24 #include "ui_test.h"
25
26 #include <stdio.h>
27
28 #define AppName "xmpptest"
29
plain2rich(const QString & plain)30 static QString plain2rich(const QString &plain)
31 {
32 QString rich;
33 int col = 0;
34
35 for(int i = 0; i < (int)plain.length(); ++i) {
36 if(plain[i] == '\n') {
37 rich += "<br>";
38 col = 0;
39 }
40 else if(plain[i] == '\t') {
41 rich += QChar::Nbsp;
42 while(col % 4) {
43 rich += QChar::Nbsp;
44 ++col;
45 }
46 }
47 else if(plain[i].isSpace()) {
48 if(i > 0 && plain[i-1] == ' ')
49 rich += QChar::Nbsp;
50 else
51 rich += ' ';
52 }
53 else if(plain[i] == '<')
54 rich += "<";
55 else if(plain[i] == '>')
56 rich += ">";
57 else if(plain[i] == '\"')
58 rich += """;
59 else if(plain[i] == '\'')
60 rich += "'";
61 else if(plain[i] == '&')
62 rich += "&";
63 else
64 rich += plain[i];
65 ++col;
66 }
67
68 return rich;
69 }
70
71 /*static void showCertInfo(const QCA::Cert &cert)
72 {
73 fprintf(stderr, "-- Cert --\n");
74 fprintf(stderr, " CN: %s\n", cert.subject()["CN"].latin1());
75 fprintf(stderr, " Valid from: %s, until %s\n",
76 cert.notBefore().toString().latin1(),
77 cert.notAfter().toString().latin1());
78 fprintf(stderr, " PEM:\n%s\n", cert.toPEM().latin1());
79 }*/
80
resultToString(int result)81 static QString resultToString(int result)
82 {
83 QString s;
84 switch(result) {
85 case QCA::TLS::NoCertificate:
86 s = QObject::tr("No certificate presented.");
87 break;
88 case QCA::TLS::Valid:
89 break;
90 case QCA::TLS::HostMismatch:
91 s = QObject::tr("Hostname mismatch.");
92 break;
93 case QCA::TLS::InvalidCertificate:
94 s = QObject::tr("Invalid Certificate.");
95 break;
96 // TODO: Inspect why
97 // case QCA::TLS::Untrusted:
98 // s = QObject::tr("Not trusted for the specified purpose.");
99 // break;
100 // case QCA::TLS::SignatureFailed:
101 // s = QObject::tr("Invalid signature.");
102 // break;
103 // case QCA::TLS::InvalidCA:
104 // s = QObject::tr("Invalid CA certificate.");
105 // break;
106 // case QCA::TLS::InvalidPurpose:
107 // s = QObject::tr("Invalid certificate purpose.");
108 // break;
109 // case QCA::TLS::SelfSigned:
110 // s = QObject::tr("Certificate is self-signed.");
111 // break;
112 // case QCA::TLS::Revoked:
113 // s = QObject::tr("Certificate has been revoked.");
114 // break;
115 // case QCA::TLS::PathLengthExceeded:
116 // s = QObject::tr("Maximum cert chain length exceeded.");
117 // break;
118 // case QCA::TLS::Expired:
119 // s = QObject::tr("Certificate has expired.");
120 // break;
121 // case QCA::TLS::Unknown:
122 default:
123 s = QObject::tr("General validation error.");
124 break;
125 }
126 return s;
127 }
128
get_block(QTextEdit * te,int index)129 static QTextBlock get_block(QTextEdit *te, int index)
130 {
131 QTextBlock block = te->document()->begin();
132 for(int n = 0; n < index && block.isValid(); ++n)
133 block = block.next();
134 return block;
135 }
136
textedit_paragraphLength(QTextEdit * te,int paragraph)137 static int textedit_paragraphLength(QTextEdit *te, int paragraph)
138 {
139 QTextBlock block = get_block(te, paragraph);
140 if(block.isValid())
141 return block.length();
142 else
143 return 0;
144 }
145
textedit_setCursorPosition(QTextEdit * te,int line,int col)146 static void textedit_setCursorPosition(QTextEdit *te, int line, int col)
147 {
148 QTextCursor cur = te->textCursor();
149 QTextBlock block = get_block(te, line);
150 if(!block.isValid())
151 return;
152 if(col > block.length())
153 col = block.length();
154 cur.setPosition(block.position() + col);
155 te->setTextCursor(cur);
156 }
157
textedit_setSelection(QTextEdit * te,int startLine,int startCol,int endLine,int endCol)158 static void textedit_setSelection(QTextEdit *te, int startLine, int startCol, int endLine, int endCol)
159 {
160 QTextCursor cur = te->textCursor();
161
162 QTextBlock block = get_block(te, startLine);
163 if(!block.isValid())
164 return;
165 if(startCol > block.length())
166 startCol = block.length();
167 cur.setPosition(block.position() + startCol); // start
168
169 block = get_block(te, endLine);
170 if(!block.isValid())
171 return;
172 if(endCol > block.length())
173 endCol = block.length();
174 cur.setPosition(block.position() + endCol, QTextCursor::KeepAnchor); // select
175
176 te->setTextCursor(cur);
177 }
178
179 class TestDebug : public XMPP::Debug
180 {
181 public:
182 void msg(const QString &);
183 void outgoingTag(const QString &);
184 void incomingTag(const QString &);
185 void outgoingXml(const QDomElement &);
186 void incomingXml(const QDomElement &);
187 };
188
189 class TestDlg : public QDialog, public Ui::TestUI
190 {
191 Q_OBJECT
192 public:
193 bool active, connected;
194 XMPP::AdvancedConnector *conn;
195 QCA::TLS *tls;
196 XMPP::QCATLSHandler *tlsHandler;
197 XMPP::ClientStream *stream;
198 XMPP::Jid jid;
199
TestDlg(QWidget * parent=0)200 TestDlg(QWidget *parent=0) : QDialog(parent)
201 {
202 setupUi(this);
203 setWindowTitle(tr("XMPP Test"));
204
205 connect(ck_probe, SIGNAL(toggled(bool)), SLOT(probe_toggled(bool)));
206 connect(cb_proxy, SIGNAL(activated(int)), SLOT(proxy_activated(int)));
207 connect(pb_go, SIGNAL(clicked()), SLOT(go()));
208 connect(pb_send, SIGNAL(clicked()), SLOT(send()));
209 connect(pb_im, SIGNAL(clicked()), SLOT(sc_im()));
210 connect(pb_msg, SIGNAL(clicked()), SLOT(sc_msg()));
211 connect(pb_iqv, SIGNAL(clicked()), SLOT(sc_iqv()));
212 connect(pb_about, SIGNAL(clicked()), SLOT(about()));
213
214 sb_ssfmin->setMinimum(0);
215 sb_ssfmin->setMaximum(256);
216 sb_ssfmax->setMinimum(0);
217 sb_ssfmax->setMaximum(256);
218
219 pb_send->setEnabled(false);
220 proxy_activated(0);
221 ck_probe->setChecked(true);
222 ck_mutual->setChecked(false);
223 pb_go->setText(tr("&Connect"));
224
225 //le_jid->setText("psitest@jabberd.jabberstudio.org/Test");
226 //ck_probe->setChecked(false);
227 //le_host->setText("jabberd.jabberstudio.org:15222");
228 //ck_mutual->setChecked(false);
229 //le_jid->setText("sasltest@e.jabber.ru/Test");
230 //le_jid->setText("psitest@jabber.cz/Test");
231 //le_pass->setText("psitest");
232 //cb_proxy->setCurrentItem(3);
233 //le_proxyurl->setText("http://connect.jabber.cz/");
234
235 // setup xmpp
236 conn = new XMPP::AdvancedConnector;
237 connect(conn, SIGNAL(srvLookup(QString)), SLOT(conn_srvLookup(QString)));
238 connect(conn, SIGNAL(srvResult(bool)), SLOT(conn_srvResult(bool)));
239 connect(conn, SIGNAL(httpSyncStarted()), SLOT(conn_httpSyncStarted()));
240 connect(conn, SIGNAL(httpSyncFinished()), SLOT(conn_httpSyncFinished()));
241
242 if(QCA::isSupported("tls")) {
243 tls = new QCA::TLS;
244 tlsHandler = new XMPP::QCATLSHandler(tls);
245 tlsHandler->setXMPPCertCheck(true);
246 connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
247 }
248 else {
249 tls = 0;
250 tlsHandler = 0;
251 }
252
253 stream = new XMPP::ClientStream(conn, tlsHandler);
254 //stream->setOldOnly(true);
255 connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
256 connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated(int)));
257 connect(stream, SIGNAL(needAuthParams(bool,bool,bool)), SLOT(cs_needAuthParams(bool,bool,bool)));
258 connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
259 connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
260 connect(stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
261 connect(stream, SIGNAL(readyRead()), SLOT(cs_readyRead()));
262 connect(stream, SIGNAL(stanzaWritten()), SLOT(cs_stanzaWritten()));
263 connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
264 connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));
265
266 QTimer::singleShot(0, this, SLOT(adjustLayout()));
267
268 le_jid->setFocus();
269 active = false;
270 connected = false;
271 }
272
~TestDlg()273 ~TestDlg()
274 {
275 delete stream;
276 delete tls; // this destroys the TLSHandler also
277 delete conn;
278 }
279
280 private slots:
adjustLayout()281 void adjustLayout()
282 {
283 tb_main->setFixedWidth(tb_main->minimumSizeHint().width());
284 resize(minimumSizeHint());
285 show();
286 }
287
about()288 void about()
289 {
290 QMessageBox::about(this, tr("About %1").arg(AppName), tr(
291 "%1 v1.0\n"
292 "\n"
293 "Utility to demonstrate the Iris XMPP library.\n"
294 "\n"
295 "Currently supports:\n"
296 " draft-ietf-xmpp-core-21\n"
297 " JEP-0025\n"
298 "\n"
299 "Copyright (C) 2003 Justin Karneges").arg(AppName));
300 }
301
probe_toggled(bool)302 void probe_toggled(bool)
303 {
304 setHostState();
305 }
306
proxy_activated(int x)307 void proxy_activated(int x)
308 {
309 bool ok = (x != 0);
310 bool okpoll = (x == 3);
311 gb_proxy->setEnabled(ok);
312 lb_proxyurl->setEnabled(okpoll);
313 le_proxyurl->setEnabled(okpoll);
314 ck_probe->setEnabled(!okpoll);
315 setHostState();
316 }
317
cleanup()318 void cleanup()
319 {
320 pb_send->setEnabled(false);
321 pb_go->setEnabled(true);
322 pb_go->setText(tr("&Connect"));
323 pb_go->setFocus();
324 gb_server->setEnabled(true);
325 active = false;
326 connected = false;
327 }
328
start()329 void start()
330 {
331 if(active)
332 return;
333
334 jid = XMPP::Jid(le_jid->text());
335 if(jid.domain().isEmpty() || jid.node().isEmpty() || jid.resource().isEmpty()) {
336 QMessageBox::information(this, tr("Error"), tr("Please enter the Full JID to connect with."));
337 return;
338 }
339
340 int p = cb_proxy->currentIndex();
341 XMPP::AdvancedConnector::Proxy proxy;
342 if(p > 0) {
343 QString s = le_proxyhost->text();
344 QString url = le_proxyurl->text();
345 if(p != 3 && s.isEmpty()) {
346 QMessageBox::information(this, tr("Error"), tr("You must specify a host:port for the proxy."));
347 return;
348 }
349 if(p == 3 && s.isEmpty() && url.isEmpty()) {
350 QMessageBox::information(this, tr("Error"), tr("You must at least enter a URL to use http poll."));
351 return;
352 }
353 QString host;
354 int port = 0;
355 if(!s.isEmpty()) {
356 int n = s.indexOf(':');
357 if(n == -1) {
358 QMessageBox::information(this, tr("Error"), tr("Please enter the proxy host in the form 'host:port'."));
359 return;
360 }
361 host = s.mid(0, n);
362 port = s.mid(n+1).toInt();
363 }
364 if(p == 1)
365 proxy.setHttpConnect(host, port);
366 else if(p == 2)
367 proxy.setSocks(host, port);
368 else if(p == 3) {
369 proxy.setHttpPoll(host, port, url);
370 proxy.setPollInterval(2); // fast during login
371 }
372 proxy.setUserPass(le_proxyuser->text(), le_proxypass->text());
373 }
374 bool probe = (p != 3 && ck_probe->isChecked());
375 bool useHost = (!probe && !le_host->text().isEmpty());
376 QString host;
377 int port = 0;
378 bool ssl = false;
379 if(useHost) {
380 QString s = le_host->text();
381 int n = s.indexOf(':');
382 if(n == -1) {
383 QMessageBox::information(this, tr("Error"), tr("Please enter the host in the form 'host:port'."));
384 return;
385 }
386 host = s.mid(0, n);
387 port = s.mid(n+1).toInt();
388
389 if(ck_ssl->isChecked())
390 ssl = true;
391 }
392 if(sb_ssfmin->value() > sb_ssfmax->value()) {
393 QMessageBox::information(this, tr("Error"), tr("Error: SSF Min is greater than SSF Max."));
394 return;
395 }
396
397 if((probe || ssl) && !tls) {
398 QMessageBox::information(this, tr("Error"), tr("Error: TLS not available. Disable any TLS options."));
399 return;
400 }
401
402 // prepare
403 conn->setProxy(proxy);
404 if(useHost)
405 conn->setOptHostPort(host, port);
406 else
407 conn->setOptHostPort("", 0);
408 conn->setOptProbe(probe);
409 conn->setOptSSL(ssl);
410
411 if(tls) {
412 tls->setTrustedCertificates(QCA::systemStore());
413 }
414
415 stream->setNoopTime(55000); // every 55 seconds
416 stream->setAllowPlain(ck_plain->isChecked() ? XMPP::ClientStream::AllowPlain : XMPP::ClientStream::NoAllowPlain);
417 stream->setRequireMutualAuth(ck_mutual->isChecked());
418 stream->setSSFRange(sb_ssfmin->value(), sb_ssfmax->value());
419 //stream->setOldOnly(true);
420 stream->setCompress(true);
421
422 gb_server->setEnabled(false);
423 pb_go->setText(tr("&Disconnect"));
424 pb_go->setFocus();
425 active = true;
426
427 appendSysMsg("Connecting...");
428 stream->connectToServer(jid);
429 }
430
stop()431 void stop()
432 {
433 if(!active)
434 return;
435
436 if(connected) {
437 pb_go->setEnabled(false);
438 appendSysMsg("Disconnecting...");
439 stream->close();
440 }
441 else {
442 stream->close();
443 appendSysMsg("Disconnected");
444 cleanup();
445 }
446 }
447
go()448 void go()
449 {
450 if(active)
451 stop();
452 else
453 start();
454 }
455
send()456 void send()
457 {
458 if(te_input->toPlainText().isEmpty())
459 return;
460
461 // construct a "temporary" document to parse the input
462 QString str = "<stream xmlns=\"jabber:client\">\n";
463 str += te_input->toPlainText() + '\n';
464 str += "</stream>";
465
466 QDomDocument doc;
467 QString errMsg;
468 int errLine, errCol;
469 if(!doc.setContent(str, true, &errMsg, &errLine, &errCol)) {
470 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
471 int lines = str.split('\n', QString::KeepEmptyParts).count();
472 #else
473 int lines = str.split('\n', Qt::KeepEmptyParts).count();
474 #endif
475 --errLine; // skip the first line
476 if(errLine == lines-1) {
477 errLine = lines-2;
478 errCol = textedit_paragraphLength(te_input, errLine-1)+1;
479 errMsg = "incomplete input";
480 }
481 textedit_setCursorPosition(te_input, errLine-1, errCol-1);
482 QMessageBox::information(this, tr("Error"), tr("Bad XML input (%1,%2): %3\nPlease correct and try again.").arg(errCol).arg(errLine).arg(errMsg));
483 return;
484 }
485 QDomElement e = doc.firstChild().toElement();
486
487 int num = 0;
488 QDomNodeList nl = e.childNodes();
489 QList<XMPP::Stanza> stanzaList;
490 for(int x = 0; x < nl.count(); ++x) {
491 QDomNode n = nl.item(x);
492 if(n.isElement()) {
493 QDomElement e = n.toElement();
494 XMPP::Stanza s = stream->createStanza(e);
495 if(s.isNull()) {
496 QMessageBox::information(this, tr("Error"), tr("Bad Stanza '%1'. Must be 'message', 'presence', or 'iq'").arg(e.tagName()));
497 return;
498 }
499 stanzaList += s;
500 ++num;
501 }
502 }
503 if(num == 0) {
504 QMessageBox::information(this, tr("Error"), tr("You must enter at least one stanza!"));
505 return;
506 }
507
508 // out the door
509 for(QList<XMPP::Stanza>::ConstIterator it = stanzaList.begin(); it != stanzaList.end(); ++it) {
510 appendXmlOut(XMPP::Stream::xmlToString((*it).element(), true));
511 stream->write(*it);
512 }
513
514 te_input->setText("");
515 }
516
sc_im()517 void sc_im()
518 {
519 /*XMPP::Message m("justin@andbit.net/Psi");
520 m.setSubject("Hi");
521 m.setBody("I send you this in order to have your advice.");
522 m.setBody("Escucha lechuga!", "es");
523 XMPP::Stanza stanza = m.toStanza(stream);
524 QString str = stanza.toString();
525 printf("[%s]\n", str.latin1());
526
527 XMPP::Message n;
528 n.fromStanza(stanza);
529 printf("subject: [%s]\n", n.subject().latin1());
530 printf("body: [%s]\n", n.body().latin1());
531 printf("body-es: [%s]\n", n.body("es").latin1());*/
532
533 QString s;
534 s += "<iq type='set' id='sess_1'>\n";
535 s += " <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>\n";
536 s += "</iq>";
537 te_input->setText(s);
538 te_input->setFocus();
539 }
540
sc_msg()541 void sc_msg()
542 {
543 QString to = le_to->text();
544 QString s;
545 if(!to.isEmpty())
546 s += QString("<message to=\"%1\">\n").arg(to);
547 else
548 s += QString("<message to=\"\">\n");
549 s += " <body>hello world</body>\n";
550 s += "</message>";
551 te_input->setText(s);
552 if(!to.isEmpty()) {
553 textedit_setCursorPosition(te_input, 1, 7);
554 textedit_setSelection(te_input, 1, 7, 1, 18);
555 }
556 else
557 textedit_setCursorPosition(te_input, 0, 13);
558 te_input->setFocus();
559 }
560
sc_iqv()561 void sc_iqv()
562 {
563 QString to = le_to->text();
564 QString s;
565 if(!to.isEmpty())
566 s += QString("<iq to=\"%1\" type=\"get\" id=\"abcde\">\n").arg(to);
567 else
568 s += QString("<iq to=\"\" type=\"get\" id=\"abcde\">\n");
569 s += " <query xmlns=\"jabber:iq:version\"/>\n";
570 s += "</iq>";
571 te_input->setText(s);
572 if(!to.isEmpty()) {
573 textedit_setCursorPosition(te_input, 0, 8);
574 textedit_setSelection(te_input, 0, 8, 0, 8 + to.length());
575 }
576 else
577 textedit_setCursorPosition(te_input, 0, 8);
578 te_input->setFocus();
579 }
580
conn_srvLookup(const QString & server)581 void conn_srvLookup(const QString &server)
582 {
583 appendLibMsg(QString("SRV lookup on [%1]").arg(server));
584 }
585
conn_srvResult(bool b)586 void conn_srvResult(bool b)
587 {
588 if(b)
589 appendLibMsg("SRV lookup success!");
590 else
591 appendLibMsg("SRV lookup failed");
592 }
593
conn_httpSyncStarted()594 void conn_httpSyncStarted()
595 {
596 appendLibMsg("HttpPoll: syncing");
597 }
598
conn_httpSyncFinished()599 void conn_httpSyncFinished()
600 {
601 appendLibMsg("HttpPoll: done");
602 }
603
tls_handshaken()604 void tls_handshaken()
605 {
606 //QCA::Certificate cert = tls->peerCertificate();
607 int vr = tls->peerIdentityResult();
608 if (vr == QCA::TLS::Valid && !tlsHandler->certMatchesHostname()) vr = QCA::TLS::HostMismatch;
609
610 appendSysMsg("Successful TLS handshake.");
611 if(vr == QCA::TLS::Valid)
612 appendSysMsg("Valid certificate.");
613 else {
614 appendSysMsg(QString("Invalid certificate: %1").arg(resultToString(vr)), Qt::red);
615 appendSysMsg("Continuing anyway");
616 }
617
618 tlsHandler->continueAfterHandshake();
619 }
620
cs_connected()621 void cs_connected()
622 {
623 QString s = "Connected";
624 if(conn->havePeerAddress())
625 s += QString(" (%1:%2)").arg(conn->peerAddress().toString()).arg(conn->peerPort());
626 if(conn->useSSL())
627 s += " [ssl]";
628 appendSysMsg(s);
629 }
630
cs_securityLayerActivated(int type)631 void cs_securityLayerActivated(int type)
632 {
633 appendSysMsg(QString("Security layer activated (%1)").arg((type == XMPP::ClientStream::LayerTLS) ? "TLS": "SASL"));
634 }
635
cs_needAuthParams(bool user,bool pass,bool realm)636 void cs_needAuthParams(bool user, bool pass, bool realm)
637 {
638 QString s = "Need auth parameters -";
639 if(user)
640 s += " (Username)";
641 if(pass)
642 s += " (Password)";
643 if(realm)
644 s += " (Realm)";
645 appendSysMsg(s);
646
647 if(user) {
648 if(!le_user->text().isEmpty())
649 stream->setUsername(le_user->text());
650 else
651 stream->setUsername(jid.node());
652 }
653 if(pass) {
654 if(!le_pass->text().isEmpty())
655 stream->setPassword(le_pass->text());
656 else {
657 conn->changePollInterval(10); // slow down during prompt
658 bool ok;
659 QString s = QInputDialog::getText(this, tr("Password"), tr("Enter the password for %1").arg(jid.full()), QLineEdit::Password, QString(), &ok);
660 if(!ok) {
661 stop();
662 return;
663 }
664 stream->setPassword(s);
665
666 conn->changePollInterval(2); // resume speed
667 }
668 }
669 if(realm)
670 stream->setRealm(jid.domain());
671
672 stream->continueAfterParams();
673 }
674
cs_authenticated()675 void cs_authenticated()
676 {
677 connected = true;
678 pb_send->setEnabled(true);
679 conn->changePollInterval(10); // slow down after login
680 appendSysMsg("Authenticated");
681 }
682
cs_connectionClosed()683 void cs_connectionClosed()
684 {
685 appendSysMsg("Disconnected by peer");
686 cleanup();
687 }
688
cs_delayedCloseFinished()689 void cs_delayedCloseFinished()
690 {
691 appendSysMsg("Disconnected");
692 cleanup();
693 }
694
cs_readyRead()695 void cs_readyRead()
696 {
697 while(stream->stanzaAvailable()) {
698 XMPP::Stanza s = stream->read();
699 appendXmlIn(XMPP::Stream::xmlToString(s.element(), true));
700 }
701 }
702
cs_stanzaWritten()703 void cs_stanzaWritten()
704 {
705 appendSysMsg("Stanza sent");
706 }
707
cs_warning(int warn)708 void cs_warning(int warn)
709 {
710 if(warn == XMPP::ClientStream::WarnOldVersion) {
711 appendSysMsg("Warning: pre-1.0 protocol server", Qt::red);
712 }
713 else if(warn == XMPP::ClientStream::WarnNoTLS) {
714 appendSysMsg("Warning: TLS not available!", Qt::red);
715 }
716 stream->continueAfterWarning();
717 }
718
cs_error(int err)719 void cs_error(int err)
720 {
721 if(err == XMPP::ClientStream::ErrParse) {
722 appendErrMsg("XML parsing error");
723 }
724 else if(err == XMPP::ClientStream::ErrProtocol) {
725 appendErrMsg("XMPP protocol error");
726 }
727 else if(err == XMPP::ClientStream::ErrStream) {
728 int x = stream->errorCondition();
729 QString s;
730 if(x == XMPP::Stream::GenericStreamError)
731 s = "generic stream error";
732 else if(x == XMPP::ClientStream::Conflict)
733 s = "conflict (remote login replacing this one)";
734 else if(x == XMPP::ClientStream::ConnectionTimeout)
735 s = "timed out from inactivity";
736 else if(x == XMPP::ClientStream::InternalServerError)
737 s = "internal server error";
738 else if(x == XMPP::ClientStream::InvalidFrom)
739 s = "invalid from address";
740 else if(x == XMPP::ClientStream::InvalidXml)
741 s = "invalid XML";
742 else if(x == XMPP::ClientStream::PolicyViolation)
743 s = "policy violation. go to jail!";
744 else if(x == XMPP::ClientStream::ResourceConstraint)
745 s = "server out of resources";
746 else if(x == XMPP::ClientStream::SystemShutdown)
747 s = "system is shutting down NOW";
748 appendErrMsg(QString("XMPP stream error: %1").arg(s));
749 }
750 else if(err == XMPP::ClientStream::ErrConnection) {
751 int x = conn->errorCode();
752 QString s;
753 if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
754 s = "unable to connect to server";
755 else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
756 s = "host not found";
757 else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
758 s = "proxy connect";
759 else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
760 s = "proxy negotiating";
761 else if(x == XMPP::AdvancedConnector::ErrProxyAuth)
762 s = "proxy authorization";
763 else if(x == XMPP::AdvancedConnector::ErrStream)
764 s = "stream error";
765 appendErrMsg(QString("Connection error: %1").arg(s));
766 }
767 else if(err == XMPP::ClientStream::ErrNeg) {
768 int x = stream->errorCondition();
769 QString s;
770 if(x == XMPP::ClientStream::HostGone)
771 s = "host no longer hosted";
772 else if(x == XMPP::ClientStream::HostUnknown)
773 s = "host unknown";
774 else if(x == XMPP::ClientStream::RemoteConnectionFailed)
775 s = "a required remote connection failed";
776 else if(x == XMPP::ClientStream::SeeOtherHost)
777 s = QString("see other host: [%1]").arg(stream->errorText());
778 else if(x == XMPP::ClientStream::UnsupportedVersion)
779 s = "server does not support proper xmpp version";
780 appendErrMsg(QString("Stream negotiation error: %1").arg(s));
781 }
782 else if(err == XMPP::ClientStream::ErrTLS) {
783 int x = stream->errorCondition();
784 QString s;
785 if(x == XMPP::ClientStream::TLSStart)
786 s = "server rejected STARTTLS";
787 else if(x == XMPP::ClientStream::TLSFail) {
788 int t = tlsHandler->tlsError();
789 if(t == QCA::TLS::ErrorHandshake)
790 s = "TLS handshake error";
791 else
792 s = "broken security layer (TLS)";
793 }
794 appendErrMsg(s);
795 }
796 else if(err == XMPP::ClientStream::ErrAuth) {
797 int x = stream->errorCondition();
798 QString s;
799 if(x == XMPP::ClientStream::GenericAuthError)
800 s = "unable to login";
801 else if(x == XMPP::ClientStream::NoMech)
802 s = "no appropriate auth mechanism available for given security settings";
803 else if(x == XMPP::ClientStream::BadProto)
804 s = "bad server response";
805 else if(x == XMPP::ClientStream::BadServ)
806 s = "server failed mutual authentication";
807 else if(x == XMPP::ClientStream::EncryptionRequired)
808 s = "encryption required for chosen SASL mechanism";
809 else if(x == XMPP::ClientStream::InvalidAuthzid)
810 s = "invalid authzid";
811 else if(x == XMPP::ClientStream::InvalidMech)
812 s = "invalid SASL mechanism";
813 else if(x == XMPP::ClientStream::InvalidRealm)
814 s = "invalid realm";
815 else if(x == XMPP::ClientStream::MechTooWeak)
816 s = "SASL mechanism too weak for authzid";
817 else if(x == XMPP::ClientStream::NotAuthorized)
818 s = "not authorized";
819 else if(x == XMPP::ClientStream::TemporaryAuthFailure)
820 s = "temporary auth failure";
821 appendErrMsg(QString("Auth error: %1").arg(s));
822 }
823 else if(err == XMPP::ClientStream::ErrSecurityLayer)
824 appendErrMsg("Broken security layer (SASL)");
825 cleanup();
826 }
827
828 private:
setHostState()829 void setHostState()
830 {
831 bool ok = false;
832 if(!ck_probe->isChecked() && cb_proxy->currentIndex() != 3)
833 ok = true;
834 lb_host->setEnabled(ok);
835 le_host->setEnabled(ok);
836 ck_ssl->setEnabled(ok);
837 }
838
appendSysMsg(const QString & s,const QColor & _c=QColor ())839 void appendSysMsg(const QString &s, const QColor &_c=QColor())
840 {
841 QString str;
842 QColor c;
843 if(_c.isValid())
844 c = _c;
845 else
846 c = Qt::blue;
847
848 if(c.isValid())
849 str += QString("<font color=\"%1\">").arg(c.name());
850 str += QString("*** %1").arg(s);
851 if(c.isValid())
852 str += QString("</font>");
853 te_log->append(str);
854 }
855
856 public:
appendLibMsg(const QString & s)857 void appendLibMsg(const QString &s)
858 {
859 appendSysMsg(s, Qt::magenta);
860 }
861
appendErrMsg(const QString & s)862 void appendErrMsg(const QString &s)
863 {
864 appendSysMsg(s, Qt::red);
865 }
866
appendXmlOut(const QString & s)867 void appendXmlOut(const QString &s)
868 {
869 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
870 QStringList lines = s.split('\n', QString::KeepEmptyParts);
871 #else
872 QStringList lines = s.split('\n', Qt::KeepEmptyParts);
873 #endif
874 QString str;
875 bool first = true;
876 for(QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it) {
877 if(!first)
878 str += "<br>";
879 str += QString("<font color=\"%1\">%2</font>").arg(QColor(Qt::darkGreen).name()).arg(plain2rich(*it));
880 first = false;
881 }
882 te_log->append(str);
883 }
884
appendXmlIn(const QString & s)885 void appendXmlIn(const QString &s)
886 {
887 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
888 QStringList lines = s.split('\n', QString::KeepEmptyParts);
889 #else
890 QStringList lines = s.split('\n', Qt::KeepEmptyParts);
891 #endif
892 QString str;
893 bool first = true;
894 for(QStringList::ConstIterator it = lines.begin(); it != lines.end(); ++it) {
895 if(!first)
896 str += "<br>";
897 str += QString("<font color=\"%1\">%2</font>").arg(QColor(Qt::darkBlue).name()).arg(plain2rich(*it));
898 first = false;
899 }
900 te_log->append(str);
901 }
902 };
903
904 TestDlg *td_glob = 0;
905
msg(const QString & s)906 void TestDebug::msg(const QString &s)
907 {
908 if(td_glob)
909 td_glob->appendLibMsg(s);
910 }
911
outgoingTag(const QString & s)912 void TestDebug::outgoingTag(const QString &s)
913 {
914 if(td_glob)
915 td_glob->appendXmlOut(s);
916 }
917
incomingTag(const QString & s)918 void TestDebug::incomingTag(const QString &s)
919 {
920 if(td_glob)
921 td_glob->appendXmlIn(s);
922 }
923
outgoingXml(const QDomElement & e)924 void TestDebug::outgoingXml(const QDomElement &e)
925 {
926 QString out = XMPP::Stream::xmlToString(e, true);
927 if(td_glob)
928 td_glob->appendXmlOut(out);
929 }
930
incomingXml(const QDomElement & e)931 void TestDebug::incomingXml(const QDomElement &e)
932 {
933 QString out = XMPP::Stream::xmlToString(e, true);
934 if(td_glob)
935 td_glob->appendXmlIn(out);
936 }
937
938
939
main(int argc,char ** argv)940 int main(int argc, char **argv)
941 {
942 QCA::Initializer init;
943
944 #ifdef Q_OS_WIN32
945 QApplication::addLibraryPath(".");
946 putenv("SASL_PATH=.\\sasl");
947 #endif
948 QApplication app(argc, argv);
949
950 // seed the random number generator (needed at least for HttpPoll)
951 srand(time(NULL));
952
953 TestDlg *w = new TestDlg(0);
954 td_glob = w;
955 TestDebug *td = new TestDebug;
956 XMPP::setDebug(td);
957 QObject::connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
958 app.exec();
959 XMPP::setDebug(0);
960 delete td;
961 delete w;
962
963 // we need this for a clean exit
964 QCA::unloadAllPlugins();
965
966 return 0;
967 }
968
969 #ifdef QCA_STATIC
970 #include <QtPlugin>
971 #ifdef HAVE_OPENSSL
972 Q_IMPORT_PLUGIN(qca_openssl)
973 #endif
974 #endif
975