void CoreProtocol::handleStreamOpen(const Parser::Event &pe) { if(isIncoming()) { QString ns = pe.nsprefix(); QString db; if(server) { db = pe.nsprefix("db"); if(!db.isEmpty()) dialback = true; } // verify namespace if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) { delayErrorAndClose(InvalidNamespace); return; } // verify version if(version.major < 1 && !dialback) { delayErrorAndClose(UnsupportedVersion); return; } } else { if(!dialback) { if(version.major >= 1 && !oldOnly) old = false; else old = true; } } }
void checkNeedMore() { // Here we will work around QXmlSimpleReader strangeness and self-closing tags. // The problem is that endElement() is called when the '/' is read, not when // the final '>' is read. This is a potential problem when obtaining unprocessed // bytes from StreamInput after this event, as the '>' character will end up // in the unprocessed chunk. To work around this, we need to advance StreamInput's // internal byte processing, but not the xml character data. This way, the '>' // will get processed and will no longer be in the unprocessed return, but // QXmlSimpleReader can still read it. To do this, we call StreamInput::readNext // with 'peek' mode. QChar c = in->readNext(true); // peek if(c == QXmlInputSource::EndOfData) { needMore = true; } else { // We'll assume the next char is a '>'. If it isn't, then // QXmlSimpleReader will deal with that problem on the next // parse. We don't need to take any action here. needMore = false; // there should have been a pending event Parser::Event *e = eventList.first(); if(!eventList.isEmpty()) { e->setActualString(e->actualString() + '>'); in->resetLastData(); } } }
/** * Process incoming stanza (first-level element). */ void Stream::processStanza(const Parser::Event& event) { QString id = event.element().attribute("id"); if (!id.isEmpty() && d->scb.contains(id)) { Private::StanzaCallback cb = d->scb.value(id); QObject *obj = cb.first; qDebug("[XMPP::Stream] Invoke %s::%s for stanza with id %s", obj->metaObject()->className(), qPrintable(cb.second), qPrintable(id)); QMetaObject::invokeMethod(obj, cb.second.toLocal8Bit().constData(), Q_ARG(XMPP::Stanza, event.element())); d->scb.remove(id); } if ( event.qualifiedName() == "stream:error" ) { handleStreamError(event); } else if ( event.qualifiedName() == "message" ) { emit stanzaMessage(event.element()); } else if ( event.qualifiedName() == "iq" ) { emit stanzaIQ(event.element()); } else if ( event.qualifiedName() == "presence" ) { emit stanzaPresence(event.element()); } else { if (!handleUnknownElement(event)) qDebug("[XMPP::Stream] Unhandled first-level element: %s", qPrintable(event.qualifiedName())); } }
void Stream::bsReadyRead() { QByteArray data = d->bytestream->readAll(); // qDebug("[XMPP:Stream] -recv-: %s", qPrintable(QString::fromUtf8(data))); d->parser.appendData(data); Parser::Event event = d->parser.readNext(); while ( !event.isNull() ) { processEvent(event); event = d->parser.readNext(); } }
void BasicProtocol::handleDocOpen(const Parser::Event &pe) { if(isIncoming()) { if(xmlEncoding() != "UTF-8") { delayErrorAndClose(UnsupportedEncoding); return; } } if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") { QXmlAttributes atts = pe.atts(); // grab the version int major = 0; int minor = 0; QString verstr = atts.value("version"); if(!verstr.isEmpty()) { int n = verstr.find('.'); if(n != -1) { major = verstr.mid(0, n).toInt(); minor = verstr.mid(n+1).toInt(); } else { major = verstr.toInt(); minor = 0; } } version = Version(major, minor); if(isIncoming()) { to = atts.value("to"); QString peerLang = atts.value(NS_XML, "lang"); if(!peerLang.isEmpty()) lang = peerLang; } // outgoing else { from = atts.value("from"); lang = atts.value(NS_XML, "lang"); id = atts.value("id"); } handleStreamOpen(pe); } else { if(isIncoming()) delayErrorAndClose(BadFormat); else delayError(ErrProtocol); } }
bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts) { if(depth == 0) { Parser::Event *e = new Parser::Event; QXmlAttributes a; for(int n = 0; n < atts.length(); ++n) { QString uri = atts.uri(n); QString ln = atts.localName(n); if(a.index(uri, ln) == -1) a.append(atts.qName(n), uri, ln, atts.value(n)); } e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues); nsnames.clear(); nsvalues.clear(); e->setActualString(in->lastString()); in->resetLastData(); eventList.append(e); in->pause(true); } else { QDomElement e = doc->createElementNS(namespaceURI, qName); for(int n = 0; n < atts.length(); ++n) { QString uri = atts.uri(n); QString ln = atts.localName(n); bool have; if(!uri.isEmpty()) { have = e.hasAttributeNS(uri, ln); if(qt_bug_have) have = !have; } else have = e.hasAttribute(ln); if(!have) e.setAttributeNS(uri, atts.qName(n), atts.value(n)); } if(depth == 1) { elem = e; current = e; } else { current.appendChild(e); current = e; } } ++depth; return true; }
bool XmlProtocol::baseStep(const Parser::Event &pe) { // Basic if(state == SendOpen) { sendTagOpen(); event = ESend; if(incoming) state = Open; else state = RecvOpen; return true; } else if(state == RecvOpen) { if(incoming) state = SendOpen; else state = Open; // note: event will always be DocumentOpen here handleDocOpen(pe); event = ERecvOpen; return true; } else if(state == Open) { QDomElement e; if(pe.type() == Parser::Event::Element) e = pe.element(); return doStep(e); } // Closing else { if(closeWritten) { if(peerClosed) { event = EPeerClosed; return true; } else return handleCloseFinished(); } need = NNotify; notify = NSend; return false; } }
bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName) { --depth; if(depth == 0) { Parser::Event *e = new Parser::Event; e->setDocumentClose(namespaceURI, localName, qName); e->setActualString(in->lastString()); in->resetLastData(); eventList.append(e); in->pause(true); } else { // done with a depth 1 element? if(depth == 1) { Parser::Event *e = new Parser::Event; e->setElement(elem); e->setActualString(in->lastString()); in->resetLastData(); eventList.append(e); in->pause(true); elem = QDomElement(); current = QDomElement(); } else current = current.parentNode().toElement(); } if(in->lastRead() == '/') checkNeedMore(); return true; }
/** * Process events from incoming stream. */ void Stream::processEvent(const Parser::Event& event) { switch ( event.type() ) { case Parser::Event::DocumentOpen: { qDebug("[XMPP::Stream] Remote entity has opened the stream"); handleStreamOpen(event); emit streamOpened(); } break; case Parser::Event::DocumentClose: { qWarning("[XMPP::Stream] Remote entity has closed the stream"); emit streamClosed(); } break; case Parser::Event::Element: processStanza(event); break; case Parser::Event::Error: qCritical("[XMPP::Stream] Parser error"); break; } }
bool XmlProtocol::processStep() { Parser::Event pe; notify = 0; transferItemList.clear(); if(state != Closing && (state == RecvOpen || stepAdvancesParser())) { // if we get here, then it's because we're in some step that advances the parser pe = xml.readNext(); if(!pe.isNull()) { // note: error/close events should be handled for ALL steps, so do them here switch(pe.type()) { case Parser::Event::DocumentOpen: { transferItemList += TransferItem(pe.actualString(), false); //stringRecv(pe.actualString()); break; } case Parser::Event::DocumentClose: { transferItemList += TransferItem(pe.actualString(), false); //stringRecv(pe.actualString()); if(incoming) { sendTagClose(); event = ESend; peerClosed = true; state = Closing; } else { event = EPeerClosed; } return true; } case Parser::Event::Element: { QDomElement e = elemDoc.importNode(pe.element(),true).toElement(); transferItemList += TransferItem(e, false); //elementRecv(pe.element()); break; } case Parser::Event::Error: { if(incoming) { // If we get a parse error during the initial element exchange, // flip immediately into 'open' mode so that we can report an error. if(state == RecvOpen) { sendTagOpen(); state = Open; } return handleError(); } else { event = EError; errorCode = ErrParse; return true; } } } } else { if(state == RecvOpen || stepRequiresElement()) { need = NNotify; notify |= NRecv; return false; } } } return baseStep(pe); }
/** * Handles stream erorrs (\<stream:error/\> element). */ void Stream::handleStreamError(const Parser::Event& event) { d->lastStreamError = StreamError( event.element() ); emit streamError(); }