diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2011-06-23 14:32:07 +0000 |
|---|---|---|
| committer | Krzysztof KosiĆski <tweenk.pl@gmail.com> | 2011-06-23 14:32:07 +0000 |
| commit | ec576062be919237064a454f812ac1bfad12debc (patch) | |
| tree | 45d34bdffa67525c823d1d6ea2237d6d2da571c6 /src/pedro/pedroxmpp.cpp | |
| parent | Fix aliasing warnings in glib-list-iterators.h by adding G_GNUC_MAY_ALIAS (diff) | |
| download | inkscape-ec576062be919237064a454f812ac1bfad12debc.tar.gz inkscape-ec576062be919237064a454f812ac1bfad12debc.zip | |
Completely remove Inkboard
(bzr r10346)
Diffstat (limited to 'src/pedro/pedroxmpp.cpp')
| -rw-r--r-- | src/pedro/pedroxmpp.cpp | 3649 |
1 files changed, 0 insertions, 3649 deletions
diff --git a/src/pedro/pedroxmpp.cpp b/src/pedro/pedroxmpp.cpp deleted file mode 100644 index 3baeeee06..000000000 --- a/src/pedro/pedroxmpp.cpp +++ /dev/null @@ -1,3649 +0,0 @@ -/* - * Implementation the Pedro mini-XMPP client - * - * Authors: - * Bob Jamison - * - * Copyright (C) 2005-2008 Bob Jamison - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include <algorithm> -#include <cstdio> -#include <stdarg.h> -#include <stdlib.h> - -#include <sys/stat.h> - -#include <time.h> - -#include "pedroxmpp.h" -#include "pedrodom.h" -#include "pedroutil.h" - -#include <map> - - - -namespace Pedro -{ - - -//######################################################################## -//######################################################################## -//# X M P P E V E N T -//######################################################################## -//######################################################################## - - -XmppEvent::XmppEvent(int type) -{ - eventType = type; - presence = false; - dom = NULL; -} - -XmppEvent::XmppEvent(const XmppEvent &other) -{ - assign(other); -} - -XmppEvent &XmppEvent::operator=(const XmppEvent &other) -{ - assign(other); - return (*this); -} - -XmppEvent::~XmppEvent() -{ - if (dom) - delete dom; -} - -void XmppEvent::assign(const XmppEvent &other) -{ - eventType = other.eventType; - presence = other.presence; - status = other.status; - show = other.show; - to = other.to; - from = other.from; - group = other.group; - data = other.data; - fileName = other.fileName; - fileDesc = other.fileDesc; - fileSize = other.fileSize; - fileHash = other.fileHash; - setDOM(other.dom); -} - -int XmppEvent::getType() const -{ - return eventType; -} - -DOMString XmppEvent::getIqId() const -{ - return iqId; -} - -void XmppEvent::setIqId(const DOMString &val) -{ - iqId = val; -} - -DOMString XmppEvent::getStreamId() const -{ - return streamId; -} - -void XmppEvent::setStreamId(const DOMString &val) -{ - streamId = val; -} - -bool XmppEvent::getPresence() const -{ - return presence; -} - -void XmppEvent::setPresence(bool val) -{ - presence = val; -} - -DOMString XmppEvent::getShow() const -{ - return show; -} - -void XmppEvent::setShow(const DOMString &val) -{ - show = val; -} - -DOMString XmppEvent::getStatus() const -{ - return status; -} - -void XmppEvent::setStatus(const DOMString &val) -{ - status = val; -} - -DOMString XmppEvent::getTo() const -{ - return to; -} - -void XmppEvent::setTo(const DOMString &val) -{ - to = val; -} - -DOMString XmppEvent::getFrom() const -{ - return from; -} - -void XmppEvent::setFrom(const DOMString &val) -{ - from = val; -} - -DOMString XmppEvent::getGroup() const -{ - return group; -} - -void XmppEvent::setGroup(const DOMString &val) -{ - group = val; -} - -DOMString XmppEvent::getData() const -{ - return data; -} - -void XmppEvent::setData(const DOMString &val) -{ - data = val; -} - -DOMString XmppEvent::getFileName() const -{ - return fileName; -} - -void XmppEvent::setFileName(const DOMString &val) -{ - fileName = val; -} - -DOMString XmppEvent::getFileDesc() const -{ - return fileDesc; -} - -void XmppEvent::setFileDesc(const DOMString &val) -{ - fileDesc = val; -} - -long XmppEvent::getFileSize() const -{ - return fileSize; -} - -void XmppEvent::setFileSize(long val) -{ - fileSize = val; -} - -DOMString XmppEvent::getFileHash() const -{ - return fileHash; -} - -void XmppEvent::setFileHash(const DOMString &val) -{ - fileHash = val; -} - -Element *XmppEvent::getDOM() const -{ - return dom; -} - -void XmppEvent::setDOM(const Element *val) -{ - if (!val) - dom = NULL; - else - dom = ((Element *)val)->clone(); -} - - -std::vector<XmppUser> XmppEvent::getUserList() const -{ - return userList; -} - -void XmppEvent::setUserList(const std::vector<XmppUser> &val) -{ - userList = val; -} - - - - - - - - - -//######################################################################## -//######################################################################## -//# X M P P E V E N T T A R G E T -//######################################################################## -//######################################################################## - - -//########################### -//# CONSTRUCTORS -//########################### - -XmppEventTarget::XmppEventTarget() -{ - eventQueueEnabled = false; -} - - -XmppEventTarget::XmppEventTarget(const XmppEventTarget &other) -{ - listeners = other.listeners; - eventQueueEnabled = other.eventQueueEnabled; -} - -XmppEventTarget::~XmppEventTarget() -{ -} - - -//########################### -//# M E S S A G E S -//########################### - -/** - * Print a printf()-like formatted error message - */ -void XmppEventTarget::error(const char *fmt, ...) -{ - va_list args; - va_start(args,fmt); - gchar * buffer = g_strdup_vprintf(fmt, args); - va_end(args) ; - fprintf(stderr, "Error:%s\n", buffer); - XmppEvent evt(XmppEvent::EVENT_ERROR); - evt.setData(buffer); - dispatchXmppEvent(evt); - g_free(buffer); -} - - - -/** - * Print a printf()-like formatted trace message - */ -void XmppEventTarget::status(const char *fmt, ...) -{ - va_list args; - va_start(args,fmt); - gchar * buffer = g_strdup_vprintf(fmt, args); - va_end(args) ; - //printf("Status:%s\n", buffer); - XmppEvent evt(XmppEvent::EVENT_STATUS); - evt.setData(buffer); - dispatchXmppEvent(evt); - g_free(buffer); -} - - - -//########################### -//# L I S T E N E R S -//########################### - -void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event) -{ - std::vector<XmppEventListener *>::iterator iter; - for (iter = listeners.begin(); iter != listeners.end() ; iter++) - (*iter)->processXmppEvent(event); - if (eventQueueEnabled) - eventQueue.push_back(event); -} - -void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener) -{ - XmppEventListener *lsnr = (XmppEventListener *)&listener; - std::vector<XmppEventListener *>::iterator iter; - for (iter = listeners.begin(); iter != listeners.end() ; iter++) - if (*iter == lsnr) - return; - listeners.push_back(lsnr); -} - -void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener) -{ - XmppEventListener *lsnr = (XmppEventListener *)&listener; - std::vector<XmppEventListener *>::iterator iter; - for (iter = listeners.begin(); iter != listeners.end() ; iter++) - if (*iter == lsnr) - listeners.erase(iter); -} - -void XmppEventTarget::clearXmppEventListeners() -{ - listeners.clear(); -} - - -//########################### -//# E V E N T Q U E U E -//########################### - -void XmppEventTarget::eventQueueEnable(bool val) -{ - eventQueueEnabled = val; - if (!eventQueueEnabled) - eventQueue.clear(); -} - -int XmppEventTarget::eventQueueAvailable() -{ - return eventQueue.size(); -} - -XmppEvent XmppEventTarget::eventQueuePop() -{ - if (!eventQueueEnabled || eventQueue.size()<1) - { - XmppEvent dummy(XmppEvent::EVENT_NONE); - return dummy; - } - XmppEvent event = *(eventQueue.begin()); - eventQueue.erase(eventQueue.begin()); - return event; -} - - - - - -//######################################################################## -//######################################################################## -//# X M P P S T R E A M -//######################################################################## -//######################################################################## - - -/** - * - */ -class XmppStream -{ -public: - - /** - * - */ - XmppStream() - { reset(); } - - /** - * - */ - XmppStream(const XmppStream &other) - { assign(other); } - - /** - * - */ - XmppStream &operator=(const XmppStream &other) - { assign(other); return *this; } - - /** - * - */ - virtual ~XmppStream() - {} - - /** - * - */ - virtual void reset() - { - state = XmppClient::STREAM_AVAILABLE; - seqNr = 0; - messageId = ""; - sourceId = ""; - data.clear(); - } - - /** - * - */ - virtual int getState() - { return state; } - - /** - * - */ - virtual void setState(int val) - { state = val; } - - /** - * - */ - virtual DOMString getStreamId() - { return streamId; } - - /** - * - */ - void setStreamId(const DOMString &val) - { streamId = val; } - - /** - * - */ - virtual DOMString getMessageId() - { return messageId; } - - /** - * - */ - void setMessageId(const DOMString &val) - { messageId = val; } - - /** - * - */ - virtual int getSeqNr() - { - seqNr++; - if (seqNr >= 65535) - seqNr = 0; - return seqNr; - } - - /** - * - */ - virtual DOMString getPeerId() - { return sourceId; } - - /** - * - */ - virtual void setPeerId(const DOMString &val) - { sourceId = val; } - - /** - * - */ - int available() - { return data.size(); } - - /** - * - */ - void receiveData(std::vector<unsigned char> &newData) - { - std::vector<unsigned char>::iterator iter; - for (iter=newData.begin() ; iter!=newData.end() ; iter++) - data.push_back(*iter); - } - - /** - * - */ - std::vector<unsigned char> read() - { - if (state != XmppClient::STREAM_OPEN) - { - std::vector<unsigned char>dummy; - return dummy; - } - std::vector<unsigned char> ret = data; - data.clear(); - return ret; - } - -private: - - void assign(const XmppStream &other) - { - streamId = other.streamId; - messageId = other.messageId; - sourceId = other.sourceId; - state = other.state; - seqNr = other.seqNr; - data = other.data; - } - - - DOMString streamId; - - DOMString messageId; - - DOMString sourceId; - - int state; - - long seqNr; - - std::vector<unsigned char> data; -}; - - - - - - - - - - -//######################################################################## -//######################################################################## -//# X M P P C L I E N T -//######################################################################## -//######################################################################## - -class ReceiverThread : public Runnable -{ -public: - - ReceiverThread(XmppClient &par) : client(par) {} - - virtual ~ReceiverThread() {} - - void run() - { client.receiveAndProcessLoop(); } - -private: - - XmppClient &client; -}; - - - - - -//######################################################################## -//# CONSTRUCTORS -//######################################################################## - -XmppClient::XmppClient() -{ - init(); -} - - -XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other) -{ - init(); - assign(other); -} - -void XmppClient::assign(const XmppClient &other) -{ - msgId = other.msgId; - host = other.host; - realm = other.realm; - port = other.port; - username = other.username; - password = other.password; - resource = other.resource; - connected = other.connected; - doRegister = other.doRegister; - groupChats = other.groupChats; - streamPacket = other.streamPacket; -} - - -void XmppClient::init() -{ - sock = new TcpSocket(); - msgId = 0; - connected = false; - doRegister = false; - streamPacket = "message"; - -} - -XmppClient::~XmppClient() -{ - disconnect(); - delete sock; - std::map<DOMString, XmppStream *>::iterator iter; - for (iter = outputStreams.begin(); iter!=outputStreams.end() ; iter++) - delete iter->second; - for (iter = inputStreams.begin(); iter!=inputStreams.end() ; iter++) - delete iter->second; - for (iter = fileSends.begin(); iter!=fileSends.end() ; iter++) - delete iter->second; - groupChatsClear(); -} - - - - - - -//######################################################################## -//# UTILILY -//######################################################################## - -/** - * - */ -bool XmppClient::pause(unsigned long millis) -{ - Thread::sleep(millis); - return true; -} - - -static int strIndex(const DOMString &str, const char *key) -{ - unsigned int p = str.find(key); - if (p == str.npos) - return -1; - return p; -} - - -DOMString XmppClient::toXml(const DOMString &str) -{ - return Parser::encode(str); -} - - - -static DOMString trim(const DOMString &str) -{ - unsigned int i; - for (i=0 ; i<str.size() ; i++) - if (!isspace(str[i])) - break; - int start = i; - for (i=str.size() ; i>0 ; i--) - if (!isspace(str[i-1])) - break; - int end = i; - if (start>=end) - return ""; - return str.substr(start, end); -} - - - - - -//######################################################################## -//# VARIABLES (ones that need special handling) -//######################################################################## - -/** - * - */ -DOMString XmppClient::getUsername() -{ - return username; -} - -/** - * - */ -void XmppClient::setUsername(const DOMString &val) -{ - int p = strIndex(val, "@"); - if (p > 0) - { - username = val.substr(0, p); - realm = val.substr(p+1, jid.size()-p-1); - } - else - { - realm = host; - username = val; - } -} - - - - - - - - -//######################################################################## -//# RECEIVING -//######################################################################## - - -DOMString XmppClient::readStanza() -{ - - int openCount = 0; - bool inTag = false; - bool slashSeen = false; - bool trivialTag = false; - bool querySeen = false; - bool inQuote = false; - bool textSeen = false; - DOMString buf; - - - time_t timeout = time((time_t *)0) + 180; - - while (true) - { - int ch = sock->read(); - //printf("%c", ch); fflush(stdout); - if (ch<0) - { - if (ch == -2) //a simple timeout, not an error - { - //Since we are timed out, let's assume that we - //are between chunks of text. Let's reset all states. - //printf("-----#### Timeout\n"); - time_t currentTime = time((time_t *)0); - if (currentTime > timeout) - { - timeout = currentTime + 180; - if (!write("\n")) - { - error("ping send error"); - disconnect(); - return ""; - } - } - continue; - } - else - { - keepGoing = false; - if (!sock->isConnected()) - { - disconnect(); - return ""; - } - else - { - error("socket read error: %s", sock->getLastError().c_str()); - disconnect(); - return ""; - } - } - } - buf.push_back(ch); - if (ch == '<') - { - inTag = true; - slashSeen = false; - querySeen = false; - inQuote = false; - textSeen = false; - trivialTag = false; - } - else if (ch == '>') - { - if (!inTag) //unescaped '>' in pcdata? horror - continue; - inTag = false; - if (!trivialTag && !querySeen) - { - if (slashSeen) - openCount--; - else - openCount++; - } - //printf("# openCount:%d t:%d q:%d\n", - // openCount, trivialTag, querySeen); - //check if we are 'balanced', but not a <?version?> tag - if (openCount <= 0 && !querySeen) - { - break; - } - //we know that this one will be open-ended - if (strIndex(buf, "<stream:stream") >= 0) - { - buf.append("</stream:stream>"); - break; - } - } - else if (ch == '/') - { - if (inTag && !inQuote) - { - slashSeen = true; - if (textSeen) // <tagName/> <--looks like this - trivialTag = true; - } - } - else if (ch == '?') - { - if (inTag && !inQuote) - querySeen = true; - } - else if (ch == '"' || ch == '\'') - { - if (inTag) - inQuote = !inQuote; - } - else - { - if (inTag && !inQuote && !isspace(ch)) - textSeen = true; - } - } - return buf; -} - - - -static bool isGroupChat(Element *root) -{ - if (!root) - return false; - ElementList elems = root->findElements("x"); - for (unsigned int i=0 ; i<elems.size() ; i++) - { - DOMString xmlns = elems[i]->getAttribute("xmlns"); - //printf("### XMLNS ### %s\n", xmlns.c_str()); - if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 ) - return true; - } - return false; -} - - - - -static bool parseJid(const DOMString &fullJid, - DOMString &jid, DOMString &resource) -{ - DOMString str = fullJid; - jid.clear(); - resource.clear(); - unsigned int p = str.size(); - unsigned int p2 = str.rfind('/', p); - if (p2 != str.npos) - { - resource = str.substr(p2+1, p-(p2+1)); - str = str.substr(0, p); - p = p2; - } - jid = str.substr(0, p); - printf("fullJid:%s jid:%s rsrc:%s\n", - fullJid.c_str(), jid.c_str(), resource.c_str()); - return true; -} - - - - -bool XmppClient::processMessage(Element *root) -{ - DOMString from = root->getTagAttribute("message", "from"); - DOMString to = root->getTagAttribute("message", "to"); - DOMString type = root->getTagAttribute("message", "type"); - - //####Check for embedded namespaces here - //### FILE TRANSFERS - if (processFileMessage(root)) - return true; - - //### STREAMS - if (processInBandByteStreamMessage(root)) - return true; - - - //#### NORMAL MESSAGES - DOMString subject = root->getTagValue("subject"); - DOMString body = root->getTagValue("body"); - DOMString thread = root->getTagValue("thread"); - //##rfc 3921, para 2.4. ignore if no recognizable info - //if (subject.size() < 1 && thread.size()<1) - // return true; - - if (type == "groupchat") - { - DOMString fromGid; - DOMString fromNick; - parseJid(from, fromGid, fromNick); - //printf("fromGid:%s fromNick:%s\n", - // fromGid.c_str(), fromNick.c_str()); - DOMString toGid; - DOMString toNick; - parseJid(to, toGid, toNick); - //printf("toGid:%s toNick:%s\n", - // toGid.c_str(), toNick.c_str()); - - if (fromNick.size() > 0)//normal group message - { - XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE); - event.setGroup(fromGid); - event.setFrom(fromNick); - event.setData(body); - event.setDOM(root); - dispatchXmppEvent(event); - } - else // from the server itself - { - //note the space before, so it doesnt match 'unlocked' - if (strIndex(body, " locked") >= 0) - { - printf("LOCKED!! ;)\n"); - const char *fmt = - "<iq id='create%d' to='%s' type='set'>" - "<query xmlns='http://jabber.org/protocol/muc#owner'>" - "<x xmlns='jabber:x:data' type='submit'/>" - "</query></iq>\n"; - if (!write(fmt, msgId++, fromGid.c_str())) - return false; - } - } - } - else - { - XmppEvent event(XmppEvent::EVENT_MESSAGE); - event.setFrom(from); - event.setData(body); - event.setDOM(root); - dispatchXmppEvent(event); - } - - return true; -} - - - - -bool XmppClient::processPresence(Element *root) -{ - - DOMString fullJid = root->getTagAttribute("presence", "from"); - DOMString to = root->getTagAttribute("presence", "to"); - DOMString presenceStr = root->getTagAttribute("presence", "type"); - bool presence = true; - if (presenceStr == "unavailable") - presence = false; - DOMString status = root->getTagValue("status"); - DOMString show = root->getTagValue("show"); - - if (isGroupChat(root)) - { - DOMString fromGid; - DOMString fromNick; - parseJid(fullJid, fromGid, fromNick); - //printf("fromGid:%s fromNick:%s\n", - // fromGid.c_str(), fromNick.c_str()); - DOMString item_jid = root->getTagAttribute("item", "jid"); - if (item_jid == jid || item_jid == to) //Me - { - if (presence) - { - groupChatCreate(fromGid); - groupChatUserAdd(fromGid, fromNick, ""); - groupChatUserShow(fromGid, fromNick, "available"); - - XmppEvent event(XmppEvent::EVENT_MUC_JOIN); - event.setGroup(fromGid); - event.setFrom(fromNick); - event.setPresence(presence); - event.setShow(show); - event.setStatus(status); - dispatchXmppEvent(event); - } - else - { - groupChatDelete(fromGid); - groupChatUserDelete(fromGid, fromNick); - - XmppEvent event(XmppEvent::EVENT_MUC_LEAVE); - event.setGroup(fromGid); - event.setFrom(fromNick); - event.setPresence(presence); - event.setShow(show); - event.setStatus(status); - dispatchXmppEvent(event); - } - } - else // someone else - { - if (presence) - { - groupChatUserAdd(fromGid, fromNick, ""); - } - else - groupChatUserDelete(fromGid, fromNick); - groupChatUserShow(fromGid, fromNick, show); - XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE); - event.setGroup(fromGid); - event.setFrom(fromNick); - event.setPresence(presence); - event.setShow(show); - event.setStatus(status); - dispatchXmppEvent(event); - } - } - else - { - DOMString shortJid; - DOMString dummy; - parseJid(fullJid, shortJid, dummy); - rosterShow(shortJid, show); //users in roster do not have resource - - XmppEvent event(XmppEvent::EVENT_PRESENCE); - event.setFrom(fullJid); - event.setPresence(presence); - event.setShow(show); - event.setStatus(status); - dispatchXmppEvent(event); - } - - return true; -} - - - -bool XmppClient::processIq(Element *root) -{ - DOMString from = root->getTagAttribute("iq", "from"); - DOMString id = root->getTagAttribute("iq", "id"); - DOMString type = root->getTagAttribute("iq", "type"); - DOMString xmlns = root->getTagAttribute("query", "xmlns"); - - if (id.size()<1) - return true; - - //Group chat - if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 ) - { - printf("results of MUC query\n"); - } - //printf("###IQ xmlns:%s\n", xmlns.c_str()); - - //### FILE TRANSFERS - if (processFileMessage(root)) - return true; - - //### STREAMS - if (processInBandByteStreamMessage(root)) - return true; - - - //###Normal Roster stuff - if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster") - { - roster.clear(); - ElementList elems = root->findElements("item"); - for (unsigned int i=0 ; i<elems.size() ; i++) - { - Element *item = elems[i]; - DOMString userJid = item->getAttribute("jid"); - DOMString name = item->getAttribute("name"); - DOMString subscription = item->getAttribute("subscription"); - DOMString group = item->getTagValue("group"); - //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(), - // subscription.c_str(), group.c_str()); - XmppUser user(userJid, name, subscription, group); - roster.push_back(user); - } - XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER); - dispatchXmppEvent(event); - } - - else if (id.find("regnew") != id.npos) - { - - } - - else if (id.find("regpass") != id.npos) - { - ElementList list = root->findElements("error"); - if (list.size()==0) - { - XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS); - evt.setTo(username); - evt.setFrom(host); - dispatchXmppEvent(evt); - return true; - } - - Element *errElem = list[0]; - DOMString errMsg = "Password change error: "; - if (errElem->findElements("bad-request").size()>0) - { - errMsg.append("password change does not contain complete information"); - } - else if (errElem->findElements("not-authorized").size()>0) - { - errMsg.append("server does not consider the channel safe " - "enough to enable a password change"); - } - else if (errElem->findElements("not-allowed").size()>0) - { - errMsg.append("server does not allow password changes"); - } - else if (errElem->findElements("unexpected-request").size()>0) - { - errMsg.append( - "IQ set does not contain a 'from' address because " - "the entity is not registered with the server"); - } - error("%s", errMsg.c_str()); - } - - else if (id.find("regcancel") != id.npos) - { - ElementList list = root->findElements("error"); - if (list.size()==0) - { - XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL); - evt.setTo(username); - evt.setFrom(host); - dispatchXmppEvent(evt); - return true; - } - - Element *errElem = list[0]; - DOMString errMsg = "Registration cancel error: "; - if (errElem->findElements("bad-request").size()>0) - { - errMsg.append("The <remove/> element was not the only child element of the <query/> element."); - } - else if (errElem->findElements("forbidden").size()>0) - { - errMsg.append("sender does not have sufficient permissions to cancel the registration"); - } - else if (errElem->findElements("not-allowed").size()>0) - { - errMsg.append("not allowed to cancel registrations in-band"); - } - else if (errElem->findElements("registration-required").size()>0) - { - errMsg.append("not previously registered"); - } - else if (errElem->findElements("unexpected-request").size()>0) - { - errMsg.append( - "IQ set does not contain a 'from' address because " - "the entity is not registered with the server"); - } - error("%s", errMsg.c_str()); - } - - return true; -} - - - - - -bool XmppClient::receiveAndProcess() -{ - if (!keepGoing) - return false; - - Parser parser; - - DOMString recvBuf = readStanza(); - recvBuf = trim(recvBuf); - if (recvBuf.size() < 1) - return true; - - //Ugly hack. Apparently the first char can be dropped on timeouts - //if (recvBuf[0] != '<') - // recvBuf.insert(0, "<"); - - status("RECV: %s", recvBuf.c_str()); - Element *root = parser.parse(recvBuf); - if (!root) - { - printf("Bad elem\n"); - return true; - } - - //#### MESSAGE - ElementList elems = root->findElements("message"); - if (elems.size()>0) - { - if (!processMessage(root)) - return false; - } - - //#### PRESENCE - elems = root->findElements("presence"); - if (elems.size()>0) - { - if (!processPresence(root)) - return false; - } - - //#### INFO - elems = root->findElements("iq"); - if (elems.size()>0) - { - if (!processIq(root)) - return false; - } - - delete root; - - return true; -} - - -bool XmppClient::receiveAndProcessLoop() -{ - keepGoing = true; - while (true) - { - if (!keepGoing) - { - status("Abort requested"); - break; - } - if (!receiveAndProcess()) - return false; - } - return true; -} - - - - -//######################################################################## -//# SENDING -//######################################################################## - - -bool XmppClient::write(const char *fmt, ...) -{ - bool rc = true; - va_list args; - va_start(args,fmt); - gchar * buffer = g_strdup_vprintf(fmt,args); - va_end(args) ; - status("SEND: %s", buffer); - if (!sock->write(buffer)) - { - error("Cannot write to socket: %s", sock->getLastError().c_str()); - rc = false; - } - g_free(buffer); - return rc; -} - - - - - - -//######################################################################## -//# R E G I S T R A T I O N -//######################################################################## - -/** - * Perform JEP-077 In-Band Registration. Performed synchronously after SSL, - * before authentication - */ -bool XmppClient::inBandRegistrationNew() -{ - Parser parser; - - const char *fmt = - "<iq type='get' id='regnew%d'>" - "<query xmlns='jabber:iq:register'/>" - "</iq>\n\n"; - if (!write(fmt, msgId++)) - return false; - - DOMString recbuf = readStanza(); - status("RECV reg: %s", recbuf.c_str()); - Element *elem = parser.parse(recbuf); - //elem->print(); - - //# does the entity send the newer "instructions" tag? - ElementList fields = elem->findElements("field"); - std::vector<DOMString> fnames; - for (unsigned int i=0; i<fields.size() ; i++) - { - DOMString fname = fields[i]->getAttribute("var"); - if (fname == "FORM_TYPE") - continue; - fnames.push_back(fname); - status("field name:%s", fname.c_str()); - } - - //Do we have any fields? - if (fnames.size() == 0) - { - //If no fields, maybe the older method was offered - if (elem->findElements("username").size() == 0 || - elem->findElements("password").size() == 0) - { - error("server did not offer registration"); - delete elem; - return false; - } - } - - delete elem; - - fmt = - "<iq type='set' id='regnew%d'>" - "<query xmlns='jabber:iq:register'>" - "<username>%s</username>" - "<password>%s</password>" - "<email/><name/>" - "</query>" - "</iq>\n\n"; - if (!write(fmt, msgId++, toXml(username).c_str(), - toXml(password).c_str() )) - return false; - - - recbuf = readStanza(); - status("RECV reg: %s", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - - ElementList list = elem->findElements("error"); - if (list.size()>0) - { - Element *errElem = list[0]; - DOMString code = errElem->getAttribute("code"); - DOMString errMsg = "Registration error: "; - if (code == "409") - { - errMsg.append("conflict with existing user name"); - } - else if (code == "406") - { - errMsg.append("some registration information was not provided"); - } - error("%s", errMsg.c_str()); - delete elem; - return false; - } - - delete elem; - - XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW); - evt.setTo(username); - evt.setFrom(host); - dispatchXmppEvent(evt); - - return true; -} - - -/** - * Perform JEP-077 In-Band Registration. Performed asynchronously, after login. - * See processIq() for response handling. - */ -bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword) -{ - Parser parser; - - //# Let's try it form-style to allow the common old/new password thing - const char *fmt = - "<iq type='set' id='regpass%d' to='%s'>" - " <query xmlns='jabber:iq:register'>" - " <x xmlns='jabber:x:data' type='form'>" - " <field type='hidden' var='FORM_TYPE'>" - " <value>jabber:iq:register:changepassword</value>" - " </field>" - " <field type='text-single' var='username'>" - " <value>%s</value>" - " </field>" - " <field type='text-private' var='old_password'>" - " <value>%s</value>" - " </field>" - " <field type='text-private' var='password'>" - " <value>%s</value>" - " </field>" - " </x>" - " </query>" - "</iq>\n\n"; - - if (!write(fmt, msgId++, host.c_str(), - username.c_str(), password.c_str(), newpassword.c_str())) - return false; - - return true; - -} - - -/** - * Perform JEP-077 In-Band Registration. Performed asynchronously, after login. - * See processIq() for response handling. - */ -bool XmppClient::inBandRegistrationCancel() -{ - Parser parser; - - const char *fmt = - "<iq type='set' id='regcancel%d'>" - "<query xmlns='jabber:iq:register'><remove/></query>" - "</iq>\n\n"; - if (!write(fmt, msgId++)) - return false; - - return true; -} - - - - - -//######################################################################## -//# A U T H E N T I C A T E -//######################################################################## - - - -bool XmppClient::iqAuthenticate(const DOMString &streamId) -{ - Parser parser; - - const char *fmt = - "<iq type='get' to='%s' id='auth%d'>" - "<query xmlns='jabber:iq:auth'><username>%s</username></query>" - "</iq>\n"; - if (!write(fmt, realm.c_str(), msgId++, username.c_str())) - return false; - - DOMString recbuf = readStanza(); - status("iq auth recv: '%s'\n", recbuf.c_str()); - Element *elem = parser.parse(recbuf); - //elem->print(); - DOMString iqType = elem->getTagAttribute("iq", "type"); - //printf("##iqType:%s\n", iqType.c_str()); - delete elem; - - if (iqType != "result") - { - error("error:server does not allow login"); - return false; - } - - bool digest = true; - if (digest) - { - //## Digest authentication - DOMString digest = streamId; - digest.append(password); - digest = Sha1::hashHex(digest); - //printf("digest:%s\n", digest.c_str()); - fmt = - "<iq type='set' id='auth%d'>" - "<query xmlns='jabber:iq:auth'>" - "<username>%s</username>" - "<digest>%s</digest>" - "<resource>%s</resource>" - "</query>" - "</iq>\n"; - if (!write(fmt, msgId++, username.c_str(), - digest.c_str(), resource.c_str())) - return false; - } - else - { - - //## Plaintext authentication - fmt = - "<iq type='set' id='auth%d'>" - "<query xmlns='jabber:iq:auth'>" - "<username>%s</username>" - "<password>%s</password>" - "<resource>%s</resource>" - "</query>" - "</iq>\n"; - if (!write(fmt, msgId++, username.c_str(), - password.c_str(), resource.c_str())) - return false; - } - - recbuf = readStanza(); - status("iq auth recv: '%s'\n", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - iqType = elem->getTagAttribute("iq", "type"); - //printf("##iqType:%s\n", iqType.c_str()); - delete elem; - - if (iqType != "result") - { - error("server does not allow login"); - return false; - } - - return true; -} - - -/** - * Parse a sasl challenge to retrieve all of its key=value pairs - */ -static bool saslParse(const DOMString &s, - std::map<DOMString, DOMString> &vals) -{ - - vals.clear(); - - int p = 0; - int siz = s.size(); - - while (p < siz) - { - DOMString key; - DOMString value; - char ch = '\0'; - - //# Parse key - while (p<siz) - { - ch = s[p++]; - if (ch == '=') - break; - key.push_back(ch); - } - - //No value? - if (ch != '=') - break; - - //# Parse value - bool quoted = false; - while (p<siz) - { - ch = s[p++]; - if (ch == '"') - quoted = !quoted; - else if (ch == ',' && !quoted) - break; - else - value.push_back(ch); - } - - //printf("# Key: '%s' Value: '%s'\n", key.c_str(), value.c_str()); - vals[key] = value; - if (ch != ',') - break; - } - - return true; -} - - - -/** - * Attempt suthentication using the MD5 SASL mechanism - */ -bool XmppClient::saslMd5Authenticate() -{ - Parser parser; - const char *fmt = - "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' " - "mechanism='DIGEST-MD5'/>\n"; - if (!write("%s",fmt)) - return false; - - DOMString recbuf = readStanza(); - status("challenge received: '%s'", recbuf.c_str()); - Element *elem = parser.parse(recbuf); - //elem->print(); - DOMString b64challenge = elem->getTagValue("challenge"); - delete elem; - - if (b64challenge.size() < 1) - { - error("login: no SASL challenge offered by server"); - return false; - } - DOMString challenge = Base64Decoder::decodeToString(b64challenge); - status("md5 challenge:'%s'", challenge.c_str()); - - std::map<DOMString, DOMString> attrs; - if (!saslParse(challenge, attrs)) - { - error("login: error parsing SASL challenge"); - return false; - } - - DOMString nonce = attrs["nonce"]; - if (nonce.size()==0) - { - error("login: no SASL nonce sent by server"); - return false; - } - - DOMString realm = attrs["realm"]; - if (realm.size()==0) - { - //Apparently this is not a problem - //error("login: no SASL realm sent by server"); - //return false; - } - - status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str()); - - char idBuf[14]; - snprintf(idBuf, 13, "%dsasl", msgId++); - DOMString cnonceStr = idBuf; - DOMString cnonce = Sha1::hashHex(cnonceStr); - DOMString authzid = username; authzid.append("@"); authzid.append(host); - DOMString digest_uri = "xmpp/"; digest_uri.append(host); - - //## Make A1 - Md5 md5; - md5.append(username); - md5.append(":"); - md5.append(realm); - md5.append(":"); - md5.append(password); - unsigned char a1tmp[16]; - md5.finish(a1tmp); - md5.init(); - md5.append(a1tmp, 16); - md5.append(":"); - md5.append(nonce); - md5.append(":"); - md5.append(cnonce); - //RFC2831 says authzid is optional. Wildfire has trouble with authzid's - //md5.append(":"); - //md5.append(authzid); - md5.append(""); - DOMString a1 = md5.finishHex(); - status("##a1:'%s'", a1.c_str()); - - //# Make A2 - md5.init(); - md5.append("AUTHENTICATE:"); - md5.append(digest_uri); - DOMString a2 = md5.finishHex(); - status("##a2:'%s'", a2.c_str()); - - //# Now make the response - md5.init(); - md5.append(a1); - md5.append(":"); - md5.append(nonce); - md5.append(":"); - md5.append("00000001");//nc - md5.append(":"); - md5.append(cnonce); - md5.append(":"); - md5.append("auth");//qop - md5.append(":"); - md5.append(a2); - DOMString response = md5.finishHex(); - - DOMString resp; - resp.append("username=\""); resp.append(username); resp.append("\","); - resp.append("realm=\""); resp.append(realm); resp.append("\","); - resp.append("nonce=\""); resp.append(nonce); resp.append("\","); - resp.append("cnonce=\""); resp.append(cnonce); resp.append("\","); - resp.append("nc=00000001,qop=auth,"); - resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," ); - //resp.append("authzid=\""); resp.append(authzid); resp.append("\","); - resp.append("response="); resp.append(response); resp.append(","); - resp.append("charset=utf-8"); - status("sending response:'%s'", resp.c_str()); - resp = Base64Encoder::encode(resp); - status("base64 response:'%s'", resp.c_str()); - fmt = - "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n"; - if (!write(fmt, resp.c_str())) - return false; - - recbuf = readStanza(); - status("server says: '%s'", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - //# Success or failure already? - if (elem->findElements("success").size() > 0) - { - delete elem; - return true; - } - else - { - ElementList list = elem->findElements("failure"); - if (list.size() > 0) - { - DOMString errmsg = ""; - Element *errmsgElem = list[0]->getFirstChild(); - if (errmsgElem) - errmsg = errmsgElem->getName(); - error("login: initial md5 authentication failed: %s", errmsg.c_str()); - delete elem; - return false; - } - } - //# Continue for one more SASL cycle - b64challenge = elem->getTagValue("challenge"); - delete elem; - - if (b64challenge.size() < 1) - { - error("login: no second SASL challenge offered by server"); - return false; - } - - challenge = Base64Decoder::decodeToString(b64challenge); - status("md5 challenge: '%s'", challenge.c_str()); - - if (!saslParse(challenge, attrs)) - { - error("login: error parsing SASL challenge"); - return false; - } - - DOMString rspauth = attrs["rspauth"]; - if (rspauth.size()==0) - { - error("login: no SASL respauth sent by server\n"); - return false; - } - - fmt = - "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n"; - if (!write("%s",fmt)) - return false; - - recbuf = readStanza(); - status("SASL recv: '%s", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - b64challenge = elem->getTagValue("challenge"); - bool success = (elem->findElements("success").size() > 0); - delete elem; - - return success; -} - - - -/** - * Attempt to authentication using the SASL PLAIN mechanism. This - * is used most commonly my Google Talk. - */ -bool XmppClient::saslPlainAuthenticate() -{ - Parser parser; - - DOMString id = username; - //id.append("@"); - //id.append(host); - Base64Encoder encoder; - encoder.append('\0'); - encoder.append(id); - encoder.append('\0'); - encoder.append(password); - DOMString base64Auth = encoder.finish(); - //printf("authbuf:%s\n", base64Auth.c_str()); - - const char *fmt = - "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' " - "mechanism='PLAIN'>%s</auth>\n"; - if (!write(fmt, base64Auth.c_str())) - return false; - DOMString recbuf = readStanza(); - status("challenge received: '%s'", recbuf.c_str()); - Element *elem = parser.parse(recbuf); - - bool success = (elem->findElements("success").size() > 0); - delete elem; - - return success; -} - - - -/** - * Handshake with SASL, and use one of its offered mechanisms to - * authenticate. - * @param streamId used for iq auth fallback is SASL not supported - */ -bool XmppClient::saslAuthenticate(const DOMString &streamId) -{ - Parser parser; - - DOMString recbuf = readStanza(); - status("RECV: '%s'\n", recbuf.c_str()); - Element *elem = parser.parse(recbuf); - //elem->print(); - - //Check for starttls - bool wantStartTls = false; - if (elem->findElements("starttls").size() > 0) - { - wantStartTls = true; - if (elem->findElements("required").size() > 0) - status("login: STARTTLS required"); - else - status("login: STARTTLS available"); - } - - //# do we want TLS, are we not already running SSL, and can - //# the client actually do an ssl connection? - if (wantStartTls && !sock->getEnableSSL() && sock->getHaveSSL()) - { - delete elem; - const char *fmt = - "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n"; - if (!write("%s",fmt)) - return false; - recbuf = readStanza(); - status("RECV: '%s'\n", recbuf.c_str()); - elem = parser.parse(recbuf); - if (elem->getTagAttribute("proceed", "xmlns").size()<1) - { - error("Server rejected TLS negotiation"); - disconnect(); - return false; - } - delete elem; - if (!sock->startTls()) - { - DOMString tcperr = sock->getLastError(); - error("Could not start TLS: %s", tcperr.c_str()); - disconnect(); - return false; - } - - fmt = - "<stream:stream xmlns='jabber:client' " - "xmlns:stream='http://etherx.jabber.org/streams' " - "to='%s' version='1.0'>\n\n"; - if (!write(fmt, realm.c_str())) - return false; - - recbuf = readStanza(); - status("RECVx: '%s'", recbuf.c_str()); - recbuf.append("</stream:stream>"); - elem = parser.parse(recbuf); - bool success = - (elem->getTagAttribute("stream:stream", "id").size()>0); - if (!success) - { - error("STARTTLS negotiation failed"); - disconnect(); - return false; - } - delete elem; - recbuf = readStanza(); - status("RECV: '%s'\n", recbuf.c_str()); - elem = parser.parse(recbuf); - - XmppEvent event(XmppEvent::EVENT_SSL_STARTED); - dispatchXmppEvent(event); - } - - //register, if user requests - if (doRegister) - { - if (!inBandRegistrationNew()) - return false; - } - - //check for sasl authentication mechanisms - ElementList elems = elem->findElements("mechanism"); - if (elems.size() < 1) - { - status("login: no SASL mechanism offered by server"); - //fall back to iq - if (iqAuthenticate(streamId)) - return true; - return false; - } - bool md5Found = false; - bool plainFound = false; - for (unsigned int i=0 ; i<elems.size() ; i++) - { - DOMString mech = elems[i]->getValue(); - if (mech == "DIGEST-MD5") - { - status("MD5 authentication offered"); - md5Found = true; - } - else if (mech == "PLAIN") - { - status("PLAIN authentication offered"); - plainFound = true; - } - } - delete elem; - - bool success = false; - if (md5Found) - { - success = saslMd5Authenticate(); - } - else if (plainFound) - { - success = saslPlainAuthenticate(); - } - else - { - error("not able to handle sasl authentication mechanisms"); - return false; - } - - if (success) - status("###### SASL authentication success\n"); - else - error("###### SASL authentication failure\n"); - - return success; -} - - - - - - -//######################################################################## -//# CONNECT -//######################################################################## - - -/** - * Check if we are connected, and fail with an error if we are not - */ -bool XmppClient::checkConnect() -{ - if (!connected) - { - XmppEvent evt(XmppEvent::EVENT_ERROR); - evt.setData("Attempted operation while disconnected"); - dispatchXmppEvent(evt); - return false; - } - return true; -} - - - -/** - * Create an XMPP session with a server. This - * is basically the transport layer of XMPP. - */ -bool XmppClient::createSession() -{ - - Parser parser; - if (port==443 || port==5223) - sock->enableSSL(true); - if (!sock->connect(host, port)) - { - error("Cannot connect:%s", sock->getLastError().c_str()); - return false; - } - - if (sock->getEnableSSL()) - { - XmppEvent event(XmppEvent::EVENT_SSL_STARTED); - dispatchXmppEvent(event); - } - - const char *fmt = - "<stream:stream " - "to='%s' " - "xmlns='jabber:client' " - "xmlns:stream='http://etherx.jabber.org/streams' " - "version='1.0'>\n\n"; - if (!write(fmt, realm.c_str())) - return false; - - DOMString recbuf = readStanza(); - status("RECV: '%s'\n", recbuf.c_str()); - recbuf.append("</stream:stream>"); - Element *elem = parser.parse(recbuf); - //elem->print(); - bool useSasl = false; - DOMString streamId = elem->getTagAttribute("stream:stream", "id"); - //printf("### StreamID: %s\n", streamId.c_str()); - DOMString streamVersion = elem->getTagAttribute("stream:stream", "version"); - if (streamVersion == "1.0") - useSasl = true; - - if (useSasl) - { - if (!saslAuthenticate(streamId)) - return false; - - fmt = - "<stream:stream " - "to='%s' " - "xmlns='jabber:client' " - "xmlns:stream='http://etherx.jabber.org/streams' " - "version='1.0'>\n\n"; - - if (!write(fmt, realm.c_str())) - return false; - recbuf = readStanza(); - recbuf.append("</stream:stream>\n"); - //printf("now server says:: '%s'\n", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - delete elem; - - recbuf = readStanza(); - //printf("now server says:: '%s'\n", recbuf.c_str()); - elem = parser.parse(recbuf); - bool hasBind = (elem->findElements("bind").size() > 0); - //elem->print(); - delete elem; - - if (!hasBind) - { - error("no binding provided by server"); - return false; - } - - - } - else // not SASL - { - if (!iqAuthenticate(streamId)) - return false; - } - - - //### Resource binding - fmt = - "<iq type='set' id='bind%d'>" - "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>" - "<resource>%s</resource>" - "</bind></iq>\n"; - if (!write(fmt, msgId++, resource.c_str())) - return false; - - recbuf = readStanza(); - status("bind result: '%s'", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - DOMString bindType = elem->getTagAttribute("iq", "type"); - //printf("##bindType:%s\n", bindType.c_str()); - DOMString givenFullJid = elem->getTagValue("jid"); - delete elem; - - if (bindType != "result") - { - error("no binding with server failed"); - return false; - } - - //The server sent us a JID. We need to listen. - if (givenFullJid.size()>0) - { - DOMString givenJid, givenResource; - parseJid(givenFullJid, givenJid, givenResource); - status("given user: %s realm: %s, rsrc: %s", - givenJid.c_str(), realm.c_str(), givenResource.c_str()); - setResource(givenResource); - } - - - fmt = - "<iq type='set' id='sess%d'>" - "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" - "</iq>\n"; - if (!write(fmt, msgId++)) - return false; - - recbuf = readStanza(); - status("session received: '%s'", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - DOMString sessionType = elem->getTagAttribute("iq", "type"); - //printf("##sessionType:%s\n", sessionType.c_str()); - delete elem; - - if (sessionType != "result") - { - error("no session provided by server"); - return false; - } - - //printf("########## COOL #########\n"); - //Now that we are bound, we have a valid JID - jid = username; - jid.append("@"); - jid.append(realm); - jid.append("/"); - jid.append(resource); - - //We are now done with the synchronous handshaking. Let's go into - //async mode - - fmt = - "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n"; - if (!write(fmt, msgId++)) - return false; - - fmt = - "<iq type='get' id='discoItems%d' to='%s'>" - "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n"; - if (!write(fmt, msgId++, realm.c_str())) - return false; - - fmt = - "<iq type='get' id='discoInfo%d' to='conference.%s'>" - "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n"; - if (!write(fmt, msgId++, realm.c_str())) - return false; - - fmt = - "<presence/>\n"; - if (!write("%s",fmt)) - return false; - - /* - recbuf = readStanza(); - status("stream received: '%s'", recbuf.c_str()); - elem = parser.parse(recbuf); - //elem->print(); - delete elem; - */ - - //We are now logged in - status("Connected"); - connected = true; - XmppEvent evt(XmppEvent::EVENT_CONNECTED); - evt.setData(host); - dispatchXmppEvent(evt); - //Thread::sleep(1000000); - - sock->setReceiveTimeout(1000); - ReceiverThread runner(*this); - Thread thread(runner); - thread.start(); - - return true; -} - - - -/** - * Public call to connect - */ -bool XmppClient::connect() -{ - if (!createSession()) - { - disconnect(); - return false; - } - return true; -} - - -/** - * Public call to connect - */ -bool XmppClient::connect(DOMString hostArg, int portArg, - DOMString usernameArg, - DOMString passwordArg, - DOMString resourceArg) -{ - host = hostArg; - port = portArg; - password = passwordArg; - resource = resourceArg; - - //parse this one - setUsername(usernameArg); - - bool ret = connect(); - return ret; -} - - - -/** - * Public call to disconnect - */ -bool XmppClient::disconnect() -{ - if (connected) - { - const char *fmt = - "<presence type='unavailable'/>\n"; - write("%s",fmt); - } - keepGoing = false; - connected = false; - Thread::sleep(2000); //allow receiving thread to quit - sock->disconnect(); - roster.clear(); - groupChatsClear(); - XmppEvent event(XmppEvent::EVENT_DISCONNECTED); - event.setData(host); - dispatchXmppEvent(event); - return true; -} - - - - - -//######################################################################## -//# ROSTER -//######################################################################## - -/** - * Add an XMPP id to your roster - */ -bool XmppClient::rosterAdd(const DOMString &rosterGroup, - const DOMString &otherJid, - const DOMString &name) -{ - if (!checkConnect()) - return false; - const char *fmt = - "<iq type='set' id='roster_%d'>" - "<query xmlns='jabber:iq:roster'>" - "<item jid='%s' name='%s'><group>%s</group></item>" - "</query></iq>\n"; - if (!write(fmt, msgId++, otherJid.c_str(), - name.c_str(), rosterGroup.c_str())) - { - return false; - } - return true; -} - - - -/** - * Delete an XMPP id from your roster. - */ -bool XmppClient::rosterDelete(const DOMString &otherJid) -{ - if (!checkConnect()) - return false; - const char *fmt = - "<iq type='set' id='roster_%d'>" - "<query xmlns='jabber:iq:roster'>" - "<item jid='%s' subscription='remove'><group>%s</group></item>" - "</query></iq>\n"; - if (!write(fmt, msgId++, otherJid.c_str())) - { - return false; - } - return true; -} - - -/** - * Comparison method for sort() call below - */ -static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2) -{ - DOMString s1 = p1.group; - DOMString s2 = p2.group; - for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++) - { - int comp = tolower(s1[len]) - tolower(s2[len]); - if (comp) - return (comp<0); - } - - s1 = p1.jid; - s2 = p2.jid; - for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++) - { - int comp = tolower(s1[len]) - tolower(s2[len]); - if (comp) - return (comp<0); - } - return false; -} - - - -/** - * Sort and return the roster that has just been reported by - * an XmppEvent::EVENT_ROSTER event. - */ -std::vector<XmppUser> XmppClient::getRoster() -{ - std::vector<XmppUser> ros = roster; - std::sort(ros.begin(), ros.end(), xmppRosterCompare); - return ros; -} - - -/** - * - */ -void XmppClient::rosterShow(const DOMString &jid, const DOMString &show) -{ - DOMString theShow = show; - if (theShow == "") - theShow = "available"; - - std::vector<XmppUser>::iterator iter; - for (iter=roster.begin() ; iter != roster.end() ; iter++) - { - if (iter->jid == jid) - iter->show = theShow; - } -} - - - - - - -//######################################################################## -//# CHAT (individual) -//######################################################################## - -/** - * Send a message to an xmpp jid - */ -bool XmppClient::message(const DOMString &user, const DOMString &subj, - const DOMString &msg) -{ - if (!checkConnect()) - return false; - - DOMString xmlSubj = toXml(subj); - DOMString xmlMsg = toXml(msg); - - if (xmlSubj.size() > 0) - { - const char *fmt = - "<message to='%s' from='%s' type='chat'>" - "<subject>%s</subject><body>%s</body></message>\n"; - if (!write(fmt, user.c_str(), jid.c_str(), - xmlSubj.c_str(), xmlMsg.c_str())) - return false; - } - else - { - const char *fmt = - "<message to='%s' from='%s'>" - "<body>%s</body></message>\n"; - if (!write(fmt, user.c_str(), jid.c_str(), xmlMsg.c_str())) - return false; - } - return true; -} - - - -/** - * - */ -bool XmppClient::message(const DOMString &user, const DOMString &msg) -{ - return message(user, "", msg); -} - - - -/** - * - */ -bool XmppClient::presence(const DOMString &presence) -{ - if (!checkConnect()) - return false; - - DOMString xmlPres = toXml(presence); - - const char *fmt = - "<presence><show>%s</show></presence>\n"; - if (!write(fmt, xmlPres.c_str())) - return false; - return true; -} - - - - - - -//######################################################################## -//# GROUP CHAT -//######################################################################## - -/** - * - */ -bool XmppClient::groupChatCreate(const DOMString &groupJid) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - { - if ((*iter)->getGroupJid() == groupJid) - { - //error("Group chat '%s' already exists", groupJid.c_str()); - return false; - } - } - XmppGroupChat *chat = new XmppGroupChat(groupJid); - groupChats.push_back(chat); - return true; -} - - - -/** - * - */ -void XmppClient::groupChatDelete(const DOMString &groupJid) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; ) - { - XmppGroupChat *chat = *iter; - if (chat->getGroupJid() == groupJid) - { - iter = groupChats.erase(iter); - delete chat; - } - else - iter++; - } -} - - - -/** - * - */ -bool XmppClient::groupChatExists(const DOMString &groupJid) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - if ((*iter)->getGroupJid() == groupJid) - return true; - return false; -} - - - -/** - * - */ -void XmppClient::groupChatsClear() -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - delete (*iter); - groupChats.clear(); -} - - - - -/** - * - */ -void XmppClient::groupChatUserAdd(const DOMString &groupJid, - const DOMString &nick, - const DOMString &jid) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - { - if ((*iter)->getGroupJid() == groupJid) - { - (*iter)->userAdd(nick, jid); - } - } -} - - - -/** - * - */ -void XmppClient::groupChatUserShow(const DOMString &groupJid, - const DOMString &nick, - const DOMString &show) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - { - if ((*iter)->getGroupJid() == groupJid) - { - (*iter)->userShow(nick, show); - } - } -} - - - - -/** - * - */ -void XmppClient::groupChatUserDelete(const DOMString &groupJid, - const DOMString &nick) -{ - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - { - if ((*iter)->getGroupJid() == groupJid) - { - (*iter)->userDelete(nick); - } - } -} - - - -/** - * Comparison method for the sort() below - */ -static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2) -{ - DOMString s1 = p1.nick; - DOMString s2 = p2.nick; - int comp = 0; - for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++) - { - comp = tolower(s1[len]) - tolower(s2[len]); - if (comp) - break; - } - return (comp<0); -} - - - -/** - * Return the user list for the named group - */ -std::vector<XmppUser> XmppClient::groupChatGetUserList( - const DOMString &groupJid) -{ - if (!checkConnect()) - { - std::vector<XmppUser> dummy; - return dummy; - } - - std::vector<XmppGroupChat *>::iterator iter; - for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++) - { - if ((*iter)->getGroupJid() == groupJid ) - { - std::vector<XmppUser> uList = (*iter)->getUserList(); - std::sort(uList.begin(), uList.end(), xmppUserCompare); - return uList; - } - } - std::vector<XmppUser> dummy; - return dummy; -} - - - - -/** - * Try to join a group - */ -bool XmppClient::groupChatJoin(const DOMString &groupJid, - const DOMString &nick, - const DOMString &/*pass*/) -{ - if (!checkConnect()) - return false; - - DOMString user = nick; - if (user.size()<1) - user = username; - - const char *fmt = - "<presence to='%s/%s'>" - "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n"; - if (!write(fmt, groupJid.c_str(), user.c_str())) - return false; - return true; -} - - - - -/** - * Leave a group - */ -bool XmppClient::groupChatLeave(const DOMString &groupJid, - const DOMString &nick) -{ - if (!checkConnect()) - return false; - - DOMString user = nick; - if (user.size()<1) - user = username; - - const char *fmt = - "<presence to='%s/%s' type='unavailable'>" - "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n"; - if (!write(fmt, groupJid.c_str(), user.c_str())) - return false; - return true; -} - - - - -/** - * Send a message to a group - */ -bool XmppClient::groupChatMessage(const DOMString &groupJid, - const DOMString &msg) -{ - if (!checkConnect()) - { - return false; - } - - DOMString xmlMsg = toXml(msg); - - const char *fmt = - "<message from='%s' to='%s' type='groupchat'>" - "<body>%s</body></message>\n"; - if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str())) - return false; - /* - const char *fmt = - "<message to='%s' type='groupchat'>" - "<body>%s</body></message>\n"; - if (!write(fmt, groupJid.c_str(), xmlMsg.c_str())) - return false; - */ - return true; -} - - - - -/** - * Send a message to an individual in a group - */ -bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid, - const DOMString &toNick, - const DOMString &msg) -{ - if (!checkConnect()) - return false; - - DOMString xmlMsg = toXml(msg); - - /* - const char *fmt = - "<message from='%s' to='%s/%s' type='chat'>" - "<body>%s</body></message>\n"; - if (!write(fmt, jid.c_str(), groupJid.c_str(), - toNick.c_str(), xmlMsg.c_str())) - return false; - */ - const char *fmt = - "<message to='%s/%s' type='chat'>" - "<body>%s</body></message>\n"; - if (!write(fmt, groupJid.c_str(), - toNick.c_str(), xmlMsg.c_str())) - return false; - return true; -} - - - - -/** - * Change your presence within a group - */ -bool XmppClient::groupChatPresence(const DOMString &groupJid, - const DOMString &myNick, - const DOMString &presence) -{ - if (!checkConnect()) - return false; - - DOMString user = myNick; - if (user.size()<1) - user = username; - - DOMString xmlPresence = toXml(presence); - - const char *fmt = - "<presence to='%s/%s' type='%s'>" - "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n"; - if (!write(fmt, groupJid.c_str(), - user.c_str(), xmlPresence.c_str())) - return true; - return true; -} - - - - - -//######################################################################## -//# S T R E A M S -//######################################################################## - - -bool XmppClient::processInBandByteStreamMessage(Element *root) -{ - DOMString from = root->getAttribute("from"); - DOMString id = root->getAttribute("id"); - DOMString type = root->getAttribute("type"); - - //### Incoming stream requests - //Input streams are id's by stream id - DOMString ibbNamespace = "http://jabber.org/protocol/ibb"; - - if (root->getTagAttribute("open", "xmlns") == ibbNamespace) - { - DOMString streamId = root->getTagAttribute("open", "sid"); - XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT); - dispatchXmppEvent(event); - std::map<DOMString, XmppStream *>::iterator iter = - inputStreams.find(streamId); - if (iter != inputStreams.end()) - { - XmppStream *ins = iter->second; - ins->setState(STREAM_OPENING); - ins->setMessageId(id); - return true; - } - return true; - } - - else if (root->getTagAttribute("close", "xmlns") == ibbNamespace) - { - XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE); - dispatchXmppEvent(event); - DOMString streamId = root->getTagAttribute("close", "sid"); - std::map<DOMString, XmppStream *>::iterator iter = - inputStreams.find(streamId); - if (iter != inputStreams.end()) - { - XmppStream *ins = iter->second; - if (from == ins->getPeerId()) - { - ins->setState(STREAM_CLOSING); - ins->setMessageId(id); - return true; - } - } - return true; - } - - else if (root->getTagAttribute("data", "xmlns") == ibbNamespace) - { - DOMString streamId = root->getTagAttribute("data", "sid"); - std::map<DOMString, XmppStream *>::iterator iter = - inputStreams.find(streamId); - if (iter != inputStreams.end()) - { - XmppStream *ins = iter->second; - if (ins->getState() != STREAM_OPEN) - { - XmppEvent event(XmppEvent::EVENT_ERROR); - event.setFrom(from); - event.setData("received unrequested stream data"); - dispatchXmppEvent(event); - return true; - } - DOMString data = root->getTagValue("data"); - std::vector<unsigned char>binData = - Base64Decoder::decode(data); - ins->receiveData(binData); - } - } - - //### Responses to outgoing requests - //Output streams are id's by message id - std::map<DOMString, XmppStream *>::iterator iter = - outputStreams.find(id); - if (iter != outputStreams.end()) - { - XmppStream *outs = iter->second; - if (type == "error") - { - outs->setState(STREAM_ERROR); - return true; - } - else if (type == "result") - { - if (outs->getState() == STREAM_OPENING) - { - outs->setState(STREAM_OPEN); - } - else if (outs->getState() == STREAM_CLOSING) - { - outs->setState(STREAM_CLOSED); - } - return true; - } - } - - return false; -} - - -/** - * - */ -bool XmppClient::outputStreamOpen(const DOMString &destId, - const DOMString &streamIdArg) -{ - char buf[32]; - snprintf(buf, 31, "inband%d", getMsgId()); - DOMString messageId = buf; - - //Output streams are id's by message id - XmppStream *outs = new XmppStream(); - outputStreams[messageId] = outs; - - outs->setState(STREAM_OPENING); - - DOMString streamId = streamIdArg; - if (streamId.size()<1) - { - snprintf(buf, 31, "stream%d", getMsgId()); - DOMString streamId = buf; - } - outs->setMessageId(messageId); - outs->setStreamId(streamId); - outs->setPeerId(destId); - - - const char *fmt = - "<%s type='set' to='%s' id='%s'>" - "<open sid='%s' block-size='4096'" - " xmlns='http://jabber.org/protocol/ibb'/></%s>\n"; - if (!write(fmt, - streamPacket.c_str(), - destId.c_str(), messageId.c_str(), - streamId.c_str(), - streamPacket.c_str())) - { - outs->reset(); - return -1; - } - - int state = outs->getState(); - for (int tim=0 ; tim<20 ; tim++) - { - if (state == STREAM_OPEN) - break; - else if (state == STREAM_ERROR) - { - printf("ERROR\n"); - outs->reset(); - return false; - } - Thread::sleep(1000); - state = outs->getState(); - } - if (state != STREAM_OPEN) - { - printf("TIMEOUT ERROR\n"); - outs->reset(); - return -1; - } - - return true; -} - -/** - * - */ -bool XmppClient::outputStreamWrite(const DOMString &streamId, - const std::vector<unsigned char> &buf) -{ - std::map<DOMString, XmppStream *>::iterator iter = - outputStreams.find(streamId); - if (iter == outputStreams.end()) - return false; - XmppStream *outs = iter->second; - - unsigned int len = buf.size(); - unsigned int pos = 0; - - while (pos < len) - { - unsigned int pos2 = pos + 1024; - if (pos2>len) - pos2 = len; - - Base64Encoder encoder; - for (unsigned int i=pos ; i<pos2 ; i++) - encoder.append(buf[i]); - DOMString b64data = encoder.finish(); - - - const char *fmt = - "<message to='%s' id='msg%d'>" - "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>" - "%s" - "</data>" - "<amp xmlns='http://jabber.org/protocol/amp'>" - "<rule condition='deliver-at' value='stored' action='error'/>" - "<rule condition='match-resource' value='exact' action='error'/>" - "</amp>" - "</message>\n"; - if (!write(fmt, - outs->getPeerId().c_str(), - getMsgId(), - outs->getStreamId().c_str(), - outs->getSeqNr(), - b64data.c_str())) - { - outs->reset(); - return false; - } - pause(5000); - - pos = pos2; - } - - return true; -} - -/** - * - */ -bool XmppClient::outputStreamClose(const DOMString &streamId) -{ - std::map<DOMString, XmppStream *>::iterator iter = - outputStreams.find(streamId); - if (iter == outputStreams.end()) - return false; - XmppStream *outs = iter->second; - - char buf[32]; - snprintf(buf, 31, "inband%d", getMsgId()); - DOMString messageId = buf; - outs->setMessageId(messageId); - - outs->setState(STREAM_CLOSING); - const char *fmt = - "<%s type='set' to='%s' id='%s'>" - "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></%s>\n"; - if (!write(fmt, - streamPacket.c_str(), - outs->getPeerId().c_str(), - messageId.c_str(), - outs->getStreamId().c_str(), - streamPacket.c_str() - )) - return false; - - int state = outs->getState(); - for (int tim=0 ; tim<20 ; tim++) - { - if (state == STREAM_CLOSED) - break; - else if (state == STREAM_ERROR) - { - printf("ERROR\n"); - outs->reset(); - return false; - } - Thread::sleep(1000); - state = outs->getState(); - } - if (state != STREAM_CLOSED) - { - printf("TIMEOUT ERROR\n"); - outs->reset(); - return false; - } - - delete outs; - outputStreams.erase(streamId); - - return true; -} - - -/** - * - */ -bool XmppClient::inputStreamOpen(const DOMString &fromJid, - const DOMString &streamId, - const DOMString &/*iqId*/) -{ - XmppStream *ins = new XmppStream(); - - inputStreams[streamId] = ins; - ins->reset(); - ins->setPeerId(fromJid); - ins->setState(STREAM_CLOSED); - ins->setStreamId(streamId); - - int state = ins->getState(); - for (int tim=0 ; tim<20 ; tim++) - { - if (state == STREAM_OPENING) - break; - else if (state == STREAM_ERROR) - { - printf("ERROR\n"); - ins->reset(); - return false; - } - Thread::sleep(1000); - state = ins->getState(); - } - if (state != STREAM_OPENING) - { - printf("TIMEOUT ERROR\n"); - ins->reset(); - return false; - } - const char *fmt = - "<%s type='result' to='%s' id='%s'/>\n"; - if (!write(fmt, streamPacket.c_str(), - fromJid.c_str(), ins->getMessageId().c_str())) - { - return false; - } - - ins->setState(STREAM_OPEN); - return true; -} - - - -/** - * - */ -bool XmppClient::inputStreamClose(const DOMString &streamId) -{ - std::map<DOMString, XmppStream *>::iterator iter = - inputStreams.find(streamId); - if (iter == inputStreams.end()) - return false; - XmppStream *ins = iter->second; - - if (ins->getState() == STREAM_CLOSING) - { - const char *fmt = - "<iq type='result' to='%s' id='%s'/>\n"; - if (!write(fmt, ins->getPeerId().c_str(), - ins->getMessageId().c_str())) - { - return false; - } - } - inputStreams.erase(streamId); - delete ins; - - return true; -} - - - - - - -//######################################################################## -//# FILE TRANSFERS -//######################################################################## - - -bool XmppClient::processFileMessage(Element *root) -{ - DOMString siNamespace = "http://jabber.org/protocol/si"; - if (root->getTagAttribute("si", "xmlns") != siNamespace) - return false; - - - Element *mainElement = root->getFirstChild(); - if (!mainElement) - return false; - - DOMString from = mainElement->getAttribute("from"); - DOMString id = mainElement->getAttribute("id"); - DOMString type = mainElement->getAttribute("type"); - - status("received file message from %s", from.c_str()); - - if (type == "set") - { - DOMString streamId = root->getTagAttribute("si", "id"); - DOMString fname = root->getTagAttribute("file", "name"); - DOMString sizeStr = root->getTagAttribute("file", "size"); - DOMString hash = root->getTagAttribute("file", "hash"); - XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE); - event.setFrom(from); - event.setIqId(id); - event.setStreamId(streamId); - event.setFileName(fname); - event.setFileHash(hash); - event.setFileSize(atol(sizeStr.c_str())); - dispatchXmppEvent(event); - return true; - } - - //##expecting result or error - //file sends id'd by message id's - std::map<DOMString, XmppStream *>::iterator iter = - fileSends.find(id); - if (iter != fileSends.end()) - { - XmppStream *outf = iter->second; - if (from != outf->getPeerId()) - return true; - if (type == "error") - { - outf->setState(STREAM_ERROR); - error("user '%s' rejected file", from.c_str()); - return true; - } - else if (type == "result") - { - if (outf->getState() == STREAM_OPENING) - { - XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED); - event.setFrom(from); - dispatchXmppEvent(event); - outf->setState(STREAM_OPEN); - } - else if (outf->getState() == STREAM_CLOSING) - { - outf->setState(STREAM_CLOSED); - } - return true; - } - } - - return true; -} - - - - - - -/** - * - */ -bool XmppClient::fileSend(const DOMString &destJidArg, - const DOMString &offeredNameArg, - const DOMString &fileNameArg, - const DOMString &descriptionArg) -{ - DOMString destJid = destJidArg; - DOMString offeredName = offeredNameArg; - DOMString fileName = fileNameArg; - DOMString description = descriptionArg; - - struct stat finfo; - if (stat(fileName.c_str(), &finfo)<0) - { - error("Cannot stat file '%s' for sending", fileName.c_str()); - return false; - } - long fileLen = finfo.st_size; - if (!fileLen > 1000000) - { - error("'%s' too large", fileName.c_str()); - return false; - } - if (!S_ISREG(finfo.st_mode)) - { - error("'%s' is not a regular file", fileName.c_str()); - return false; - } - FILE *f = fopen(fileName.c_str(), "rb"); - if (!f) - { - error("cannot open '%s' for sending", fileName.c_str()); - return false; - } - std::vector<unsigned char> sendBuf; - Md5 md5hash; - for (long i=0 ; i<fileLen && !feof(f); i++) - { - int ch = fgetc(f); - if (ch<0) - break; - md5hash.append((unsigned char)ch); - sendBuf.push_back((unsigned char)ch); - } - fclose(f); - DOMString hash = md5hash.finishHex(); - printf("Hash:%s\n", hash.c_str()); - - - //## get the last path segment from the whole path - if (offeredName.size()<1) - { - int slashPos = -1; - for (unsigned int i=0 ; i<fileName.size() ; i++) - { - int ch = fileName[i]; - if (ch == '/' || ch == '\\') - slashPos = i; - } - if (slashPos>=0 && slashPos<=(int)(fileName.size()-1)) - { - offeredName = fileName.substr(slashPos+1, - fileName.size()-slashPos-1); - printf("offeredName:%s\n", offeredName.c_str()); - } - } - - char buf[32]; - snprintf(buf, 31, "file%d", getMsgId()); - DOMString messageId = buf; - - XmppStream *outf = new XmppStream(); - - outf->setState(STREAM_OPENING); - outf->setMessageId(messageId); - fileSends[messageId] = outf; - - snprintf(buf, 31, "stream%d", getMsgId()); - DOMString streamId = buf; - //outf->setStreamId(streamId); - - outf->setPeerId(destJid); - - char dtgBuf[81]; - struct tm *timeVal = gmtime(&(finfo.st_mtime)); - strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal); - - const char *fmt = - "<%s type='set' id='%s' to='%s'>" - "<si xmlns='http://jabber.org/protocol/si' id='%s'" - " mime-type='text/plain'" - " profile='http://jabber.org/protocol/si/profile/file-transfer'>" - "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'" - " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>" - "<feature xmlns='http://jabber.org/protocol/feature-neg'>" - "<x xmlns='jabber:x:data' type='form'>" - "<field var='stream-method' type='list-single'>" - //"<option><value>http://jabber.org/protocol/bytestreams</value></option>" - "<option><value>http://jabber.org/protocol/ibb</value></option>" - "</field></x></feature></si></%s>\n"; - if (!write(fmt, streamPacket.c_str(), - messageId.c_str(), destJid.c_str(), - streamId.c_str(), offeredName.c_str(), fileLen, - hash.c_str(), dtgBuf, description.c_str(), - streamPacket.c_str())) - { - return false; - } - - int ret = true; - int state = outf->getState(); - for (int tim=0 ; tim<20 ; tim++) - { - printf("##### waiting for open\n"); - if (state == STREAM_OPEN) - { - outf->reset(); - break; - } - else if (state == STREAM_ERROR) - { - printf("ERROR\n"); - outf->reset(); - ret = false; - } - Thread::sleep(1000); - state = outf->getState(); - } - if (state != STREAM_OPEN) - { - printf("TIMEOUT ERROR\n"); - ret = false; - } - - //free up this resource - fileSends.erase(messageId); - delete outf; - - if (!outputStreamOpen(destJid, streamId)) - { - error("cannot open output stream %s", streamId.c_str()); - return false; - } - - if (!outputStreamWrite(streamId, sendBuf)) - { - } - - if (!outputStreamClose(streamId)) - { - } - - return true; -} - - -class FileSendThread : public Thread -{ -public: - - FileSendThread(XmppClient &par, - const DOMString &destJidArg, - const DOMString &offeredNameArg, - const DOMString &fileNameArg, - const DOMString &descriptionArg) : client(par) - { - destJid = destJidArg; - offeredName = offeredNameArg; - fileName = fileNameArg; - description = descriptionArg; - } - - virtual ~FileSendThread() {} - - void run() - { - client.fileSend(destJid, offeredName, - fileName, description); - } - -private: - - XmppClient &client; - DOMString destJid; - DOMString offeredName; - DOMString fileName; - DOMString description; -}; - -/** - * - */ -bool XmppClient::fileSendBackground(const DOMString &destJid, - const DOMString &offeredName, - const DOMString &fileName, - const DOMString &description) -{ - FileSendThread thread(*this, destJid, offeredName, - fileName, description); - thread.start(); - return true; -} - - -/** - * - */ -bool XmppClient::fileReceive(const DOMString &fromJid, - const DOMString &iqId, - const DOMString &streamId, - const DOMString &fileName, - long /*fileSize*/, - const DOMString &/*fileHash*/) -{ - const char *fmt = - "<%s type='result' to='%s' id='%s'>" - "<si xmlns='http://jabber.org/protocol/si'>" - "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>" - "<feature xmlns='http://jabber.org/protocol/feature-neg'>" - "<x xmlns='jabber:x:data' type='submit'>" - "<field var='stream-method'>" - "<value>http://jabber.org/protocol/ibb</value>" - "</field></x></feature></si></%s>\n"; - if (!write(fmt, streamPacket.c_str(), - fromJid.c_str(), iqId.c_str(), - streamPacket.c_str())) - { - return false; - } - - if (!inputStreamOpen(fromJid, streamId, iqId)) - { - return false; - } - - XmppStream *ins = inputStreams[streamId]; - - Md5 md5; - FILE *f = fopen(fileName.c_str(), "wb"); - if (!f) - { - return false; - } - - while (true) - { - if (ins->available()<1) - { - if (ins->getState() == STREAM_CLOSING) - break; - pause(100); - continue; - } - std::vector<unsigned char> ret = ins->read(); - std::vector<unsigned char>::iterator iter; - for (iter=ret.begin() ; iter!=ret.end() ; iter++) - { - unsigned char ch = *iter; - md5.append(&ch, 1); - fwrite(&ch, 1, 1, f); - } - } - - inputStreamClose(streamId); - fclose(f); - - DOMString hash = md5.finishHex(); - printf("received file hash:%s\n", hash.c_str()); - - return true; -} - - - -class FileReceiveThread : public Thread -{ -public: - - FileReceiveThread(XmppClient &par, - const DOMString &fromJidArg, - const DOMString &iqIdArg, - const DOMString &streamIdArg, - const DOMString &fileNameArg, - long fileSizeArg, - const DOMString &fileHashArg) : client(par) - { - fromJid = fromJidArg; - iqId = iqIdArg; - streamId = streamIdArg; - fileName = fileNameArg; - fileSize = fileSizeArg; - fileHash = fileHashArg; - } - - virtual ~FileReceiveThread() {} - - void run() - { - client.fileReceive(fromJid, iqId, streamId, - fileName, fileSize, fileHash); - } - -private: - - XmppClient &client; - DOMString fromJid; - DOMString iqId; - DOMString streamId; - DOMString fileName; - long fileSize; - DOMString fileHash; -}; - -/** - * - */ -bool XmppClient::fileReceiveBackground(const DOMString &fromJid, - const DOMString &iqId, - const DOMString &streamId, - const DOMString &fileName, - long fileSize, - const DOMString &fileHash) -{ - FileReceiveThread thread(*this, fromJid, iqId, streamId, - fileName, fileSize, fileHash); - thread.start(); - return true; -} - - - -//######################################################################## -//# X M P P G R O U P C H A T -//######################################################################## - -/** - * - */ -XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg) -{ - groupJid = groupJidArg; -} - -/** - * - */ -XmppGroupChat::XmppGroupChat(const XmppGroupChat &other) -{ - groupJid = other.groupJid; - userList = other.userList; -} - -/** - * - */ -XmppGroupChat::~XmppGroupChat() -{ -} - - -/** - * - */ -DOMString XmppGroupChat::getGroupJid() -{ - return groupJid; -} - - -void XmppGroupChat::userAdd(const DOMString &nick, - const DOMString &jid) -{ - std::vector<XmppUser>::iterator iter; - for (iter= userList.begin() ; iter!=userList.end() ; iter++) - { - if (iter->nick == nick) - return; - } - XmppUser user(jid, nick); - userList.push_back(user); -} - -void XmppGroupChat::userShow(const DOMString &nick, - const DOMString &show) -{ - DOMString theShow = show; - if (theShow == "") - theShow = "available"; // a join message will now have a show - std::vector<XmppUser>::iterator iter; - for (iter= userList.begin() ; iter!=userList.end() ; iter++) - { - if (iter->nick == nick) - iter->show = theShow; - } -} - -void XmppGroupChat::userDelete(const DOMString &nick) -{ - std::vector<XmppUser>::iterator iter; - for (iter= userList.begin() ; iter!=userList.end() ; ) - { - if (iter->nick == nick) - iter = userList.erase(iter); - else - iter++; - } -} - -std::vector<XmppUser> XmppGroupChat::getUserList() const -{ - return userList; -} - - - - - - - - - -} //namespace Pedro -//######################################################################## -//# E N D O F F I L E -//######################################################################## - - - - - - - - - - - - - - - |
