Пример #1
0
FileTransferHandler::FileTransferHandler(PsiAccount *pa, FileTransfer *ft)
{
	d = new Private;
	d->pa = pa;
	d->c = 0;

	if(ft) {
		d->sending = false;
		d->peer = ft->peer();
		d->fileName = clean_filename(ft->fileName());
		d->fileSize = ft->fileSize();
		d->desc = ft->description();
		d->shift = calcShift(d->fileSize);
		d->complement = calcComplement(d->fileSize, d->shift);
		d->ft = ft;
		Jid proxy = d->pa->userAccount().dtProxy;
		if(proxy.isValid())
			d->ft->setProxy(proxy);
		mapSignals();
	}
	else {
		d->sending = true;
		d->ft = 0;
	}
}
Пример #2
0
bool SASLBind::xmppStanzaIn(IXmppStream *AXmppStream, Stanza &AStanza, int AOrder)
{
	if (AXmppStream==FXmppStream && AOrder==XSHO_XMPP_FEATURE && AStanza.id()=="bind")
	{
		FXmppStream->removeXmppStanzaHandler(this, XSHO_XMPP_FEATURE);
		if (AStanza.type() == "result")
		{
			Jid streamJid = AStanza.firstElement().firstChild().toElement().text();
			if (streamJid.isValid())
			{
				LogDetaile(QString("[SASLBind][%1] XMPP session binded with resource='%2'").arg(FXmppStream->streamJid().bare()).arg(streamJid.resource()));
				deleteLater();
				FXmppStream->setStreamJid(streamJid);
				emit finished(false);
			}
			else
			{
				LogError(QString("[SASLBind][%1] Invalid XMPP stream JID in response").arg(FXmppStream->streamJid().bare()));
				emit error(tr("Invalid XMPP stream JID in SASL bind response"));
			}
		}
		else
		{
			ErrorHandler err(AStanza.element());
			LogError(QString("[SASLBind] Failed to bind XMPP session: %1").arg(err.message()));
			emit error(err.message());
		}
		return true;
	}
	return false;
}
Пример #3
0
void MUCJoinDlg::doJoin()
{
	if(!d->pa || !d->pa->checkConnected(this))
		return;

	QString host = le_host->text();
	QString room = le_room->text();
	QString nick = le_nick->text();
	QString pass = le_pass->text();

	if(host.isEmpty() || room.isEmpty() || nick.isEmpty()) {
		QMessageBox::information(this, tr("Error"), tr("You must fill out the fields in order to join."));
		return;
	}

	Jid j = room + '@' + host + '/' + nick;
	if(!j.isValid()) {
		QMessageBox::information(this, tr("Error"), tr("You entered an invalid room name."));
		return;
	}

	if(!d->pa->groupChatJoin(host, room, nick, pass, !ck_history->isChecked())) {
		QMessageBox::information(this, tr("Error"), tr("You are in or joining this room already!"));
		return;
	}

	d->psi->dialogUnregister(this);
	d->jid = room + '@' + host + '/' + nick;
	d->pa->dialogRegister(this, d->jid);

	disableWidgets();
	d->busy->start();
}
Пример #4
0
bool ClientInfo::requestLastActivity(const Jid &AStreamJid, const Jid &AContactJid)
{
	bool sent = FActivityId.values().contains(AContactJid);
	if (!sent && AStreamJid.isValid() && AContactJid.isValid())
	{
		Stanza iq(STANZA_KIND_IQ);
		iq.setType(STANZA_TYPE_GET).setTo(AContactJid.full()).setUniqueId();
		iq.addElement("query",NS_JABBER_LAST);
		sent = FStanzaProcessor->sendStanzaRequest(this,AStreamJid,iq,LAST_ACTIVITY_TIMEOUT);
		if (sent)
		{
			FActivityId.insert(iq.id(),AContactJid);
			LOG_STRM_INFO(AStreamJid,QString("Last activity request sent to=%1").arg(AContactJid.full()));
		}
		else
		{
			LOG_STRM_WARNING(AStreamJid,QString("Failed to send last activity request to=%1").arg(AContactJid.full()));
		}
	}
	return sent;
}
Пример #5
0
bool ClientInfo::requestSoftwareInfo(const Jid &AStreamJid, const Jid &AContactJid)
{
	bool sent = FSoftwareId.values().contains(AContactJid);
	if (!sent && AStreamJid.isValid() && AContactJid.isValid())
	{
		Stanza iq(STANZA_KIND_IQ);
		iq.setType(STANZA_TYPE_GET).setTo(AContactJid.full()).setUniqueId();
		iq.addElement("query",NS_JABBER_VERSION);
		sent = FStanzaProcessor->sendStanzaRequest(this,AStreamJid,iq,SOFTWARE_INFO_TIMEOUT);
		if (sent)
		{
			FSoftwareId.insert(iq.id(),AContactJid);
			FSoftwareItems[AContactJid].status = SoftwareLoading;
			LOG_STRM_INFO(AStreamJid,QString("Software version request sent to=%1").arg(AContactJid.full()));
		}
		else
		{
			LOG_STRM_WARNING(AStreamJid,QString("Failed to sent software request to=%1").arg(AContactJid.full()));
		}
	}
	return sent;
}
Пример #6
0
void NewPoi::initStreamList(const QString &ABareJid, bool AEditable)
{
    if (FAccounts)
    {
        Jid streamJid;
        if (!ABareJid.isEmpty())
            streamJid=FPoi->findStreamJid(ABareJid);
        for (QList<IAccount *>::const_iterator it=FAccounts->constBegin(); it!=FAccounts->constEnd(); it++)
            ui->boxAccount->addItem((*it)->name(), QVariant((*it)->streamJid().full()));
        ui->boxAccount->setCurrentIndex(streamJid.isValid()?ui->boxAccount->findData(QVariant(streamJid.full())):0);
    }
    ui->boxAccount->setEnabled(AEditable);
}
Пример #7
0
bool ClientInfo::requestEntityTime(const Jid &AStreamJid, const Jid &AContactJid)
{
	bool sent = FTimeId.values().contains(AContactJid);
	if (!sent && AStreamJid.isValid() && AContactJid.isValid())
	{
		Stanza iq(STANZA_KIND_IQ);
		iq.setType(STANZA_TYPE_GET).setTo(AContactJid.full()).setUniqueId();
		iq.addElement("time",NS_XMPP_TIME);
		sent = FStanzaProcessor->sendStanzaRequest(this,AStreamJid,iq,ENTITY_TIME_TIMEOUT);
		if (sent)
		{
			TimeItem &tItem = FTimeItems[AContactJid];
			tItem.ping = QTime::currentTime().msecsTo(QTime(0,0,0,0));
			FTimeId.insert(iq.id(),AContactJid);
			LOG_STRM_INFO(AStreamJid,QString("Current time request sent to=%1").arg(AContactJid.full()));
			emit entityTimeChanged(AContactJid);
		}
		else
		{
			LOG_STRM_WARNING(AStreamJid,QString("Failed to send current time request to=%1").arg(AContactJid.full()));
		}
	}
	return sent;
}
Пример #8
0
void ClientInfo::showClientInfo(const Jid &AStreamJid, const Jid &AContactJid, int AInfoTypes)
{
	if (AStreamJid.isValid() && AContactJid.isValid() && AInfoTypes>0)
	{
		ClientInfoDialog *dialog = FClientInfoDialogs.value(AContactJid,NULL);
		if (!dialog)
		{
			QString contactName =  AContactJid.uNode();
			if (FDiscovery!=NULL && FDiscovery->discoInfo(AStreamJid,AContactJid.bare()).identity.value(0).category=="conference")
				contactName = AContactJid.resource();
			if (contactName.isEmpty())
				contactName = FDiscovery!=NULL ? FDiscovery->discoInfo(AStreamJid,AContactJid).identity.value(0).name : AContactJid.domain();
			
			if (FRosterManager)
			{
				IRoster *roster = FRosterManager->findRoster(AStreamJid);
				if (roster)
				{
					IRosterItem ritem = roster->findItem(AContactJid);
					if (!ritem.name.isEmpty())
						contactName = ritem.name;
				}
			}

			dialog = new ClientInfoDialog(this,AStreamJid,AContactJid,!contactName.isEmpty() ? contactName : AContactJid.uFull(),AInfoTypes);
			connect(dialog,SIGNAL(clientInfoDialogClosed(const Jid &)),SLOT(onClientInfoDialogClosed(const Jid &)));
			FClientInfoDialogs.insert(AContactJid,dialog);
			dialog->show();
		}
		else
		{
			dialog->setInfoTypes(dialog->infoTypes() | AInfoTypes);
			WidgetManager::showActivateRaiseWindow(dialog);
		}
	}
}
Пример #9
0
QString XmppUriQueries::makeXmppUri(const Jid &AContactJid, const QString &AAction, const QMultiMap<QString, QString> &AParams) const
{
	if (AContactJid.isValid() && !AAction.isEmpty())
	{
		QUrl url;
		url.setQueryDelimiters('=',';');

		url.setScheme(XMPP_URI_SCHEME);
		url.setPath(AContactJid.full());

		QList< QPair<QString, QString> > query;
		query.append(qMakePair<QString,QString>(AAction,QString::null));
		for(QMultiMap<QString, QString>::const_iterator it=AParams.constBegin(); it!=AParams.end(); ++it)
			query.append(qMakePair<QString,QString>(it.key(),it.value()));
		url.setQueryItems(query);

		return url.toString().replace(QString("?%1=;").arg(AAction),QString("?%1;").arg(AAction));
	}
	return QString::null;
}
Пример #10
0
Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id)
{
	Q_ASSERT(s);
	d = new Private;

	Kind kind;
	if(k == Message || k == Presence || k == IQ)
		kind = k;
	else
		kind = Message;

	d->s = s;
	if(d->s)
		d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind));
	if(to.isValid())
		setTo(to);
	if(!type.isEmpty())
		setType(type);
	if(!id.isEmpty())
		setId(id);
}
Пример #11
0
bool XmppUriQueries::parseXmppUri(const QUrl &AUrl, Jid &AContactJid, QString &AAction, QMultiMap<QString, QString> &AParams) const
{
	if (AUrl.isValid() && AUrl.scheme()==XMPP_URI_SCHEME)
	{
		QUrl url =  QUrl::fromEncoded(AUrl.toEncoded().replace(';','&'), QUrl::StrictMode);

		QList< QPair<QString, QString> > keyValues = url.queryItems();
		if (!keyValues.isEmpty())
		{
			AContactJid = url.path();
			AAction = keyValues.takeAt(0).first;
			if (AContactJid.isValid() && !AAction.isEmpty())
			{
				for (int i=0; i<keyValues.count(); i++)
					AParams.insertMulti(keyValues.at(i).first, keyValues.at(i).second);
				return true;
			}
		}
	}
	return false;
}
Пример #12
0
bool XmppUriQueries::openXmppUri(const Jid &AStreamJid, const QUrl &AUrl) const
{
	if (AUrl.isValid() && AUrl.scheme()=="xmpp")
	{
		QUrl url =  QUrl::fromEncoded(AUrl.toEncoded().replace(';','&'), QUrl::StrictMode);
		Jid contactJid = url.path();
		QList< QPair<QString, QString> > keyValues = url.queryItems();
		if (keyValues.count() > 0)
		{
			QString action = keyValues.takeAt(0).first;
			if (contactJid.isValid() && !action.isEmpty())
			{
				QMultiMap<QString, QString> params;
				for (int i=0; i<keyValues.count(); i++)
					params.insertMulti(keyValues.at(i).first, keyValues.at(i).second);

				LOG_STRM_INFO(AStreamJid,QString("Opening XMPP URI, url=%1").arg(AUrl.toString()));
				foreach (IXmppUriHandler *handler, FHandlers)
				{
					if (handler->xmppUriOpen(AStreamJid, contactJid, action, params))
						return true;
				}
			}
Пример #13
0
void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
{
	if(!d->sending)
		return;

	d->peer = to;
	QFileInfo fi(fname);
	d->filePath = fname;
	d->fileName = fi.fileName();
	d->fileSize = fi.size(); // TODO: large file support
	d->desc = desc;
	d->shift = calcShift(d->fileSize);
	d->complement = calcComplement(d->fileSize, d->shift);

	d->ft = d->pa->client()->fileTransferManager()->createTransfer();
	Jid proxy = d->pa->userAccount().dtProxy;
	if(proxy.isValid())
		d->ft->setProxy(proxy);
	mapSignals();

	d->f.setFileName(fname);
	d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc);
}
Пример #14
0
void FileTransferHandler::send(const XMPP::Jid &to, const QString &fname, const QString &desc)
{
	if(!d->sending)
		return;

	d->peer = to;
	QFileInfo fi(fname);
	d->filePath = fname;
	d->fileName = fi.fileName();
	d->fileSize = fi.size(); // TODO: large file support
	d->desc = desc;
	d->shift = calcShift(d->fileSize);
	d->complement = calcComplement(d->fileSize, d->shift);

	d->ft = d->pa->client()->fileTransferManager()->createTransfer();
	Jid proxy = d->pa->userAccount().dtProxy;
	if(proxy.isValid())
		d->ft->setProxy(proxy);
	mapSignals();

	d->f.setFileName(fname);

	// try to make thumbnail
	QImage img(fname);
	XMPP::FTThumbnail thumb;
	if (!img.isNull()) {
		QByteArray ba;
		QBuffer buffer(&ba);
		buffer.open(QIODevice::WriteOnly);
		img = img.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation);
		img.save(&buffer, "PNG");
		thumb = XMPP::FTThumbnail(ba, "image/png", img.width(), img.height());
	}

	d->ft->sendFile(d->peer, d->fileName, d->fileSize, desc, thumb);
}
Пример #15
0
bool CoreProtocol::normalStep(const QDomElement &e)
{
	if(step == Start) {
		if(isIncoming()) {
			need = NSASLMechs;
			step = SendFeatures;
			return false;
		}
		else {
			if(old) {
				if(doAuth)
					step = HandleAuthGet;
				else
					return loginComplete();
			}
			else
				step = GetFeatures;

			return processStep();
		}
	}
	else if(step == HandleFeatures) {
		// deal with TLS?
		if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
			QDomElement e = doc.createElementNS(NS_TLS, "starttls");

			send(e, true);
			event = ESend;
			step = GetTLSProceed;
			return true;
		}

		// Should we go further ?
		if (!doAuth)
			return loginComplete();
		
		// Deal with compression
		if (doCompress && !compress_started && features.compress_supported && features.compression_mechs.contains("zlib")) {
			QDomElement e = doc.createElementNS(NS_COMPRESS_PROTOCOL, "compress");
			QDomElement m = doc.createElementNS(NS_COMPRESS_PROTOCOL, "method");
			m.appendChild(doc.createTextNode("zlib"));
			e.appendChild(m);
			send(e,true);
			event = ESend;
			step = GetCompressProceed;
			return true;
		}

		// deal with SASL?
		if(!sasl_authed) {
			if(!features.sasl_supported) {
				// SASL MUST be supported
				//event = EError;
				//errorCode = ErrProtocol;
				//return true;
				
				// Fall back on auth for non-compliant servers 
				step = HandleAuthGet;
				old = true;
				return true;
			}

#ifdef XMPP_TEST
			TD::msg("starting SASL authentication...");
#endif
			need = NSASLFirst;
			step = GetSASLFirst;
			return false;
		}

		if(server) {
			return loginComplete();
		}
		else {
			if(!doBinding)
				return loginComplete();
		}

		// deal with bind
		if(!features.bind_supported) {
			// bind MUST be supported
			event = EError;
			errorCode = ErrProtocol;
			return true;
		}

		QDomElement e = doc.createElement("iq");
		e.setAttribute("type", "set");
		e.setAttribute("id", "bind_1");
		QDomElement b = doc.createElementNS(NS_BIND, "bind");

		// request specific resource?
		QString resource = jid_.resource();
		if(!resource.isEmpty()) {
			QDomElement r = doc.createElement("resource");
			r.appendChild(doc.createTextNode(jid_.resource()));
			b.appendChild(r);
		}

		e.appendChild(b);

		send(e);
		event = ESend;
		step = GetBindResponse;
		return true;
	}
	else if(step == GetSASLFirst) {
		QDomElement e = doc.createElementNS(NS_SASL, "auth");
		e.setAttribute("mechanism", sasl_mech);
		if(!sasl_step.isEmpty()) {
#ifdef XMPP_TEST
			TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
#endif
			e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(sasl_step)));
		}

		send(e, true);
		event = ESend;
		step = GetSASLChallenge;
		return true;
	}
	else if(step == GetSASLNext) {
		if(isIncoming()) {
			if(sasl_authed) {
				QDomElement e = doc.createElementNS(NS_SASL, "success");
				writeElement(e, TypeElement, false, true);
				event = ESend;
				step = IncHandleSASLSuccess;
				return true;
			}
			else {
				QByteArray stepData = sasl_step;
				QDomElement e = doc.createElementNS(NS_SASL, "challenge");
				if(!stepData.isEmpty())
					e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));

				writeElement(e, TypeElement, false, true);
				event = ESend;
				step = GetSASLResponse;
				return true;
			}
		}
		else {
			// already authed?  then ignore last client step
			//   (this happens if "additional data with success"
			//   is used)
			if(sasl_authed)
			{
				event = ESASLSuccess;
				step = HandleSASLSuccess;
				return true;
			}

			QByteArray stepData = sasl_step;
#ifdef XMPP_TEST
			TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
#endif
			QDomElement e = doc.createElementNS(NS_SASL, "response");
			if(!stepData.isEmpty())
				e.appendChild(doc.createTextNode(QCA::Base64().arrayToString(stepData)));

			send(e, true);
			event = ESend;
			step = GetSASLChallenge;
			return true;
		}
	}
	else if(step == HandleSASLSuccess) {
		need = NSASLLayer;
		spare = resetStream();
		step = Start;
		return false;
	}
	else if(step == HandleAuthGet) {
		QDomElement e = doc.createElement("iq");
		e.setAttribute("to", to);
		e.setAttribute("type", "get");
		e.setAttribute("id", "auth_1");
		QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
		QDomElement u = doc.createElement("username");
		u.appendChild(doc.createTextNode(jid_.node()));
		q.appendChild(u);
		e.appendChild(q);

		send(e);
		event = ESend;
		step = GetAuthGetResponse;
		return true;
	}
	else if(step == HandleAuthSet) {
		QDomElement e = doc.createElement("iq");
		e.setAttribute("to", to);
		e.setAttribute("type", "set");
		e.setAttribute("id", "auth_2");
		QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
		QDomElement u = doc.createElement("username");
		u.appendChild(doc.createTextNode(jid_.node()));
		q.appendChild(u);
		QDomElement p;
		if(digest) {
			// need SHA1 here
			//if(!QCA::isSupported(QCA::CAP_SHA1))
			//	QCA::insertProvider(createProviderHash());

			p = doc.createElement("digest");
			Q3CString cs = id.utf8() + password.utf8();
			p.appendChild(doc.createTextNode(QCA::Hash("sha1").hashToString(cs)));
		}
		else {
			p = doc.createElement("password");
			p.appendChild(doc.createTextNode(password));
		}
		q.appendChild(p);
		QDomElement r = doc.createElement("resource");
		r.appendChild(doc.createTextNode(jid_.resource()));
		q.appendChild(r);
		e.appendChild(q);

		send(e, true);
		event = ESend;
		step = GetAuthSetResponse;
		return true;
	}
	// server
	else if(step == SendFeatures) {
		QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
		if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
			QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
			f.appendChild(tls);
		}

		if(sasl_authed) {
			if(!server) {
				QDomElement bind = doc.createElementNS(NS_BIND, "bind");
				f.appendChild(bind);
			}
		}
		else {
			QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
			for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
				QDomElement m = doc.createElement("mechanism");
				m.appendChild(doc.createTextNode(*it));
				mechs.appendChild(m);
			}
			f.appendChild(mechs);
		}

		writeElement(f, TypeElement, false);
		event = ESend;
		step = GetRequest;
		return true;
	}
	// server
	else if(step == HandleTLS) {
		tls_started = true;
		need = NStartTLS;
		spare = resetStream();
		step = Start;
		return false;
	}
	// server
	else if(step == IncHandleSASLSuccess) {
		event = ESASLSuccess;
		spare = resetStream();
		step = Start;
		printf("sasl success\n");
		return true;
	}
	else if(step == GetFeatures) {
		// we are waiting for stream features
		if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
			// extract features
			StreamFeatures f;
			QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
			if(!s.isNull()) {
				f.tls_supported = true;
				f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
			}
			QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
			if(!m.isNull()) {
				f.sasl_supported = true;
				QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
				for(int n = 0; n < l.count(); ++n)
					f.sasl_mechs += l.item(n).toElement().text();
			}
			QDomElement c = e.elementsByTagNameNS(NS_COMPRESS_FEATURE, "compression").item(0).toElement();
			if(!c.isNull()) {
				f.compress_supported = true;
				QDomNodeList l = c.elementsByTagNameNS(NS_COMPRESS_FEATURE, "method");
				for(int n = 0; n < l.count(); ++n)
					f.compression_mechs += l.item(n).toElement().text();
			}
			QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
			if(!b.isNull())
				f.bind_supported = true;

			if(f.tls_supported) {
#ifdef XMPP_TEST
				QString s = "STARTTLS is available";
				if(f.tls_required)
					s += " (required)";
				TD::msg(s);
#endif
			}
			if(f.sasl_supported) {
#ifdef XMPP_TEST
				QString s = "SASL mechs:";
				for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
					s += QString(" [%1]").arg((*it));
				TD::msg(s);
#endif
			}
			if(f.compress_supported) {
#ifdef XMPP_TEST
				QString s = "Compression mechs:";
				for(QStringList::ConstIterator it = f.compression_mechs.begin(); it != f.compression_mechs.end(); ++it)
					s += QString(" [%1]").arg((*it));
				TD::msg(s);
#endif
			}

			event = EFeatures;
			features = f;
			step = HandleFeatures;
			return true;
		}
		else {
			// ignore
		}
	}
	else if(step == GetTLSProceed) {
		// waiting for proceed to starttls
		if(e.namespaceURI() == NS_TLS) {
			if(e.tagName() == "proceed") {
#ifdef XMPP_TEST
				TD::msg("Server wants us to proceed with ssl handshake");
#endif
				tls_started = true;
				need = NStartTLS;
				spare = resetStream();
				step = Start;
				return false;
			}
			else if(e.tagName() == "failure") {
				event = EError;
				errorCode = ErrStartTLS;
				return true;
			}
			else {
				event = EError;
				errorCode = ErrProtocol;
				return true;
			}
		}
		else {
			// ignore
		}
	}
	else if(step == GetCompressProceed) {
		// waiting for proceed to compression
		if(e.namespaceURI() == NS_COMPRESS_PROTOCOL) {
			if(e.tagName() == "compressed") {
#ifdef XMPP_TEST
				TD::msg("Server wants us to proceed with compression");
#endif
				compress_started = true;
				need = NCompress;
				spare = resetStream();
				step = Start;
				return false;
			}
			else if(e.tagName() == "failure") {
				event = EError;
				errorCode = ErrCompress;
				return true;
			}
			else {
				event = EError;
				errorCode = ErrProtocol;
				return true;
			}
		}
		else {
			// ignore
		}
	}
	else if(step == GetSASLChallenge) {
		// waiting for sasl challenge/success/fail
		if(e.namespaceURI() == NS_SASL) {
			if(e.tagName() == "challenge") {
				QByteArray a = QCA::Base64().stringToArray(e.text()).toByteArray();
#ifdef XMPP_TEST
				TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
#endif
				sasl_step = a;
				need = NSASLNext;
				step = GetSASLNext;
				return false;
			}
			else if(e.tagName() == "success") {
				QString str = e.text();
				// "additional data with success" ?
				if(!str.isEmpty())
				{
					QByteArray a = QCA::Base64().stringToArray(str).toByteArray();
					sasl_step = a;
					sasl_authed = true;
					need = NSASLNext;
					step = GetSASLNext;
					return false;
				}

				sasl_authed = true;
				event = ESASLSuccess;
				step = HandleSASLSuccess;
				return true;
			}
			else if(e.tagName() == "failure") {
				QDomElement t = firstChildElement(e);
				if(t.isNull() || t.namespaceURI() != NS_SASL)
					errCond = -1;
				else
					errCond = stringToSASLCond(t.tagName());

				event = EError;
				errorCode = ErrAuth;
				return true;
			}
			else {
				event = EError;
				errorCode = ErrProtocol;
				return true;
			}
		}
	}
	else if(step == GetBindResponse) {
		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
			QString type(e.attribute("type"));
			QString id(e.attribute("id"));

			if(id == "bind_1" && (type == "result" || type == "error")) {
				if(type == "result") {
					QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
					Jid j;
					if(!b.isNull()) {
						QDomElement je = e.elementsByTagName("jid").item(0).toElement();
						j = je.text();
					}
					if(!j.isValid()) {
						event = EError;
						errorCode = ErrProtocol;
						return true;
					}
					jid_ = j;
					return loginComplete();
				}
				else {
					errCond = -1;

					QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
					if(!err.isNull()) {
						// get error condition
						QDomNodeList nl = err.childNodes();
						QDomElement t;
						for(int n = 0; n < nl.count(); ++n) {
							QDomNode i = nl.item(n);
							if(i.isElement()) {
								t = i.toElement();
								break;
							}
						}
						if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
							QString cond = t.tagName();
							if(cond == "not-allowed")
								errCond = BindNotAllowed;
							else if(cond == "conflict")
								errCond = BindConflict;
						}
					}

					event = EError;
					errorCode = ErrBind;
					return true;
				}
			}
			else {
				// ignore
			}
		}
		else {
			// ignore
		}
	}
	else if(step == GetAuthGetResponse) {
		// waiting for an iq
		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
			Jid from(e.attribute("from"));
			QString type(e.attribute("type"));
			QString id(e.attribute("id"));

			bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
			if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
				if(type == "result") {
					QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
					if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
						event = EError;
						errorCode = ErrProtocol;
						return true;
					}
					bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
					bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();

					if(!digest_supported && !plain_supported) {
						event = EError;
						errorCode = ErrProtocol;
						return true;
					}

					// plain text not allowed?
					if(!digest_supported && !allowPlain) {
						event = EError;
						errorCode = ErrPlain;
						return true;
					}

					digest = digest_supported;
					need = NPassword;
					step = HandleAuthSet;
					return false;
				}
				else {
					errCond = getOldErrorCode(e);

					event = EError;
					errorCode = ErrAuth;
					return true;
				}
			}
			else {
				// ignore
			}
		}
		else {
			// ignore
		}
	}
	else if(step == GetAuthSetResponse) {
		// waiting for an iq
		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
			Jid from(e.attribute("from"));
			QString type(e.attribute("type"));
			QString id(e.attribute("id"));

			bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
			if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
				if(type == "result") {
					return loginComplete();
				}
				else {
					errCond = getOldErrorCode(e);

					event = EError;
					errorCode = ErrAuth;
					return true;
				}
			}
			else {
				// ignore
			}
		}
		else {
			// ignore
		}
	}
	// server
	else if(step == GetRequest) {
		printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
		if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
			// TODO: don't let this be done twice

			QDomElement e = doc.createElementNS(NS_TLS, "proceed");
			writeElement(e, TypeElement, false, true);
			event = ESend;
			step = HandleTLS;
			return true;
		}
		if(e.namespaceURI() == NS_SASL) {
			if(e.localName() == "auth") {
				if(sasl_started) {
					// TODO
					printf("error\n");
					return false;
				}

				sasl_started = true;
				sasl_mech = e.attribute("mechanism");
				// TODO: if child text missing, don't pass it
				sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
				need = NSASLFirst;
				step = GetSASLNext;
				return false;
			}
			else {
				// TODO
				printf("unknown sasl tag\n");
				return false;
			}
		}
		if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
			QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
			if(!b.isNull()) {
				QDomElement res = b.elementsByTagName("resource").item(0).toElement();
				QString resource = res.text();

				QDomElement r = doc.createElement("iq");
				r.setAttribute("type", "result");
				r.setAttribute("id", e.attribute("id"));
				QDomElement bind = doc.createElementNS(NS_BIND, "bind");
				QDomElement jid = doc.createElement("jid");
				Jid j = user + '@' + host + '/' + resource;
				jid.appendChild(doc.createTextNode(j.full()));
				bind.appendChild(jid);
				r.appendChild(bind);

				writeElement(r, TypeElement, false);
				event = ESend;
				// TODO
				return true;
			}
			else {
				// TODO
			}
		}
	}
	else if(step == GetSASLResponse) {
		if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
			sasl_step = QCA::Base64().stringToArray(e.text()).toByteArray();
			need = NSASLNext;
			step = GetSASLNext;
			return false;
		}
	}

	if(isReady()) {
		if(!e.isNull() && isValidStanza(e)) {
			stanzaToRecv = e;
			event = EStanzaReady;
			setIncomingAsExternal();
			return true;
		}
	}

	need = NNotify;
	notify |= NRecv;
	return false;
}