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 += "&lt;";
55 		else if(plain[i] == '>')
56 			rich += "&gt;";
57 		else if(plain[i] == '\"')
58 			rich += "&quot;";
59 		else if(plain[i] == '\'')
60 			rich += "&apos;";
61 		else if(plain[i] == '&')
62 			rich += "&amp;";
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