Code

empty messages are passed onto all listeners in pedro, gui ignores such messages
[inkscape.git] / src / pedro / pedroxmpp.cpp
1 /*
2  * Implementation the Pedro mini-XMPP client
3  *
4  * Authors:
5  *   Bob Jamison
6  *
7  * Copyright (C) 2005-2006 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
25 #include <stdio.h>
26 #include <stdarg.h>
28 #include <sys/stat.h>
30 #include <time.h>
32 #include "pedroxmpp.h"
33 #include "pedrodom.h"
34 #include "pedroutil.h"
36 #include <map>
40 namespace Pedro
41 {
44 //########################################################################
45 //########################################################################
46 //# X M P P    E V E N T
47 //########################################################################
48 //########################################################################
51 XmppEvent::XmppEvent(int type)
52 {
53     eventType = type;
54     presence  = false;
55     dom       = NULL;
56 }
58 XmppEvent::XmppEvent(const XmppEvent &other)
59 {
60     assign(other);
61 }
63 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
64 {
65     assign(other);
66     return (*this);
67 }
69 XmppEvent::~XmppEvent()
70 {
71     if (dom)
72         delete dom;
73 }
75 void XmppEvent::assign(const XmppEvent &other)
76 {
77     eventType = other.eventType;
78     presence  = other.presence;
79     status    = other.status;
80     show      = other.show;
81     to        = other.to;
82     from      = other.from;
83     group     = other.group;
84     data      = other.data;
85     fileName  = other.fileName;
86     fileDesc  = other.fileDesc;
87     fileSize  = other.fileSize;
88     fileHash  = other.fileHash;
89     setDOM(other.dom);
90 }
92 int XmppEvent::getType() const
93 {
94     return eventType;
95 }
97 DOMString XmppEvent::getIqId() const
98 {
99     return iqId;
102 void XmppEvent::setIqId(const DOMString &val)
104     iqId = val;
107 DOMString XmppEvent::getStreamId() const
109     return streamId;
112 void XmppEvent::setStreamId(const DOMString &val)
114     streamId = val;
117 bool XmppEvent::getPresence() const
119     return presence;
122 void XmppEvent::setPresence(bool val)
124     presence = val;
127 DOMString XmppEvent::getShow() const
129     return show;
132 void XmppEvent::setShow(const DOMString &val)
134     show = val;
137 DOMString XmppEvent::getStatus() const
139     return status;
142 void XmppEvent::setStatus(const DOMString &val)
144     status = val;
147 DOMString XmppEvent::getTo() const
149     return to;
152 void XmppEvent::setTo(const DOMString &val)
154     to = val;
157 DOMString XmppEvent::getFrom() const
159     return from;
162 void XmppEvent::setFrom(const DOMString &val)
164     from = val;
167 DOMString XmppEvent::getGroup() const
169     return group;
172 void XmppEvent::setGroup(const DOMString &val)
174     group = val;
177 DOMString XmppEvent::getData() const
179     return data;
182 void XmppEvent::setData(const DOMString &val)
184     data = val;
187 DOMString XmppEvent::getFileName() const
189     return fileName;
192 void XmppEvent::setFileName(const DOMString &val)
194     fileName = val;
197 DOMString XmppEvent::getFileDesc() const
199     return fileDesc;
202 void XmppEvent::setFileDesc(const DOMString &val)
204     fileDesc = val;
207 long XmppEvent::getFileSize() const
209     return fileSize;
212 void XmppEvent::setFileSize(long val)
214     fileSize = val;
217 DOMString XmppEvent::getFileHash() const
219     return fileHash;
222 void XmppEvent::setFileHash(const DOMString &val)
224     fileHash = val;
227 Element *XmppEvent::getDOM() const
229     return dom;
232 void XmppEvent::setDOM(const Element *val)
234     if (!val)
235         dom = NULL;
236     else
237         dom = ((Element *)val)->clone();
241 std::vector<XmppUser> XmppEvent::getUserList() const
243     return userList;
246 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
248     userList = val;
259 //########################################################################
260 //########################################################################
261 //# X M P P    E V E N T    T A R G E T
262 //########################################################################
263 //########################################################################
266 //###########################
267 //# CONSTRUCTORS
268 //###########################
270 XmppEventTarget::XmppEventTarget()
272     eventQueueEnabled = false;
276 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
278     listeners         = other.listeners;
279     eventQueueEnabled = other.eventQueueEnabled;
282 XmppEventTarget::~XmppEventTarget()
287 //###########################
288 //# M E S S A G E S
289 //###########################
291 /**
292  *  Print a printf()-like formatted error message
293  */
294 void XmppEventTarget::error(char *fmt, ...)
296     va_list args;
297     va_start(args,fmt);
298     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
299     va_end(args) ;
300     fprintf(stderr, "Error:%s\n", targetWriteBuf);
301     XmppEvent evt(XmppEvent::EVENT_ERROR);
302     evt.setData(targetWriteBuf);
303     dispatchXmppEvent(evt);
308 /**
309  *  Print a printf()-like formatted trace message
310  */
311 void XmppEventTarget::status(char *fmt, ...)
313     va_list args;
314     va_start(args,fmt);
315     vsnprintf(targetWriteBuf, targetWriteBufLen, fmt, args);
316     va_end(args) ;
317     //printf("Status:%s\n", targetWriteBuf);
318     XmppEvent evt(XmppEvent::EVENT_STATUS);
319     evt.setData(targetWriteBuf);
320     dispatchXmppEvent(evt);
325 //###########################
326 //# L I S T E N E R S
327 //###########################
329 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
331     std::vector<XmppEventListener *>::iterator iter;
332     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
333         (*iter)->processXmppEvent(event);
334     if (eventQueueEnabled)
335         eventQueue.push_back(event);
338 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
340     XmppEventListener *lsnr = (XmppEventListener *)&listener;
341     std::vector<XmppEventListener *>::iterator iter;
342     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
343         if (*iter == lsnr)
344             return;
345     listeners.push_back(lsnr);
348 void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
350     XmppEventListener *lsnr = (XmppEventListener *)&listener;
351     std::vector<XmppEventListener *>::iterator iter;
352     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
353         if (*iter == lsnr)
354             listeners.erase(iter);
357 void XmppEventTarget::clearXmppEventListeners()
359     listeners.clear();
363 //###########################
364 //# E V E N T    Q U E U E
365 //###########################
367 void XmppEventTarget::eventQueueEnable(bool val)
369     eventQueueEnabled = val;
370     if (!eventQueueEnabled)
371         eventQueue.clear();
374 int XmppEventTarget::eventQueueAvailable()
376     return eventQueue.size();
379 XmppEvent XmppEventTarget::eventQueuePop()
381     if (!eventQueueEnabled || eventQueue.size()<1)
382         {
383         XmppEvent dummy(XmppEvent::EVENT_NONE);
384         return dummy;
385         }
386     XmppEvent event = *(eventQueue.begin());
387     eventQueue.erase(eventQueue.begin());
388     return event;
395 //########################################################################
396 //########################################################################
397 //# X M P P    S T R E A M
398 //########################################################################
399 //########################################################################
402 /**
403  *
404  */
405 class XmppStream
407 public:
409     /**
410      *
411      */
412     XmppStream();
414     /**
415      *
416      */
417     virtual ~XmppStream();
419     /**
420      *
421      */
422     virtual void reset();
424     /**
425      *
426      */
427     virtual int getState();
429     /**
430      *
431      */
432     virtual void setState(int val);
434     /**
435      *
436      */
437     virtual DOMString getStreamId();
439     /**
440      *
441      */
442     void setStreamId(const DOMString &val);
444     /**
445      *
446      */
447     virtual DOMString getIqId();
449     /**
450      *
451      */
452     void setIqId(const DOMString &val);
454     /**
455      *
456      */
457     virtual int getSeqNr();
459     /**
460      *
461      */
462     virtual DOMString getPeerId();
464     /**
465      *
466      */
467     virtual void setPeerId(const DOMString &val);
469     /**
470      *
471      */
472     int available();
474     /**
475      *
476      */
477     void receiveData(std::vector<unsigned char> &newData);
479     /**
480      *
481      */
482     std::vector<unsigned char> read();
484 private:
487     DOMString streamId;
489     DOMString iqId;
491     DOMString sourceId;
493     int state;
495     long seqNr;
497     std::vector<unsigned char> data;
498 };
501 /**
502  *
503  */
504 XmppStream::XmppStream()
506     reset();
509 /**
510  *
511  */
512 XmppStream::~XmppStream()
514     reset();
517 /**
518  *
519  */
520 void XmppStream::reset()
522     state = XmppClient::STREAM_AVAILABLE;
523     seqNr = 0;
524     data.clear();
527 /**
528  *
529  */
530 int XmppStream::getState()
532     return state;
535 /**
536  *
537  */
538 void XmppStream::setState(int val)
540     state = val;
543 /**
544  *
545  */
546 DOMString XmppStream::getStreamId()
548     return streamId;
551 /**
552  *
553  */
554 void XmppStream::setStreamId(const DOMString &val)
556     streamId = val;
559 /**
560  *
561  */
562 DOMString XmppStream::getIqId()
564     return iqId;
568 /**
569  *
570  */
571 void XmppStream::setIqId(const DOMString &val)
573     iqId = val;
576 /**
577  *  Source or destination JID
578  */
579 void XmppStream::setPeerId(const DOMString &val)
581     sourceId = val;
584 /**
585  *  Source or destination JID
586  */
587 DOMString XmppStream::getPeerId()
589     return sourceId;
592 /**
593  *  Stream packet sequence number
594  */
595 int XmppStream::getSeqNr()
597     seqNr++;
598     if (seqNr >= 65535)
599         seqNr = 0;
600     return seqNr;
603 /**
604  *
605  */
606 int XmppStream::available()
608     return data.size();
611 /**
612  *
613  */
614 void XmppStream::receiveData(std::vector<unsigned char> &newData)
616     std::vector<unsigned char>::iterator iter;
617     for (iter=newData.begin() ; iter!=newData.end() ; iter++)
618         data.push_back(*iter);
621 /**
622  *
623  */
624 std::vector<unsigned char> XmppStream::read()
626     if (state != XmppClient::STREAM_OPEN)
627         {
628         std::vector<unsigned char>dummy;
629         return dummy;
630         }
631     std::vector<unsigned char> ret = data;
632     data.clear();
633     return ret;
641 //########################################################################
642 //########################################################################
643 //# X M P P    C L I E N T
644 //########################################################################
645 //########################################################################
647 class ReceiverThread : public Runnable
649 public:
651     ReceiverThread(XmppClient &par) : client(par) {}
653     virtual ~ReceiverThread() {}
655     void run()
656       { client.receiveAndProcessLoop(); }
658 private:
660     XmppClient &client;
661 };
667 //########################################################################
668 //# CONSTRUCTORS
669 //########################################################################
671 XmppClient::XmppClient()
673     init();
677 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
679     init();
680     assign(other);
683 void XmppClient::assign(const XmppClient &other)
685     msgId         = other.msgId;
686     host          = other.host;
687     realm         = other.realm;
688     port          = other.port;
689     username      = other.username;
690     password      = other.password;
691     resource      = other.resource;
692     connected     = other.connected;
693     doRegister    = other.doRegister;
694     groupChats    = other.groupChats;
698 void XmppClient::init()
700     sock          = new TcpSocket();
701     msgId         = 0;
702     connected     = false;
703     doRegister    = false;
705     for (int i=0 ; i<outputStreamCount ; i++)
706         {
707         outputStreams[i] = new XmppStream();
708         }
709     for (int i=0 ; i<inputStreamCount ; i++)
710         {
711         inputStreams[i] = new XmppStream();
712         }
713     for (int i=0 ; i<fileSendCount ; i++)
714         {
715         fileSends[i] = new XmppStream();
716         }
719 XmppClient::~XmppClient()
721     disconnect();
722     delete sock;
723     for (int i=0 ; i<outputStreamCount ; i++)
724         {
725         delete outputStreams[i];
726         }
727     for (int i=0 ; i<inputStreamCount ; i++)
728         {
729         delete inputStreams[i];
730         }
731     for (int i=0 ; i<fileSendCount ; i++)
732         {
733         delete fileSends[i];
734         }
735     groupChatsClear();
743 //########################################################################
744 //# UTILILY
745 //########################################################################
747 /**
748  *
749  */
750 bool XmppClient::pause(unsigned long millis)
752     Thread::sleep(millis);
753     return true;
757 static int strIndex(const DOMString &str, char *key)
759     unsigned int p = str.find(key);
760     if (p == str.npos)
761         return -1;
762     return p;
767 DOMString XmppClient::toXml(const DOMString &str)
769     return Parser::encode(str);
774 static DOMString trim(const DOMString &str)
776     unsigned int i;
777     for (i=0 ; i<str.size() ; i++)
778         if (!isspace(str[i]))
779             break;
780     int start = i;
781     for (i=str.size() ; i>0 ; i--)
782         if (!isspace(str[i-1]))
783             break;
784     int end = i;
785     if (start>=end)
786         return "";
787     return str.substr(start, end);
794 //########################################################################
795 //# VARIABLES  (ones that need special handling)
796 //########################################################################
798 /**
799  *
800  */
801 DOMString XmppClient::getUsername()
803     return username;
806 /**
807  *
808  */
809 void XmppClient::setUsername(const DOMString &val)
811     int p = strIndex(val, "@");
812     if (p > 0)
813         {
814         username = val.substr(0, p);
815         realm    = val.substr(p+1, jid.size()-p-1);
816         }
817     else
818        {
819        realm    = host;
820        username = val;
821        }
831 //########################################################################
832 //# RECEIVING
833 //########################################################################
836 DOMString XmppClient::readStanza()
839     int  openCount    = 0;
840     bool inTag        = false;
841     bool slashSeen    = false;
842     bool trivialTag   = false;
843     bool querySeen    = false;
844     bool inQuote      = false;
845     bool textSeen     = false;
846     DOMString buf;
849     time_t timeout = time((time_t *)0) + 180;
851     while (true)
852         {
853         int ch = sock->read();
854         //printf("%c", ch); fflush(stdout);
855         if (ch<0)
856             {
857             if (ch == -2) //a simple timeout, not an error
858                 {
859                 //Since we are timed out, let's assume that we
860                 //are between chunks of text.  Let's reset all states.
861                 //printf("-----#### Timeout\n");
862                 time_t currentTime = time((time_t *)0);
863                 if (currentTime > timeout)
864                     {
865                     timeout = currentTime + 180;
866                     if (!write("\n"))
867                         {
868                         error("ping send error");
869                         disconnect();
870                         return "";
871                         }
872                     }
873                 continue;
874                 }
875             else
876                 {
877                 keepGoing = false;
878                 if (!sock->isConnected())
879                     {
880                     disconnect();
881                     return "";
882                     }
883                 else
884                     {
885                     error("socket read error");
886                     disconnect();
887                     return "";
888                     }
889                 }
890             }
891         buf.push_back(ch);
892         if (ch == '<')
893             {
894             inTag      = true;
895             slashSeen  = false;
896             querySeen  = false;
897             inQuote    = false;
898             textSeen   = false;
899             trivialTag = false;
900             }
901         else if (ch == '>')
902             {
903             if (!inTag)  //unescaped '>' in pcdata? horror
904                 continue;
905             inTag     = false;
906             if (!trivialTag && !querySeen)
907                 {
908                 if (slashSeen)
909                     openCount--;
910                 else
911                     openCount++;
912                 }
913             //printf("# openCount:%d t:%d q:%d\n",
914             //      openCount, trivialTag, querySeen);
915             //check if we are 'balanced', but not a <?version?> tag
916             if (openCount <= 0 && !querySeen)
917                 {
918                 break;
919                 }
920             //we know that this one will be open-ended
921             if (strIndex(buf, "<stream:stream") >= 0)
922                 {
923                 buf.append("</stream:stream>");
924                 break;
925                 }
926             }
927         else if (ch == '/')
928             {
929             if (inTag && !inQuote)
930                 {
931                 slashSeen = true;
932                 if (textSeen) // <tagName/>  <--looks like this
933                     trivialTag = true;
934                 }
935             }
936         else if (ch == '?')
937             {
938             if (inTag && !inQuote)
939                 querySeen = true;
940             }
941         else if (ch == '"' || ch == '\'')
942             {
943             if (inTag)
944                 inQuote = !inQuote;
945             }
946         else
947             {
948             if (inTag && !inQuote && !isspace(ch))
949                 textSeen = true;
950             }
951         }
952     return buf;
957 static bool isGroupChat(Element *root)
959     if (!root)
960         return false;
961     std::vector<Element *>elems = root->findElements("x");
962     for (unsigned int i=0 ; i<elems.size() ; i++)
963         {
964         DOMString xmlns = elems[i]->getAttribute("xmlns");
965         //printf("### XMLNS ### %s\n", xmlns.c_str());
966         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
967             return true;
968         }
969    return false;
975 static bool parseJid(const DOMString &fullJid,
976              DOMString &jid, DOMString &resource)
978     int p = strIndex(fullJid, "/");
979     if (p < 0)
980         {
981         jid = fullJid;
982         resource = "";
983         return true;
984         }
985     jid = fullJid.substr(0, p);
986     resource = fullJid.substr(p+1, fullJid.size()-p-1);
987     return true;
993 bool XmppClient::processMessage(Element *root)
995     DOMString from    = root->getTagAttribute("message", "from");
996     DOMString to      = root->getTagAttribute("message", "to");
997     DOMString type    = root->getTagAttribute("message", "type");
999     //####Check for embedded namespaces here
1000     //# IN BAND BYTESTREAMS
1001     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
1002     if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
1003         {
1004         DOMString streamId = root->getTagAttribute("data", "sid");
1005         if (streamId.size() > 0)
1006             {
1007             for (int i=0 ; i<inputStreamCount ; i++)
1008                 {
1009                 XmppStream *ins = inputStreams[i];
1010                 //printf("##ins:%s  streamid:%s\n",
1011                 //    ins->getStreamId().c_str(),  streamId.c_str());
1012                 if (ins->getStreamId() == streamId)
1013                     {
1014                     //# We have a winner
1015                     if (ins->getState() != STREAM_OPEN)
1016                         {
1017                         XmppEvent event(XmppEvent::EVENT_ERROR);
1018                         event.setFrom(from);
1019                         event.setData("received unrequested stream data");
1020                         dispatchXmppEvent(event);
1021                         return true;
1022                         }
1023                     DOMString data = root->getTagValue("data");
1024                     std::vector<unsigned char>binData =
1025                                Base64Decoder::decode(data);
1026                     ins->receiveData(binData);
1027                     }
1028                 }
1029             }
1030         }
1033     //#### NORMAL MESSAGES
1034     DOMString subject = root->getTagValue("subject");
1035     DOMString body    = root->getTagValue("body");
1036     DOMString thread  = root->getTagValue("thread");
1037     //##rfc 3921, para 2.4.  ignore if no recognizable info
1038     //if (subject.size() < 1 && thread.size()<1)
1039     //    return true;
1041     if (type == "groupchat")
1042         {
1043         DOMString fromGid;
1044         DOMString fromNick;
1045         parseJid(from, fromGid, fromNick);
1046         //printf("fromGid:%s  fromNick:%s\n",
1047         //        fromGid.c_str(), fromNick.c_str());
1048         DOMString toGid;
1049         DOMString toNick;
1050         parseJid(to, toGid, toNick);
1051         //printf("toGid:%s  toNick:%s\n",
1052         //        toGid.c_str(), toNick.c_str());
1054         if (fromNick.size() > 0)//normal group message
1055             {
1056             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
1057             event.setGroup(fromGid);
1058             event.setFrom(fromNick);
1059             event.setData(body);
1060             event.setDOM(root);
1061             dispatchXmppEvent(event);
1062             }
1063         else // from the server itself
1064             {
1065             //note the space before, so it doesnt match 'unlocked'
1066             if (strIndex(body, " locked") >= 0)
1067                 {
1068                 printf("LOCKED!! ;)\n");
1069                 char *fmt =
1070                 "<iq from='%s' id='create%d' to='%s' type='set'>"
1071                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
1072                 "<x xmlns='jabber:x:data' type='submit'/>"
1073                 "</query></iq>\n";
1074                 if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
1075                     return false;
1076                 }
1077             }
1078         }
1079     else
1080         {
1081         XmppEvent event(XmppEvent::EVENT_MESSAGE);
1082         event.setFrom(from);
1083         event.setData(body);
1084         event.setDOM(root);
1085         dispatchXmppEvent(event);
1086         }
1088     return true;
1094 bool XmppClient::processPresence(Element *root)
1097     DOMString fullJid     = root->getTagAttribute("presence", "from");
1098     DOMString to          = root->getTagAttribute("presence", "to");
1099     DOMString presenceStr = root->getTagAttribute("presence", "type");
1100     bool presence = true;
1101     if (presenceStr == "unavailable")
1102         presence = false;
1103     DOMString status      = root->getTagValue("status");
1104     DOMString show        = root->getTagValue("show");
1106     if (isGroupChat(root))
1107         {
1108         DOMString fromGid;
1109         DOMString fromNick;
1110         parseJid(fullJid, fromGid, fromNick);
1111         //printf("fromGid:%s  fromNick:%s\n",
1112         //        fromGid.c_str(), fromNick.c_str());
1113         DOMString item_jid = root->getTagAttribute("item", "jid");
1114         if (item_jid == jid) //Me
1115             {
1116             if (presence)
1117                 {
1118                 groupChatCreate(fromGid);
1119                 groupChatUserAdd(fromGid, fromNick, "");
1120                 groupChatUserShow(fromGid, fromNick, "available");
1122                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
1123                 event.setGroup(fromGid);
1124                 event.setFrom(fromNick);
1125                 event.setPresence(presence);
1126                 event.setShow(show);
1127                 event.setStatus(status);
1128                 dispatchXmppEvent(event);
1129                 }
1130             else
1131                 {
1132                 groupChatDelete(fromGid);
1133                 groupChatUserDelete(fromGid, fromNick);
1135                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
1136                 event.setGroup(fromGid);
1137                 event.setFrom(fromNick);
1138                 event.setPresence(presence);
1139                 event.setShow(show);
1140                 event.setStatus(status);
1141                 dispatchXmppEvent(event);
1142                 }
1143             }
1144         else // someone else
1145             {
1146             if (presence)
1147                 {
1148                 groupChatUserAdd(fromGid, fromNick, "");
1149                 }
1150             else
1151                 groupChatUserDelete(fromGid, fromNick);
1152             groupChatUserShow(fromGid, fromNick, show);
1153             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
1154             event.setGroup(fromGid);
1155             event.setFrom(fromNick);
1156             event.setPresence(presence);
1157             event.setShow(show);
1158             event.setStatus(status);
1159             dispatchXmppEvent(event);
1160             }
1161         }
1162     else
1163         {
1164         DOMString shortJid;
1165         DOMString dummy;
1166         parseJid(fullJid, shortJid, dummy);
1167         rosterShow(shortJid, show); //users in roster do not have resource
1169         XmppEvent event(XmppEvent::EVENT_PRESENCE);
1170         event.setFrom(fullJid);
1171         event.setPresence(presence);
1172         event.setShow(show);
1173         event.setStatus(status);
1174         dispatchXmppEvent(event);
1175         }
1177     return true;
1182 bool XmppClient::processIq(Element *root)
1184     DOMString from  = root->getTagAttribute("iq", "from");
1185     DOMString id    = root->getTagAttribute("iq", "id");
1186     DOMString type  = root->getTagAttribute("iq", "type");
1187     DOMString xmlns = root->getTagAttribute("query", "xmlns");
1189     if (id.size()<1)
1190         return true;
1192     //Group chat
1193     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
1194         {
1195         printf("results of MUC query\n");
1196         }
1197     //printf("###IQ xmlns:%s\n", xmlns.c_str());
1199     //### FILE TRANSFERS
1200     DOMString siNamespace = "http://jabber.org/protocol/si";
1201     if (root->getTagAttribute("si", "xmlns") == siNamespace)
1202         {
1203         if (type == "set")
1204             {
1205             DOMString streamId = root->getTagAttribute("si", "id");
1206             DOMString fname    = root->getTagAttribute("file", "name");
1207             DOMString sizeStr  = root->getTagAttribute("file", "size");
1208             DOMString hash     = root->getTagAttribute("file", "hash");
1209             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
1210             event.setFrom(from);
1211             event.setIqId(id);
1212             event.setStreamId(streamId);
1213             event.setFileName(fname);
1214             event.setFileHash(hash);
1215             event.setFileSize(atol(sizeStr.c_str()));
1216             dispatchXmppEvent(event);
1217             }
1218         else  //result
1219             {
1220             //printf("Got result\n");
1221             for (int i=0 ; i<fileSendCount ; i++)
1222                 {
1223                 XmppStream *outf = fileSends[i];
1224                 if (outf->getIqId() == id &&
1225                     from == outf->getPeerId())
1226                     {
1227                     if (type == "error")
1228                         {
1229                         outf->setState(STREAM_ERROR);
1230                         error("user '%s' rejected file", from.c_str());
1231                         return true;
1232                         }
1233                     else if (type == "result")
1234                         {
1235                         if (outf->getState() == STREAM_OPENING)
1236                             {
1237                             XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
1238                             event.setFrom(from);
1239                             dispatchXmppEvent(event);
1240                             outf->setState(STREAM_OPEN);
1241                             }
1242                         else if (outf->getState() == STREAM_CLOSING)
1243                             {
1244                             outf->setState(STREAM_CLOSED);
1245                             }
1246                         return true;
1247                         }
1248                     }
1249                 }
1250             }
1251         return true;
1252         }
1255     //### IN-BAND BYTESTREAMS
1256     //### Incoming stream requests
1257     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
1258     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
1259         {
1260         DOMString streamId = root->getTagAttribute("open", "sid");
1261         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
1262         dispatchXmppEvent(event);
1263         if (streamId.size()>0)
1264             {
1265             for (int i=0 ; i<inputStreamCount ; i++)
1266                 {
1267                 XmppStream *ins = inputStreams[i];
1268                 if (ins->getStreamId() == streamId)
1269                     {
1270                     ins->setState(STREAM_OPENING);
1271                     ins->setIqId(id);
1272                     return true;
1273                     }
1274                 }
1275             }
1276         return true;
1277         }
1278     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
1279         {
1280         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
1281         dispatchXmppEvent(event);
1282         DOMString streamId = root->getTagAttribute("close", "sid");
1283         if (streamId.size()>0)
1284             {
1285             for (int i=0 ; i<inputStreamCount ; i++)
1286                 {
1287                 XmppStream *ins = inputStreams[i];
1288                 if (ins->getStreamId() == streamId &&
1289                     from == ins->getPeerId())
1290                     {
1291                     ins->setState(STREAM_CLOSING);
1292                     ins->setIqId(id);
1293                     return true;
1294                     }
1295                 }
1296             }
1297         return true;
1298         }
1299     //### Responses to outgoing requests
1300     for (int i=0 ; i<outputStreamCount ; i++)
1301         {
1302         XmppStream *outs = outputStreams[i];
1303         if (outs->getIqId() == id)
1304             {
1305             if (type == "error")
1306                 {
1307                 outs->setState(STREAM_ERROR);
1308                 return true;
1309                 }
1310             else if (type == "result")
1311                 {
1312                 if (outs->getState() == STREAM_OPENING)
1313                     {
1314                     outs->setState(STREAM_OPEN);
1315                     }
1316                 else if (outs->getState() == STREAM_CLOSING)
1317                     {
1318                     outs->setState(STREAM_CLOSED);
1319                     }
1320                 return true;
1321                 }
1322             }
1323         }
1325     //###Normal Roster stuff
1326     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
1327         {
1328         roster.clear();
1329         std::vector<Element *>elems = root->findElements("item");
1330         for (unsigned int i=0 ; i<elems.size() ; i++)
1331             {
1332             Element *item = elems[i];
1333             DOMString userJid      = item->getAttribute("jid");
1334             DOMString name         = item->getAttribute("name");
1335             DOMString subscription = item->getAttribute("subscription");
1336             DOMString group        = item->getTagValue("group");
1337             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
1338             //        subscription.c_str(), group.c_str());
1339             XmppUser user(userJid, name, subscription, group);
1340             roster.push_back(user);
1341             }
1342         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
1343         dispatchXmppEvent(event);
1344         }
1346     else if (id.find("regnew") != id.npos)
1347         {
1349         }
1351     else if (id.find("regpass") != id.npos)
1352         {
1353         std::vector<Element *> list = root->findElements("error");
1354         if (list.size()==0)
1355             {
1356             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
1357             evt.setTo(username);
1358             evt.setFrom(host);
1359             dispatchXmppEvent(evt);
1360             return true;
1361             }
1363         Element *errElem = list[0];
1364         DOMString errMsg = "Password change error: ";
1365         if (errElem->findElements("bad-request").size()>0)
1366             {
1367             errMsg.append("password change does not contain complete information");
1368             }
1369         else if (errElem->findElements("not-authorized").size()>0)
1370             {
1371             errMsg.append("server does not consider the channel safe "
1372                           "enough to enable a password change");
1373             }
1374         else if (errElem->findElements("not-allowed").size()>0)
1375             {
1376             errMsg.append("server does not allow password changes");
1377             }
1378         else if (errElem->findElements("unexpected-request").size()>0)
1379             {
1380             errMsg.append(
1381              "IQ set does not contain a 'from' address because "
1382              "the entity is not registered with the server");
1383             }
1384         error((char *)errMsg.c_str());
1385         }
1387     else if (id.find("regcancel") != id.npos)
1388         {
1389         std::vector<Element *> list = root->findElements("error");
1390         if (list.size()==0)
1391             {
1392             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
1393             evt.setTo(username);
1394             evt.setFrom(host);
1395             dispatchXmppEvent(evt);
1396             return true;
1397             }
1399         Element *errElem = list[0];
1400         DOMString errMsg = "Registration cancel error: ";
1401         if (errElem->findElements("bad-request").size()>0)
1402             {
1403             errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
1404             }
1405         else if (errElem->findElements("forbidden").size()>0)
1406             {
1407             errMsg.append("sender does not have sufficient permissions to cancel the registration");
1408             }
1409         else if (errElem->findElements("not-allowed").size()>0)
1410             {
1411             errMsg.append("not allowed to cancel registrations in-band");
1412             }
1413         else if (errElem->findElements("registration-required").size()>0)
1414             {
1415             errMsg.append("not previously registered");
1416             }
1417         else if (errElem->findElements("unexpected-request").size()>0)
1418             {
1419             errMsg.append(
1420                  "IQ set does not contain a 'from' address because "
1421                  "the entity is not registered with the server");
1422             }
1423         error((char *)errMsg.c_str());
1424         }
1426     return true;
1431 bool XmppClient::receiveAndProcess()
1433     if (!keepGoing)
1434         return false;
1436     Parser parser;
1438     DOMString recvBuf = readStanza();
1439     recvBuf = trim(recvBuf);
1440     if (recvBuf.size() < 1)
1441         return true;
1443     //Ugly hack.  Apparently the first char can be dropped on timeouts
1444     //if (recvBuf[0] != '<')
1445     //    recvBuf.insert(0, "<");
1447     status("RECV: %s", recvBuf.c_str());
1448     Element *root = parser.parse(recvBuf);
1449     if (!root)
1450         {
1451         printf("Bad elem\n");
1452         return true;
1453         }
1455     //#### MESSAGE
1456     std::vector<Element *>elems = root->findElements("message");
1457     if (elems.size()>0)
1458         {
1459         if (!processMessage(root))
1460             return false;
1461         }
1463     //#### PRESENCE
1464     elems = root->findElements("presence");
1465     if (elems.size()>0)
1466         {
1467         if (!processPresence(root))
1468             return false;
1469         }
1471     //#### INFO
1472     elems = root->findElements("iq");
1473     if (elems.size()>0)
1474         {
1475         if (!processIq(root))
1476             return false;
1477         }
1479     delete root;
1481     return true;
1485 bool XmppClient::receiveAndProcessLoop()
1487     keepGoing = true;
1488     while (true)
1489         {
1490         if (!keepGoing)
1491             {
1492             status("Abort requested");
1493             break;
1494             }
1495         if (!receiveAndProcess())
1496             return false;
1497         }
1498     return true;
1504 //########################################################################
1505 //# SENDING
1506 //########################################################################
1509 bool XmppClient::write(char *fmt, ...)
1511     va_list args;
1512     va_start(args,fmt);
1513     vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
1514     va_end(args) ;
1515     status("SEND: %s", writeBuf);
1516     if (!sock->write((char *)writeBuf))
1517         {
1518         error("Cannot write to socket");
1519         return false;
1520         }
1521     return true;
1529 //########################################################################
1530 //# R E G I S T R A T I O N
1531 //########################################################################
1533 /**
1534  * Perform JEP-077 In-Band Registration.  Performed synchronously after SSL,
1535  * before authentication
1536  */
1537 bool XmppClient::inBandRegistrationNew()
1539     Parser parser;
1541     char *fmt =
1542      "<iq type='get' id='regnew%d'>"
1543          "<query xmlns='jabber:iq:register'/>"
1544          "</iq>\n\n";
1545     if (!write(fmt, msgId++))
1546         return false;
1548     DOMString recbuf = readStanza();
1549     status("RECV reg: %s", recbuf.c_str());
1550     Element *elem = parser.parse(recbuf);
1551     //elem->print();
1553     //# does the entity send the "instructions" tag?
1554     std::vector<Element *> fields = elem->findElements("field");
1555     std::vector<DOMString> fnames;
1556     for (unsigned int i=0; i<fields.size() ; i++)
1557         {
1558         DOMString fname = fields[i]->getAttribute("var");
1559         if (fname == "FORM_TYPE")
1560             continue;
1561         fnames.push_back(fname);
1562         status("field name:%s", fname.c_str());
1563         }
1565     delete elem;
1567     if (fnames.size() == 0)
1568         {
1569         error("server did not offer registration");
1570         return false;
1571         }
1574     fmt =
1575      "<iq type='set' id='regnew%d'>"
1576          "<query xmlns='jabber:iq:register'>"
1577          "<username>%s</username>"
1578          "<password>%s</password>"
1579          "<email/><name/>"
1580          "</query>"
1581          "</iq>\n\n";
1582     if (!write(fmt, msgId++, toXml(username).c_str(),
1583                     toXml(password).c_str() ))
1584         return false;
1587     recbuf = readStanza();
1588     status("RECV reg: %s", recbuf.c_str());
1589     elem = parser.parse(recbuf);
1590     //elem->print();
1592     std::vector<Element *> list = elem->findElements("error");
1593     if (list.size()>0)
1594         {
1595         Element *errElem = list[0];
1596         DOMString code = errElem->getAttribute("code");
1597         DOMString errMsg = "Registration error: ";
1598         if (code == "409")
1599             {
1600             errMsg.append("conflict with existing user name");
1601             }
1602         else if (code == "406")
1603             {
1604             errMsg.append("some registration information was not provided");
1605             }
1606         error((char *)errMsg.c_str());
1607         delete elem;
1608         return false;
1609         }
1611     delete elem;
1613     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1614     evt.setTo(username);
1615     evt.setFrom(host);
1616     dispatchXmppEvent(evt);
1618     return true;
1622 /**
1623  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1624  * See processIq() for response handling.
1625  */
1626 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1628     Parser parser;
1630     //# Let's try it form-style to allow the common old/new password thing
1631     char *fmt =
1632       "<iq type='set' id='regpass%d' from='%s' to='%s'>"
1633       "  <query xmlns='jabber:iq:register'>"
1634       "    <x xmlns='jabber:x:data' type='form'>"
1635       "      <field type='hidden' var='FORM_TYPE'>"
1636       "        <value>jabber:iq:register:changepassword</value>"
1637       "      </field>"
1638       "      <field type='text-single' var='username'>"
1639       "        <value>%s</value>"
1640       "      </field>"
1641       "      <field type='text-private' var='old_password'>"
1642       "        <value>%s</value>"
1643       "      </field>"
1644       "      <field type='text-private' var='password'>"
1645       "        <value>%s</value>"
1646       "      </field>"
1647       "    </x>"
1648       "  </query>"
1649       "</iq>\n\n";
1651     if (!write(fmt, msgId++, jid.c_str(), host.c_str(),
1652              username.c_str(), password.c_str(), newpassword.c_str()))
1653         return false;
1655     return true;
1660 /**
1661  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1662  * See processIq() for response handling.
1663  */
1664 bool XmppClient::inBandRegistrationCancel()
1666     Parser parser;
1668     char *fmt =
1669      "<iq type='set' id='regcancel%d' from='%s'>"
1670           "<query xmlns='jabber:iq:register'><remove/></query>"
1671           "</iq>\n\n";  
1672     if (!write(fmt, msgId++, jid.c_str()))
1673         return false;
1675     return true;
1682 //########################################################################
1683 //# A U T H E N T I C A T E
1684 //########################################################################
1686 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1688     Parser parser;
1690     char *fmt =
1691     "<iq type='get' to='%s' id='auth%d'>"
1692     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1693     "</iq>\n";
1694     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1695         return false;
1697     DOMString recbuf = readStanza();
1698     //printf("iq received: '%s'\n", recbuf.c_str());
1699     Element *elem = parser.parse(recbuf);
1700     //elem->print();
1701     DOMString iqType = elem->getTagAttribute("iq", "type");
1702     //printf("##iqType:%s\n", iqType.c_str());
1703     delete elem;
1705     if (iqType != "result")
1706         {
1707         error("error:server does not allow login");
1708         return false;
1709         }
1711     bool digest = true;
1712     if (digest)
1713         {
1714         //## Digest authentication
1715         DOMString digest = streamId;
1716         digest.append(password);
1717         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
1718         //printf("digest:%s\n", digest.c_str());
1719         fmt =
1720         "<iq type='set' id='auth%d'>"
1721         "<query xmlns='jabber:iq:auth'>"
1722         "<username>%s</username>"
1723         "<digest>%s</digest>"
1724         "<resource>%s</resource>"
1725         "</query>"
1726         "</iq>\n";
1727         if (!write(fmt, msgId++, username.c_str(),
1728                     digest.c_str(), resource.c_str()))
1729             return false;
1730         }
1731     else
1732         {
1734         //## Plaintext authentication
1735         fmt =
1736         "<iq type='set' id='auth%d'>"
1737         "<query xmlns='jabber:iq:auth'>"
1738         "<username>%s</username>"
1739         "<password>%s</password>"
1740         "<resource>%s</resource>"
1741         "</query>"
1742         "</iq>\n";
1743         if (!write(fmt, msgId++, username.c_str(),
1744                    password.c_str(), resource.c_str()))
1745             return false;
1746         }
1748     recbuf = readStanza();
1749     //printf("iq received: '%s'\n", recbuf.c_str());
1750     elem = parser.parse(recbuf);
1751     //elem->print();
1752     iqType = elem->getTagAttribute("iq", "type");
1753     //printf("##iqType:%s\n", iqType.c_str());
1754     delete elem;
1756     if (iqType != "result")
1757         {
1758         error("server does not allow login");
1759         return false;
1760         }
1762     return true;
1766 /**
1767  * Parse a sasl challenge to retrieve all of its key=value pairs
1768  */
1769 static bool saslParse(const DOMString &s, 
1770                       std::map<DOMString, DOMString> &vals)
1773     vals.clear();
1775     int p  = 0;
1776     int siz = s.size();
1778     while (p < siz)
1779         {
1780         DOMString key;
1781         DOMString value;
1782         char ch = '\0';
1784         //# Parse key
1785         while (p<siz)
1786             {
1787             ch = s[p++];
1788             if (ch == '=')
1789                 break;
1790             key.push_back(ch);
1791             }
1793         //No value?
1794         if (ch != '=')
1795             break;
1797         //# Parse value
1798         bool quoted = false;
1799         while (p<siz)
1800             {
1801             ch = s[p++];
1802             if (ch == '"')
1803                 quoted = !quoted;
1804             else if (ch == ',' && !quoted)
1805                 break;
1806             else
1807                 value.push_back(ch);
1808             }
1810         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
1811         vals[key] = value;
1812         if (ch != ',')
1813             break;
1814         }
1816     return true;
1821 /**
1822  * Attempt suthentication using the MD5 SASL mechanism
1823  */
1824 bool XmppClient::saslMd5Authenticate()
1826     Parser parser;
1827     char *fmt =
1828     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1829         "mechanism='DIGEST-MD5'/>\n";
1830     if (!write(fmt))
1831         return false;
1833     DOMString recbuf = readStanza();
1834     status("challenge received: '%s'", recbuf.c_str());
1835     Element *elem = parser.parse(recbuf);
1836     //elem->print();
1837     DOMString b64challenge = elem->getTagValue("challenge");
1838     delete elem;
1840     if (b64challenge.size() < 1)
1841         {
1842         error("login: no SASL challenge offered by server");
1843         return false;
1844         }
1845     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1846     status("md5 challenge:'%s'", challenge.c_str());
1848     std::map<DOMString, DOMString> attrs;
1849     if (!saslParse(challenge, attrs))
1850         {
1851         error("login: error parsing SASL challenge");
1852         return false;
1853         }
1855     DOMString nonce = attrs["nonce"];
1856     if (nonce.size()==0)
1857         {
1858         error("login: no SASL nonce sent by server");
1859         return false;
1860         }
1862     DOMString realm = attrs["realm"];
1863     if (realm.size()==0)
1864         {
1865         error("login: no SASL realm sent by server");
1866         return false;
1867         }
1869     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1871     char idBuf[10];
1872     snprintf(idBuf, 9, "%dsasl", msgId++);
1873     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
1874     DOMString authzid = username; authzid.append("@"); authzid.append(host);
1875     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1877     //## Make A1
1878     Md5 md5;
1879     md5.append(username);
1880     md5.append(":");
1881     md5.append(realm);
1882     md5.append(":");
1883     md5.append(password);
1884     unsigned char a1tmp[16];
1885     md5.finish(a1tmp);
1886     md5.init();
1887     md5.append(a1tmp, 16);
1888     md5.append(":");
1889     md5.append(nonce);
1890     md5.append(":");
1891     md5.append(cnonce);
1892     //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1893     //md5.append(":");
1894     //md5.append(authzid);
1895     md5.append("");
1896     DOMString a1 = md5.finishHex();
1897     status("##a1:'%s'", a1.c_str());
1899     //# Make A2
1900     md5.init();
1901     md5.append("AUTHENTICATE:");
1902     md5.append(digest_uri);
1903     DOMString a2 = md5.finishHex();
1904     status("##a2:'%s'", a2.c_str());
1906     //# Now make the response
1907     md5.init();
1908     md5.append(a1);
1909     md5.append(":");
1910     md5.append(nonce);
1911     md5.append(":");
1912     md5.append("00000001");//nc
1913     md5.append(":");
1914     md5.append(cnonce);
1915     md5.append(":");
1916     md5.append("auth");//qop
1917     md5.append(":");
1918     md5.append(a2);
1919     DOMString response = md5.finishHex();
1921     DOMString resp;
1922     resp.append("username=\""); resp.append(username); resp.append("\",");
1923     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
1924     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
1925     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
1926     resp.append("nc=00000001,qop=auth,");
1927     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1928     //resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
1929     resp.append("response=");   resp.append(response); resp.append(",");
1930     resp.append("charset=utf-8");
1931     status("sending response:'%s'", resp.c_str());
1932     resp = Base64Encoder::encode(resp);
1933     status("base64 response:'%s'", resp.c_str());
1934     fmt =
1935     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1936     if (!write(fmt, resp.c_str()))
1937         return false;
1939     recbuf = readStanza();
1940     status("server says:: '%s'", recbuf.c_str());
1941     elem = parser.parse(recbuf);
1942     //elem->print();
1943     b64challenge = elem->getTagValue("challenge");
1944     delete elem;
1946     if (b64challenge.size() < 1)
1947         {
1948         error("login: no second SASL challenge offered by server");
1949         return false;
1950         }
1952     challenge = Base64Decoder::decodeToString(b64challenge);
1953     status("md5 challenge: '%s'", challenge.c_str());
1955     if (!saslParse(challenge, attrs))
1956         {
1957         error("login: error parsing SASL challenge");
1958         return false;
1959         }
1961     DOMString rspauth = attrs["rspauth"];
1962     if (rspauth.size()==0)
1963         {
1964         error("login: no SASL respauth sent by server\n");
1965         return false;
1966         }
1968     fmt =
1969     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1970     if (!write(fmt))
1971         return false;
1973     recbuf = readStanza();
1974     status("SASL recv: '%s", recbuf.c_str());
1975     elem = parser.parse(recbuf);
1976     //elem->print();
1977     b64challenge = elem->getTagValue("challenge");
1978     bool success = (elem->findElements("success").size() > 0);
1979     delete elem;
1981     return success;
1986 /**
1987  *  Attempt to authentication using the SASL PLAIN mechanism.  This
1988  *  is used most commonly my Google Talk.
1989  */
1990 bool XmppClient::saslPlainAuthenticate()
1992     Parser parser;
1994     DOMString id = username;
1995     //id.append("@");
1996     //id.append(host);
1997     Base64Encoder encoder;
1998     encoder.append('\0');
1999     encoder.append(id);
2000     encoder.append('\0');
2001     encoder.append(password);
2002     DOMString base64Auth = encoder.finish();
2003     //printf("authbuf:%s\n", base64Auth.c_str());
2005     char *fmt =
2006     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
2007     "mechanism='PLAIN'>%s</auth>\n";
2008     if (!write(fmt, base64Auth.c_str()))
2009         return false;
2010     DOMString recbuf = readStanza();
2011     status("challenge received: '%s'", recbuf.c_str());
2012     Element *elem = parser.parse(recbuf);
2014     bool success = (elem->findElements("success").size() > 0);
2015     delete elem;
2017     return success;
2022 /**
2023  * Handshake with SASL, and use one of its offered mechanisms to
2024  * authenticate.
2025  */
2026 bool XmppClient::saslAuthenticate()
2028     Parser parser;
2030     DOMString recbuf = readStanza();
2031     status("RECV: '%s'\n", recbuf.c_str());
2032     Element *elem = parser.parse(recbuf);
2033     //elem->print();
2035     //Check for starttls
2036     bool wantStartTls = false;
2037     if (elem->findElements("starttls").size() > 0)
2038         {
2039         wantStartTls = true;
2040         if (elem->findElements("required").size() > 0)
2041             status("login: STARTTLS required");
2042         else
2043             status("login: STARTTLS available");
2044         }
2046     if (wantStartTls && !sock->getEnableSSL())
2047         {
2048         delete elem;
2049         char *fmt =
2050         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
2051         if (!write(fmt))
2052             return false;
2053         recbuf = readStanza();
2054         status("RECV: '%s'\n", recbuf.c_str());
2055         elem = parser.parse(recbuf);
2056         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
2057             {
2058             error("Server rejected TLS negotiation");
2059             disconnect();
2060             return false;
2061             }
2062         delete elem;
2063         if (!sock->startTls())
2064             {
2065             error("Could not start TLS");
2066             disconnect();
2067             return false;
2068             }
2070         fmt =
2071          "<stream:stream xmlns='jabber:client' "
2072          "xmlns:stream='http://etherx.jabber.org/streams' "
2073          "to='%s' version='1.0'>\n\n";
2074         if (!write(fmt, realm.c_str()))
2075             return false;
2077         recbuf = readStanza();
2078         status("RECVx: '%s'", recbuf.c_str());
2079         recbuf.append("</stream:stream>");
2080         elem = parser.parse(recbuf);
2081         bool success =
2082         (elem->getTagAttribute("stream:stream", "id").size()>0);
2083         if (!success)
2084             {
2085             error("STARTTLS negotiation failed");
2086             disconnect();
2087             return false;
2088             }
2089         delete elem;
2090         recbuf = readStanza();
2091         status("RECV: '%s'\n", recbuf.c_str());
2092         elem = parser.parse(recbuf);
2094         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2095         dispatchXmppEvent(event);
2096         }
2098     //register, if user requests
2099     if (doRegister)
2100         {
2101         if (!inBandRegistrationNew())
2102             return false;
2103         }
2105     //check for sasl authentication mechanisms
2106     std::vector<Element *> elems =
2107                elem->findElements("mechanism");
2108     if (elems.size() < 1)
2109         {
2110         error("login: no SASL mechanism offered by server");
2111         return false;
2112         }
2113     bool md5Found = false;
2114     bool plainFound = false;
2115     for (unsigned int i=0 ; i<elems.size() ; i++)
2116         {
2117         DOMString mech = elems[i]->getValue();
2118         if (mech == "DIGEST-MD5")
2119             {
2120             status("MD5 authentication offered");
2121             md5Found = true;
2122             }
2123         else if (mech == "PLAIN")
2124             {
2125             status("PLAIN authentication offered");
2126             plainFound = true;
2127             }
2128         }
2129     delete elem;
2131     bool success = false;
2132     if (md5Found)
2133         {
2134         success = saslMd5Authenticate();
2135         }
2136     else if (plainFound)
2137         {
2138         success = saslPlainAuthenticate();
2139         }
2140     else
2141         {
2142         error("not able to handle sasl authentication mechanisms");
2143         return false;
2144         }
2146     if (success)
2147         status("###### SASL authentication success\n");
2148     else
2149         error("###### SASL authentication failure\n");
2151     return success;
2159 //########################################################################
2160 //# CONNECT
2161 //########################################################################
2164 /**
2165  * Check if we are connected, and fail with an error if we are not
2166  */
2167 bool XmppClient::checkConnect()
2169     if (!connected)
2170         {
2171         XmppEvent evt(XmppEvent::EVENT_ERROR);
2172         evt.setData("Attempted operation while disconnected");
2173         dispatchXmppEvent(evt);
2174         return false;
2175         }
2176     return true;
2181 /**
2182  * Create an XMPP session with a server.  This
2183  * is basically the transport layer of XMPP.
2184  */
2185 bool XmppClient::createSession()
2188     Parser parser;
2189     if (port==443 || port==5223)
2190         sock->enableSSL(true);
2191     if (!sock->connect(host, port))
2192         {
2193         return false;
2194         }
2196     if (sock->getEnableSSL())
2197         {
2198         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2199         dispatchXmppEvent(event);
2200         }
2202     char *fmt =
2203      "<stream:stream "
2204           "to='%s' "
2205           "xmlns='jabber:client' "
2206           "xmlns:stream='http://etherx.jabber.org/streams' "
2207           "version='1.0'>\n\n";
2208     if (!write(fmt, realm.c_str()))
2209         return false;
2211     DOMString recbuf = readStanza();
2212     //printf("received: '%s'\n", recbuf.c_str());
2213     recbuf.append("</stream:stream>");
2214     Element *elem = parser.parse(recbuf);
2215     //elem->print();
2216     bool useSasl = false;
2217     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2218     //printf("### StreamID: %s\n", streamId.c_str());
2219     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2220     if (streamVersion == "1.0")
2221         useSasl = true;
2223     if (useSasl)
2224         {
2225         if (!saslAuthenticate())
2226             return false;
2227         fmt =
2228           "<stream:stream "
2229           "to='%s' "
2230           "xmlns='jabber:client' "
2231           "xmlns:stream='http://etherx.jabber.org/streams' "
2232           "version='1.0'>\n\n";
2234         if (!write(fmt, realm.c_str()))
2235             return false;
2236         recbuf = readStanza();
2237         recbuf.append("</stream:stream>\n");
2238         //printf("now server says:: '%s'\n", recbuf.c_str());
2239         elem = parser.parse(recbuf);
2240         //elem->print();
2241         delete elem;
2243         recbuf = readStanza();
2244         //printf("now server says:: '%s'\n", recbuf.c_str());
2245         elem = parser.parse(recbuf);
2246         bool hasBind = (elem->findElements("bind").size() > 0);
2247         //elem->print();
2248         delete elem;
2250         if (!hasBind)
2251             {
2252             error("no binding provided by server");
2253             return false;
2254             }
2257         }
2258     else // not SASL
2259         {
2260         if (!iqAuthenticate(streamId))
2261             return false;
2262         }
2265     //### Resource binding
2266     fmt =
2267     "<iq type='set' id='bind%d'>"
2268     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2269     "<resource>%s</resource>"
2270     "</bind></iq>\n";
2271     if (!write(fmt, msgId++, resource.c_str()))
2272         return false;
2274     recbuf = readStanza();
2275     status("bind result: '%s'", recbuf.c_str());
2276     elem = parser.parse(recbuf);
2277     //elem->print();
2278     DOMString bindType = elem->getTagAttribute("iq", "type");
2279     //printf("##bindType:%s\n", bindType.c_str());
2280     delete elem;
2282     if (bindType != "result")
2283         {
2284         error("no binding with server failed");
2285         return false;
2286         }
2288     fmt =
2289     "<iq type='set' id='sess%d'>"
2290     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2291     "</iq>\n";
2292     if (!write(fmt, msgId++))
2293         return false;
2295     recbuf = readStanza();
2296     status("session received: '%s'", recbuf.c_str());
2297     elem = parser.parse(recbuf);
2298     //elem->print();
2299     DOMString sessionType = elem->getTagAttribute("iq", "type");
2300     //printf("##sessionType:%s\n", sessionType.c_str());
2301     delete elem;
2303     if (sessionType != "result")
2304         {
2305         error("no session provided by server");
2306         return false;
2307         }
2309     //printf("########## COOL #########\n");
2310     //Now that we are bound, we have a valid JID
2311     jid = username;
2312     jid.append("@");
2313     jid.append(realm);
2314     jid.append("/");
2315     jid.append(resource);
2317     //We are now done with the synchronous handshaking.  Let's go into
2318     //async mode
2320     fmt =
2321      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2322     if (!write(fmt, msgId++))
2323         return false;
2325     fmt =
2326      "<iq type='get' id='discoItems%d' to='%s'>"
2327      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2328     if (!write(fmt, msgId++, realm.c_str()))
2329         return false;
2331     fmt =
2332     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2333     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2334     if (!write(fmt, msgId++, realm.c_str()))
2335         return false;
2337     fmt =
2338      "<presence/>\n";
2339     if (!write(fmt))
2340         return false;
2342     /*
2343     recbuf = readStanza();
2344     status("stream received: '%s'", recbuf.c_str());
2345     elem = parser.parse(recbuf);
2346     //elem->print();
2347     delete elem;
2348     */
2350     //We are now logged in
2351     status("Connected");
2352     connected = true;
2353     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2354     evt.setData(host);
2355     dispatchXmppEvent(evt);
2356     //Thread::sleep(1000000);
2358     sock->setReceiveTimeout(1000);
2359     ReceiverThread runner(*this);
2360     Thread thread(runner);
2361     thread.start();
2363     return true;
2368 /**
2369  * Public call to connect
2370  */
2371 bool XmppClient::connect()
2373     if (!createSession())
2374         {
2375         disconnect();
2376         return false;
2377         }
2378     return true;
2382 /**
2383  * Public call to connect
2384  */
2385 bool XmppClient::connect(DOMString hostArg, int portArg,
2386                          DOMString usernameArg,
2387                          DOMString passwordArg,
2388                          DOMString resourceArg)
2390     host     = hostArg;
2391     port     = portArg;
2392     password = passwordArg;
2393     resource = resourceArg;
2395     //parse this one
2396     setUsername(usernameArg);
2398     bool ret = connect();
2399     return ret;
2404 /**
2405  * Public call to disconnect
2406  */
2407 bool XmppClient::disconnect()
2409     if (connected)
2410         {
2411         char *fmt =
2412         "<presence from='%s' type='unavailable'/>\n";
2413         write(fmt, jid.c_str());
2414         }
2415     keepGoing = false;
2416     connected = false;
2417     Thread::sleep(2000); //allow receiving thread to quit
2418     sock->disconnect();
2419     roster.clear();
2420     groupChatsClear();
2421     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2422     event.setData(host);
2423     dispatchXmppEvent(event);
2424     return true;
2431 //########################################################################
2432 //# ROSTER
2433 //########################################################################
2435 /**
2436  *  Add an XMPP id to your roster
2437  */
2438 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2439                            const DOMString &otherJid,
2440                            const DOMString &name)
2442     if (!checkConnect())
2443         return false;
2444     char *fmt =
2445     "<iq from='%s' type='set' id='roster_%d'>"
2446     "<query xmlns='jabber:iq:roster'>"
2447     "<item jid='%s' name='%s'><group>%s</group></item>"
2448     "</query></iq>\n";
2449     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
2450          name.c_str(), rosterGroup.c_str()))
2451         {
2452         return false;
2453         }
2454     return true;
2459 /**
2460  *  Delete an XMPP id from your roster.
2461  */
2462 bool XmppClient::rosterDelete(const DOMString &otherJid)
2464     if (!checkConnect())
2465         return false;
2466     char *fmt =
2467     "<iq from='%s' type='set' id='roster_%d'>"
2468     "<query xmlns='jabber:iq:roster'>"
2469     "<item jid='%s' subscription='remove'><group>%s</group></item>"
2470     "</query></iq>\n";
2471     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
2472         {
2473         return false;
2474         }
2475     return true;
2479 /**
2480  *  Comparison method for sort() call below
2481  */
2482 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2484     DOMString s1 = p1.group;
2485     DOMString s2 = p2.group;
2486     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2487         {
2488         int comp = tolower(s1[len]) - tolower(s2[len]);
2489         if (comp)
2490             return (comp<0);
2491         }
2493     s1 = p1.jid;
2494     s2 = p2.jid;
2495     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2496         {
2497         int comp = tolower(s1[len]) - tolower(s2[len]);
2498         if (comp)
2499             return (comp<0);
2500         }
2501     return false;
2506 /**
2507  *  Sort and return the roster that has just been reported by
2508  *  an XmppEvent::EVENT_ROSTER event.
2509  */
2510 std::vector<XmppUser> XmppClient::getRoster()
2512     std::vector<XmppUser> ros = roster;
2513     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2514     return ros;
2518 /**
2519  *
2520  */
2521 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2523     DOMString theShow = show;
2524     if (theShow == "")
2525         theShow = "available";
2527     std::vector<XmppUser>::iterator iter;
2528     for (iter=roster.begin() ; iter != roster.end() ; iter++)
2529         {
2530         if (iter->jid == jid)
2531             iter->show = theShow;
2532         }
2540 //########################################################################
2541 //# CHAT (individual)
2542 //########################################################################
2544 /**
2545  * Send a message to an xmpp jid
2546  */
2547 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2548                          const DOMString &msg)
2550     if (!checkConnect())
2551         return false;
2553     DOMString xmlSubj = toXml(subj);
2554     DOMString xmlMsg  = toXml(msg);
2556     if (xmlSubj.size() > 0)
2557         {
2558         char *fmt =
2559         "<message from='%s' to='%s' type='chat'>"
2560         "<subject>%s</subject><body>%s</body></message>\n";
2561         if (!write(fmt, jid.c_str(), user.c_str(),
2562                 xmlSubj.c_str(), xmlMsg.c_str()))
2563             return false;
2564         }
2565     else
2566         {
2567         char *fmt =
2568         "<message from='%s' to='%s'>"
2569         "<body>%s</body></message>\n";
2570         if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
2571             return false;
2572         }
2573     return true;
2578 /**
2579  *
2580  */
2581 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2583     return message(user, "", msg);
2588 /**
2589  *
2590  */
2591 bool XmppClient::presence(const DOMString &presence)
2593     if (!checkConnect())
2594         return false;
2596     DOMString xmlPres = toXml(presence);
2598     char *fmt =
2599     "<presence from='%s'><show>%s</show></presence>\n";
2600     if (!write(fmt, jid.c_str(), xmlPres.c_str()))
2601         return false;
2602     return true;
2610 //########################################################################
2611 //# GROUP  CHAT
2612 //########################################################################
2614 /**
2615  *
2616  */
2617 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2619     std::vector<XmppGroupChat *>::iterator iter;
2620     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2621         {
2622         if ((*iter)->getGroupJid() == groupJid)
2623             {
2624             error("Group chat '%s' already exists", groupJid.c_str());
2625             return false;
2626             }
2627         }
2628     XmppGroupChat *chat = new XmppGroupChat(groupJid);
2629     groupChats.push_back(chat);
2630     return true;
2635 /**
2636  *
2637  */
2638 void XmppClient::groupChatDelete(const DOMString &groupJid)
2640     std::vector<XmppGroupChat *>::iterator iter;
2641     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2642         {
2643         XmppGroupChat *chat = *iter;
2644         if (chat->getGroupJid() == groupJid)
2645             {
2646             iter = groupChats.erase(iter);
2647             delete chat;
2648             }
2649         else
2650             iter++;
2651         }
2656 /**
2657  *
2658  */
2659 bool XmppClient::groupChatExists(const DOMString &groupJid)
2661     std::vector<XmppGroupChat *>::iterator iter;
2662     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2663         if ((*iter)->getGroupJid() == groupJid)
2664             return true;
2665     return false;
2670 /**
2671  *
2672  */
2673 void XmppClient::groupChatsClear()
2675     std::vector<XmppGroupChat *>::iterator iter;
2676     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2677         delete (*iter);
2678     groupChats.clear();
2684 /**
2685  *
2686  */
2687 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2688                                   const DOMString &nick,
2689                                   const DOMString &jid)
2691     std::vector<XmppGroupChat *>::iterator iter;
2692     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2693         {
2694         if ((*iter)->getGroupJid() == groupJid)
2695             {
2696             (*iter)->userAdd(nick, jid);
2697             }
2698         }
2703 /**
2704  *
2705  */
2706 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2707                                    const DOMString &nick,
2708                                    const DOMString &show)
2710     std::vector<XmppGroupChat *>::iterator iter;
2711     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2712         {
2713         if ((*iter)->getGroupJid() == groupJid)
2714             {
2715             (*iter)->userShow(nick, show);
2716             }
2717         }
2723 /**
2724  *
2725  */
2726 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2727                                      const DOMString &nick)
2729     std::vector<XmppGroupChat *>::iterator iter;
2730     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2731         {
2732         if ((*iter)->getGroupJid() == groupJid)
2733             {
2734             (*iter)->userDelete(nick);
2735             }
2736         }
2741 /**
2742  *  Comparison method for the sort() below
2743  */
2744 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2746     DOMString s1 = p1.nick;
2747     DOMString s2 = p2.nick;
2748     int comp = 0;
2749     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2750         {
2751         comp = tolower(s1[len]) - tolower(s2[len]);
2752         if (comp)
2753             break;
2754         }
2755     return (comp<0);
2760 /**
2761  *  Return the user list for the named group
2762  */
2763 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2764                               const DOMString &groupJid)
2766     if (!checkConnect())
2767         {
2768         std::vector<XmppUser> dummy;
2769         return dummy;
2770         }
2772     std::vector<XmppGroupChat *>::iterator iter;
2773     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2774         {
2775         if ((*iter)->getGroupJid() == groupJid )
2776             {
2777             std::vector<XmppUser> uList = (*iter)->getUserList();
2778             std::sort(uList.begin(), uList.end(), xmppUserCompare);
2779             return uList;
2780             }
2781         }
2782     std::vector<XmppUser> dummy;
2783     return dummy;
2789 /**
2790  *  Try to join a group
2791  */
2792 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2793                                const DOMString &nick,
2794                                const DOMString &pass)
2796     if (!checkConnect())
2797         return false;
2799     DOMString user = nick;
2800     if (user.size()<1)
2801         user = username;
2803     char *fmt =
2804     "<presence to='%s/%s'>"
2805     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2806     if (!write(fmt, groupJid.c_str(), user.c_str()))
2807         return false;
2808     return true;
2814 /**
2815  * Leave a group
2816  */
2817 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2818                                 const DOMString &nick)
2820     if (!checkConnect())
2821         return false;
2823     DOMString user = nick;
2824     if (user.size()<1)
2825         user = username;
2827     char *fmt =
2828     "<presence to='%s/%s' type='unavailable'>"
2829     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2830     if (!write(fmt, groupJid.c_str(), user.c_str()))
2831         return false;
2832     return true;
2838 /**
2839  *  Send a message to a group
2840  */
2841 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2842                                   const DOMString &msg)
2844     if (!checkConnect())
2845         {
2846         return false;
2847         }
2849     DOMString xmlMsg = toXml(msg);
2851     char *fmt =
2852     "<message from='%s' to='%s' type='groupchat'>"
2853     "<body>%s</body></message>\n";
2854     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2855         return false;
2856     return true;
2862 /**
2863  *  Send a message to an individual in a group
2864  */
2865 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2866                                          const DOMString &toNick,
2867                                          const DOMString &msg)
2869     if (!checkConnect())
2870         return false;
2872     DOMString xmlMsg = toXml(msg);
2874     char *fmt =
2875     "<message from='%s' to='%s/%s' type='chat'>"
2876     "<body>%s</body></message>\n";
2877     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2878                toNick.c_str(), xmlMsg.c_str()))
2879         return false;
2880     return true;
2886 /**
2887  *  Change your presence within a group
2888  */
2889 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2890                                    const DOMString &myNick,
2891                                    const DOMString &presence)
2893     if (!checkConnect())
2894         return false;
2896     DOMString user = myNick;
2897     if (user.size()<1)
2898         user = username;
2900     DOMString xmlPresence = toXml(presence);
2902     char *fmt =
2903     "<presence from='%s' to='%s/%s' type='unavailable'>"
2904     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2905     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2906                user.c_str(), xmlPresence.c_str()))
2907         return true;
2908     return true;
2915 //########################################################################
2916 //# S T R E A M S
2917 //########################################################################
2920 /**
2921  *
2922  */
2923 int XmppClient::outputStreamOpen(const DOMString &destId,
2924                                  const DOMString &streamIdArg)
2926     int i;
2927     for (i=0; i<outputStreamCount ; i++)
2928         if (outputStreams[i]->getState() == STREAM_AVAILABLE)
2929             break;
2930     if (i>=outputStreamCount)
2931         {
2932         error("No available output streams");
2933         return -1;
2934         }
2935     int streamNr = i;
2936     XmppStream *outs = outputStreams[streamNr];
2938     outs->setState(STREAM_OPENING);
2940     char buf[32];
2941     snprintf(buf, 31, "inband%d", getMsgId());
2942     DOMString iqId = buf;
2944     DOMString streamId = streamIdArg;
2945     if (streamId.size()<1)
2946         {
2947         snprintf(buf, 31, "stream%d", getMsgId());
2948         DOMString streamId = buf;
2949         }
2950     outs->setIqId(iqId);
2951     outs->setStreamId(streamId);
2952     outs->setPeerId(destId);
2954     char *fmt =
2955     "<iq type='set' from='%s' to='%s' id='%s'>"
2956     "<open sid='%s' block-size='4096'"
2957     " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
2958     if (!write(fmt, jid.c_str(),
2959               destId.c_str(), iqId.c_str(),
2960               streamId.c_str()))
2961         {
2962         outs->reset();
2963         return -1;
2964         }
2966     int state = outs->getState();
2967     for (int tim=0 ; tim<20 ; tim++)
2968         {
2969         if (state == STREAM_OPEN)
2970             break;
2971         else if (state == STREAM_ERROR)
2972             {
2973             printf("ERROR\n");
2974             outs->reset();
2975             return -1;
2976             }
2977         Thread::sleep(1000);
2978         state = outs->getState();
2979         }
2980     if (state != STREAM_OPEN)
2981         {
2982         printf("TIMEOUT ERROR\n");
2983         outs->reset();
2984         return -1;
2985         }
2987     return streamNr;
2990 /**
2991  *
2992  */
2993 int XmppClient::outputStreamWrite(int streamNr,
2994                       const unsigned char *buf, unsigned long len)
2996     XmppStream *outs = outputStreams[streamNr];
2998     unsigned long outLen = 0;
2999     unsigned char *p = (unsigned char *)buf;
3001     while (outLen < len)
3002         {
3003         unsigned long chunksize = 1024;
3004         if (chunksize + outLen > len)
3005             chunksize = len - outLen;
3007         Base64Encoder encoder;
3008         encoder.append(p, chunksize);
3009         DOMString b64data = encoder.finish();
3010         p      += chunksize;
3011         outLen += chunksize;
3013         char *fmt =
3014         "<message from='%s' to='%s' id='msg%d'>"
3015         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
3016         "%s"
3017         "</data>"
3018         "<amp xmlns='http://jabber.org/protocol/amp'>"
3019         "<rule condition='deliver-at' value='stored' action='error'/>"
3020         "<rule condition='match-resource' value='exact' action='error'/>"
3021         "</amp>"
3022         "</message>\n";
3023         if (!write(fmt, jid.c_str(),
3024               outs->getPeerId().c_str(),
3025               getMsgId(),
3026               outs->getStreamId().c_str(),
3027               outs->getSeqNr(),
3028               b64data.c_str()))
3029             {
3030             outs->reset();
3031             return -1;
3032             }
3033         pause(5000);
3034         }
3035     return outLen;
3038 /**
3039  *
3040  */
3041 int XmppClient::outputStreamClose(int streamNr)
3043     XmppStream *outs = outputStreams[streamNr];
3045     char buf[32];
3046     snprintf(buf, 31, "inband%d", getMsgId());
3047     DOMString iqId = buf;
3048     outs->setIqId(iqId);
3050     outs->setState(STREAM_CLOSING);
3051     char *fmt =
3052     "<iq type='set' from='%s' to='%s' id='%s'>"
3053     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
3054     if (!write(fmt, jid.c_str(),
3055                     outs->getPeerId().c_str(),
3056                     iqId.c_str(),
3057                     outs->getStreamId().c_str()))
3058         return false;
3060     int state = outs->getState();
3061     for (int tim=0 ; tim<20 ; tim++)
3062         {
3063         if (state == STREAM_CLOSED)
3064             break;
3065         else if (state == STREAM_ERROR)
3066             {
3067             printf("ERROR\n");
3068             outs->reset();
3069             return -1;
3070             }
3071         Thread::sleep(1000);
3072         state = outs->getState();
3073         }
3074     if (state != STREAM_CLOSED)
3075         {
3076         printf("TIMEOUT ERROR\n");
3077         outs->reset();
3078         return -1;
3079         }
3081     outs->reset();
3082     return 1;
3086 /**
3087  *
3088  */
3089 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
3090                                 const DOMString &iqId)
3092     int i;
3093     for (i=0 ; i<inputStreamCount ; i++)
3094         {
3095         if (inputStreams[i]->getState() == STREAM_AVAILABLE)
3096             break;
3097         }
3098     if (i>=inputStreamCount)
3099         {
3100         error("No available input streams");
3101         return -1;
3102         }
3103     int streamNr = i;
3104     XmppStream *ins = inputStreams[streamNr];
3105     ins->reset();
3106     ins->setPeerId(fromJid);
3107     ins->setState(STREAM_CLOSED);
3108     ins->setStreamId(streamId);
3110     int state = ins->getState();
3111     for (int tim=0 ; tim<20 ; tim++)
3112         {
3113         if (state == STREAM_OPENING)
3114             break;
3115         else if (state == STREAM_ERROR)
3116             {
3117             printf("ERROR\n");
3118             ins->reset();
3119             return -1;
3120             }
3121         Thread::sleep(1000);
3122         state = ins->getState();
3123         }
3124     if (state != STREAM_OPENING)
3125         {
3126         printf("TIMEOUT ERROR\n");
3127         ins->reset();
3128         return -1;
3129         }
3130     char *fmt =
3131     "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3132     if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
3133         {
3134         return -1;
3135         }
3137     ins->setState(STREAM_OPEN);
3138     return streamNr;
3143 /**
3144  *
3145  */
3146 int XmppClient::inputStreamAvailable(int streamNr)
3148     XmppStream *ins = inputStreams[streamNr];
3149     return ins->available();
3152 /**
3153  *
3154  */
3155 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
3157     XmppStream *ins = inputStreams[streamNr];
3158     return ins->read();
3161 /**
3162  *
3163  */
3164 bool XmppClient::inputStreamClosing(int streamNr)
3166     XmppStream *ins = inputStreams[streamNr];
3167     if (ins->getState() == STREAM_CLOSING)
3168         return true;
3169     return false;
3173 /**
3174  *
3175  */
3176 int XmppClient::inputStreamClose(int streamNr)
3178     int ret=1;
3179     XmppStream *ins = inputStreams[streamNr];
3180     if (ins->getState() == STREAM_CLOSING)
3181         {
3182         char *fmt =
3183         "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3184         if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
3185                     ins->getIqId().c_str()))
3186             {
3187             ret = -1;
3188             }
3189         }
3190     ins->reset();
3191     return ret;
3199 //########################################################################
3200 //# FILE   TRANSFERS
3201 //########################################################################
3204 /**
3205  *
3206  */
3207 bool XmppClient::fileSend(const DOMString &destJidArg,
3208                           const DOMString &offeredNameArg,
3209                           const DOMString &fileNameArg,
3210                           const DOMString &descriptionArg)
3212     DOMString destJid     = destJidArg;
3213     DOMString offeredName = offeredNameArg;
3214     DOMString fileName    = fileNameArg;
3215     DOMString description = descriptionArg;
3217     int i;
3218     for (i=0; i<fileSendCount ; i++)
3219         if (fileSends[i]->getState() == STREAM_AVAILABLE)
3220             break;
3221     if (i>=fileSendCount)
3222         {
3223         error("No available file send streams");
3224         return false;
3225         }
3226     int fileSendNr = i;
3227     XmppStream *outf = fileSends[fileSendNr];
3229     outf->setState(STREAM_OPENING);
3231     struct stat finfo;
3232     if (stat(fileName.c_str(), &finfo)<0)
3233         {
3234         error("Cannot stat file '%s' for sending", fileName.c_str());
3235         return false;
3236         }
3237     long fileLen = finfo.st_size;
3238     if (!fileLen > 1000000)
3239         {
3240         error("'%s' too large", fileName.c_str());
3241         return false;
3242         }
3243     if (!S_ISREG(finfo.st_mode))
3244         {
3245         error("'%s' is not a regular file", fileName.c_str());
3246         return false;
3247         }
3248     FILE *f = fopen(fileName.c_str(), "rb");
3249     if (!f)
3250         {
3251         error("cannot open '%s' for sending", fileName.c_str());
3252         return false;
3253         }
3254     unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
3255     if (!sendBuf)
3256         {
3257         error("cannot cannot allocate send buffer for %s", fileName.c_str());
3258         return false;
3259         }
3260     for (long i=0 ; i<fileLen && !feof(f); i++)
3261         {
3262         sendBuf[i] = fgetc(f);
3263         }
3264     fclose(f);
3266     //## get the last path segment from the whole path
3267     if (offeredName.size()<1)
3268         {
3269         int slashPos = -1;
3270         for (unsigned int i=0 ; i<fileName.size() ; i++)
3271             {
3272             int ch = fileName[i];
3273             if (ch == '/' || ch == '\\')
3274                 slashPos = i;
3275             }
3276         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3277             {
3278             offeredName = fileName.substr(slashPos+1,
3279                                           fileName.size()-slashPos-1);
3280             printf("offeredName:%s\n", offeredName.c_str());
3281             }
3282         }
3284     char buf[32];
3285     snprintf(buf, 31, "file%d", getMsgId());
3286     DOMString iqId = buf;
3287     outf->setIqId(iqId);
3289     snprintf(buf, 31, "stream%d", getMsgId());
3290     DOMString streamId = buf;
3291     //outf->setStreamId(streamId);
3293     DOMString hash = Md5::hashHex(sendBuf, fileLen);
3294     printf("Hash:%s\n", hash.c_str());
3296     outf->setPeerId(destJid);
3298     char dtgBuf[81];
3299     struct tm *timeVal = gmtime(&(finfo.st_mtime));
3300     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3302     char *fmt =
3303     "<iq type='set' id='%s' to='%s'>"
3304     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3305       " mime-type='text/plain'"
3306       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3307     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3308           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3309     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3310     "<x xmlns='jabber:x:data' type='form'>"
3311     "<field var='stream-method' type='list-single'>"
3312     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3313     "<option><value>http://jabber.org/protocol/ibb</value></option>"
3314     "</field></x></feature></si></iq>\n";
3315     if (!write(fmt, iqId.c_str(), destJid.c_str(),
3316          streamId.c_str(), offeredName.c_str(), fileLen,
3317          hash.c_str(), dtgBuf, description.c_str()))
3318         {
3319         free(sendBuf);
3320         return false;
3321         }
3323     int state = outf->getState();
3324     for (int tim=0 ; tim<20 ; tim++)
3325         {
3326         printf("##### waiting for open\n");
3327         if (state == STREAM_OPEN)
3328             {
3329             outf->reset();
3330             break;
3331             }
3332         else if (state == STREAM_ERROR)
3333             {
3334             printf("ERROR\n");
3335             outf->reset();
3336             return false;
3337             }
3338         Thread::sleep(1000);
3339         state = outf->getState();
3340         }
3341     if (state != STREAM_OPEN)
3342         {
3343         printf("TIMEOUT ERROR\n");
3344         outf->reset();
3345         return false;
3346         }
3348     //free up this reqource
3349     outf->reset();
3351     int  streamNr = outputStreamOpen(destJid, streamId);
3352     if (streamNr<0)
3353         {
3354         error("cannot open output stream %s", streamId.c_str());
3355         outf->reset();
3356         return false;
3357         }
3359     int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
3361     if (ret<0)
3362         {
3363         }
3365     outputStreamClose(streamNr);
3367     free(sendBuf);
3368     return true;
3372 class FileSendThread : public Thread
3374 public:
3376     FileSendThread(XmppClient &par,
3377                    const DOMString &destJidArg,
3378                    const DOMString &offeredNameArg,
3379                    const DOMString &fileNameArg,
3380                    const DOMString &descriptionArg) : client(par)
3381         {
3382         destJid     = destJidArg;
3383         offeredName = offeredNameArg;
3384         fileName    = fileNameArg;
3385         description = descriptionArg;
3386         }
3388     virtual ~FileSendThread() {}
3390     void run()
3391       {
3392       client.fileSend(destJid, offeredName,
3393                       fileName, description);
3394       }
3396 private:
3398     XmppClient &client;
3399     DOMString destJid;
3400     DOMString offeredName;
3401     DOMString fileName;
3402     DOMString description;
3403 };
3405 /**
3406  *
3407  */
3408 bool XmppClient::fileSendBackground(const DOMString &destJid,
3409                                     const DOMString &offeredName,
3410                                     const DOMString &fileName,
3411                                     const DOMString &description)
3413     FileSendThread thread(*this, destJid, offeredName,
3414                            fileName, description);
3415     thread.start();
3416     return true;
3420 /**
3421  *
3422  */
3423 bool XmppClient::fileReceive(const DOMString &fromJid,
3424                              const DOMString &iqId,
3425                              const DOMString &streamId,
3426                              const DOMString &fileName,
3427                              long  fileSize,
3428                              const DOMString &fileHash)
3430     char *fmt =
3431     "<iq type='result' to='%s' id='%s'>"
3432     "<si xmlns='http://jabber.org/protocol/si'>"
3433     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3434     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3435     "<x xmlns='jabber:x:data' type='submit'>"
3436     "<field var='stream-method'>"
3437     "<value>http://jabber.org/protocol/ibb</value>"
3438     "</field></x></feature></si></iq>\n";
3439     if (!write(fmt, fromJid.c_str(), iqId.c_str()))
3440         {
3441         return false;
3442         }
3444     int streamNr = inputStreamOpen(fromJid, streamId, iqId);
3445     if (streamNr < 0)
3446         {
3447         return false;
3448         }
3451     Md5 md5;
3452     FILE *f = fopen(fileName.c_str(), "wb");
3453     if (!f)
3454         {
3455         return false;
3456         }
3458     while (true)
3459         {
3460         if (inputStreamAvailable(streamNr)<1)
3461             {
3462             if (inputStreamClosing(streamNr))
3463                 break;
3464             pause(100);
3465             continue;
3466             }
3467         std::vector<unsigned char> ret = inputStreamRead(streamNr);
3468         std::vector<unsigned char>::iterator iter;
3469         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3470             {
3471             unsigned char ch = *iter;
3472             md5.append(&ch, 1);
3473             fwrite(&ch, 1, 1, f);
3474             }
3475         }
3477     inputStreamClose(streamNr);
3478     fclose(f);
3480     DOMString hash = md5.finishHex();
3481     printf("received file hash:%s\n", hash.c_str());
3483     return true;
3488 class FileReceiveThread : public Thread
3490 public:
3492     FileReceiveThread(XmppClient &par,
3493                       const DOMString &fromJidArg,
3494                       const DOMString &iqIdArg,
3495                       const DOMString &streamIdArg,
3496                       const DOMString &fileNameArg,
3497                       long  fileSizeArg,
3498                       const DOMString &fileHashArg) : client(par)
3499         {
3500         fromJid     = fromJidArg;
3501         iqId        = iqIdArg;
3502         streamId    = streamIdArg;
3503         fileName    = fileNameArg;
3504         fileSize    = fileSizeArg;
3505         fileHash    = fileHashArg;
3506         }
3508     virtual ~FileReceiveThread() {}
3510     void run()
3511       {
3512       client.fileReceive(fromJid, iqId, streamId,
3513                         fileName, fileSize, fileHash);
3514       }
3516 private:
3518     XmppClient &client;
3519     DOMString fromJid;
3520     DOMString iqId;
3521     DOMString streamId;
3522     DOMString fileName;
3523     long      fileSize;
3524     DOMString fileHash;
3525 };
3527 /**
3528  *
3529  */
3530 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3531                                        const DOMString &iqId,
3532                                        const DOMString &streamId,
3533                                        const DOMString &fileName,
3534                                        long  fileSize,
3535                                        const DOMString &fileHash)
3537     FileReceiveThread thread(*this, fromJid, iqId, streamId,
3538                   fileName, fileSize, fileHash);
3539     thread.start();
3540     return true;
3545 //########################################################################
3546 //# X M P P    G R O U P    C H A T
3547 //########################################################################
3549 /**
3550  *
3551  */
3552 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3554     groupJid = groupJidArg;
3557 /**
3558  *
3559  */
3560 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3562     groupJid = other.groupJid;
3563     userList = other.userList;
3566 /**
3567  *
3568  */
3569 XmppGroupChat::~XmppGroupChat()
3574 /**
3575  *
3576  */
3577 DOMString XmppGroupChat::getGroupJid()
3579     return groupJid;
3583 void XmppGroupChat::userAdd(const DOMString &nick,
3584                             const DOMString &jid)
3586     std::vector<XmppUser>::iterator iter;
3587     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3588         {
3589         if (iter->nick == nick)
3590             return;
3591         }
3592     XmppUser user(jid, nick);
3593     userList.push_back(user);
3596 void XmppGroupChat::userShow(const DOMString &nick,
3597                              const DOMString &show)
3599     DOMString theShow = show;
3600     if (theShow == "")
3601         theShow = "available"; // a join message will now have a show
3602     std::vector<XmppUser>::iterator iter;
3603     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3604         {
3605         if (iter->nick == nick)
3606             iter->show = theShow;
3607         }
3610 void XmppGroupChat::userDelete(const DOMString &nick)
3612     std::vector<XmppUser>::iterator iter;
3613     for (iter= userList.begin() ; iter!=userList.end() ; )
3614         {
3615         if (iter->nick == nick)
3616             iter = userList.erase(iter);
3617         else
3618             iter++;
3619         }
3622 std::vector<XmppUser> XmppGroupChat::getUserList() const
3624     return userList;
3635 } //namespace Pedro
3636 //########################################################################
3637 //# E N D    O F     F I L E
3638 //########################################################################