Code

allow either forms-based or older method of in-band registration
[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 newer "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     //Do we have any fields?
1566     if (fnames.size() == 0)
1567         {
1568         //If no fields, maybe the older method was offered
1569         if (elem->findElements("username").size() == 0 ||
1570             elem->findElements("password").size() == 0)
1571             {
1572             error("server did not offer registration");
1573             delete elem;
1574             return false;
1575             }
1576         }
1578     delete elem;
1580     fmt =
1581      "<iq type='set' id='regnew%d'>"
1582          "<query xmlns='jabber:iq:register'>"
1583          "<username>%s</username>"
1584          "<password>%s</password>"
1585          "<email/><name/>"
1586          "</query>"
1587          "</iq>\n\n";
1588     if (!write(fmt, msgId++, toXml(username).c_str(),
1589                     toXml(password).c_str() ))
1590         return false;
1593     recbuf = readStanza();
1594     status("RECV reg: %s", recbuf.c_str());
1595     elem = parser.parse(recbuf);
1596     //elem->print();
1598     std::vector<Element *> list = elem->findElements("error");
1599     if (list.size()>0)
1600         {
1601         Element *errElem = list[0];
1602         DOMString code = errElem->getAttribute("code");
1603         DOMString errMsg = "Registration error: ";
1604         if (code == "409")
1605             {
1606             errMsg.append("conflict with existing user name");
1607             }
1608         else if (code == "406")
1609             {
1610             errMsg.append("some registration information was not provided");
1611             }
1612         error((char *)errMsg.c_str());
1613         delete elem;
1614         return false;
1615         }
1617     delete elem;
1619     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1620     evt.setTo(username);
1621     evt.setFrom(host);
1622     dispatchXmppEvent(evt);
1624     return true;
1628 /**
1629  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1630  * See processIq() for response handling.
1631  */
1632 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1634     Parser parser;
1636     //# Let's try it form-style to allow the common old/new password thing
1637     char *fmt =
1638       "<iq type='set' id='regpass%d' from='%s' to='%s'>"
1639       "  <query xmlns='jabber:iq:register'>"
1640       "    <x xmlns='jabber:x:data' type='form'>"
1641       "      <field type='hidden' var='FORM_TYPE'>"
1642       "        <value>jabber:iq:register:changepassword</value>"
1643       "      </field>"
1644       "      <field type='text-single' var='username'>"
1645       "        <value>%s</value>"
1646       "      </field>"
1647       "      <field type='text-private' var='old_password'>"
1648       "        <value>%s</value>"
1649       "      </field>"
1650       "      <field type='text-private' var='password'>"
1651       "        <value>%s</value>"
1652       "      </field>"
1653       "    </x>"
1654       "  </query>"
1655       "</iq>\n\n";
1657     if (!write(fmt, msgId++, jid.c_str(), host.c_str(),
1658              username.c_str(), password.c_str(), newpassword.c_str()))
1659         return false;
1661     return true;
1666 /**
1667  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1668  * See processIq() for response handling.
1669  */
1670 bool XmppClient::inBandRegistrationCancel()
1672     Parser parser;
1674     char *fmt =
1675      "<iq type='set' id='regcancel%d' from='%s'>"
1676           "<query xmlns='jabber:iq:register'><remove/></query>"
1677           "</iq>\n\n";  
1678     if (!write(fmt, msgId++, jid.c_str()))
1679         return false;
1681     return true;
1688 //########################################################################
1689 //# A U T H E N T I C A T E
1690 //########################################################################
1692 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1694     Parser parser;
1696     char *fmt =
1697     "<iq type='get' to='%s' id='auth%d'>"
1698     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1699     "</iq>\n";
1700     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1701         return false;
1703     DOMString recbuf = readStanza();
1704     //printf("iq received: '%s'\n", recbuf.c_str());
1705     Element *elem = parser.parse(recbuf);
1706     //elem->print();
1707     DOMString iqType = elem->getTagAttribute("iq", "type");
1708     //printf("##iqType:%s\n", iqType.c_str());
1709     delete elem;
1711     if (iqType != "result")
1712         {
1713         error("error:server does not allow login");
1714         return false;
1715         }
1717     bool digest = true;
1718     if (digest)
1719         {
1720         //## Digest authentication
1721         DOMString digest = streamId;
1722         digest.append(password);
1723         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
1724         //printf("digest:%s\n", digest.c_str());
1725         fmt =
1726         "<iq type='set' id='auth%d'>"
1727         "<query xmlns='jabber:iq:auth'>"
1728         "<username>%s</username>"
1729         "<digest>%s</digest>"
1730         "<resource>%s</resource>"
1731         "</query>"
1732         "</iq>\n";
1733         if (!write(fmt, msgId++, username.c_str(),
1734                     digest.c_str(), resource.c_str()))
1735             return false;
1736         }
1737     else
1738         {
1740         //## Plaintext authentication
1741         fmt =
1742         "<iq type='set' id='auth%d'>"
1743         "<query xmlns='jabber:iq:auth'>"
1744         "<username>%s</username>"
1745         "<password>%s</password>"
1746         "<resource>%s</resource>"
1747         "</query>"
1748         "</iq>\n";
1749         if (!write(fmt, msgId++, username.c_str(),
1750                    password.c_str(), resource.c_str()))
1751             return false;
1752         }
1754     recbuf = readStanza();
1755     //printf("iq received: '%s'\n", recbuf.c_str());
1756     elem = parser.parse(recbuf);
1757     //elem->print();
1758     iqType = elem->getTagAttribute("iq", "type");
1759     //printf("##iqType:%s\n", iqType.c_str());
1760     delete elem;
1762     if (iqType != "result")
1763         {
1764         error("server does not allow login");
1765         return false;
1766         }
1768     return true;
1772 /**
1773  * Parse a sasl challenge to retrieve all of its key=value pairs
1774  */
1775 static bool saslParse(const DOMString &s, 
1776                       std::map<DOMString, DOMString> &vals)
1779     vals.clear();
1781     int p  = 0;
1782     int siz = s.size();
1784     while (p < siz)
1785         {
1786         DOMString key;
1787         DOMString value;
1788         char ch = '\0';
1790         //# Parse key
1791         while (p<siz)
1792             {
1793             ch = s[p++];
1794             if (ch == '=')
1795                 break;
1796             key.push_back(ch);
1797             }
1799         //No value?
1800         if (ch != '=')
1801             break;
1803         //# Parse value
1804         bool quoted = false;
1805         while (p<siz)
1806             {
1807             ch = s[p++];
1808             if (ch == '"')
1809                 quoted = !quoted;
1810             else if (ch == ',' && !quoted)
1811                 break;
1812             else
1813                 value.push_back(ch);
1814             }
1816         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
1817         vals[key] = value;
1818         if (ch != ',')
1819             break;
1820         }
1822     return true;
1827 /**
1828  * Attempt suthentication using the MD5 SASL mechanism
1829  */
1830 bool XmppClient::saslMd5Authenticate()
1832     Parser parser;
1833     char *fmt =
1834     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1835         "mechanism='DIGEST-MD5'/>\n";
1836     if (!write(fmt))
1837         return false;
1839     DOMString recbuf = readStanza();
1840     status("challenge received: '%s'", recbuf.c_str());
1841     Element *elem = parser.parse(recbuf);
1842     //elem->print();
1843     DOMString b64challenge = elem->getTagValue("challenge");
1844     delete elem;
1846     if (b64challenge.size() < 1)
1847         {
1848         error("login: no SASL challenge offered by server");
1849         return false;
1850         }
1851     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1852     status("md5 challenge:'%s'", challenge.c_str());
1854     std::map<DOMString, DOMString> attrs;
1855     if (!saslParse(challenge, attrs))
1856         {
1857         error("login: error parsing SASL challenge");
1858         return false;
1859         }
1861     DOMString nonce = attrs["nonce"];
1862     if (nonce.size()==0)
1863         {
1864         error("login: no SASL nonce sent by server");
1865         return false;
1866         }
1868     DOMString realm = attrs["realm"];
1869     if (realm.size()==0)
1870         {
1871         //Apparently this is not a problem
1872         //error("login: no SASL realm sent by server");
1873         //return false;
1874         }
1876     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1878     char idBuf[10];
1879     snprintf(idBuf, 9, "%dsasl", msgId++);
1880     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
1881     DOMString authzid = username; authzid.append("@"); authzid.append(host);
1882     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1884     //## Make A1
1885     Md5 md5;
1886     md5.append(username);
1887     md5.append(":");
1888     md5.append(realm);
1889     md5.append(":");
1890     md5.append(password);
1891     unsigned char a1tmp[16];
1892     md5.finish(a1tmp);
1893     md5.init();
1894     md5.append(a1tmp, 16);
1895     md5.append(":");
1896     md5.append(nonce);
1897     md5.append(":");
1898     md5.append(cnonce);
1899     //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1900     //md5.append(":");
1901     //md5.append(authzid);
1902     md5.append("");
1903     DOMString a1 = md5.finishHex();
1904     status("##a1:'%s'", a1.c_str());
1906     //# Make A2
1907     md5.init();
1908     md5.append("AUTHENTICATE:");
1909     md5.append(digest_uri);
1910     DOMString a2 = md5.finishHex();
1911     status("##a2:'%s'", a2.c_str());
1913     //# Now make the response
1914     md5.init();
1915     md5.append(a1);
1916     md5.append(":");
1917     md5.append(nonce);
1918     md5.append(":");
1919     md5.append("00000001");//nc
1920     md5.append(":");
1921     md5.append(cnonce);
1922     md5.append(":");
1923     md5.append("auth");//qop
1924     md5.append(":");
1925     md5.append(a2);
1926     DOMString response = md5.finishHex();
1928     DOMString resp;
1929     resp.append("username=\""); resp.append(username); resp.append("\",");
1930     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
1931     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
1932     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
1933     resp.append("nc=00000001,qop=auth,");
1934     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1935     //resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
1936     resp.append("response=");   resp.append(response); resp.append(",");
1937     resp.append("charset=utf-8");
1938     status("sending response:'%s'", resp.c_str());
1939     resp = Base64Encoder::encode(resp);
1940     status("base64 response:'%s'", resp.c_str());
1941     fmt =
1942     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1943     if (!write(fmt, resp.c_str()))
1944         return false;
1946     recbuf = readStanza();
1947     status("server says:: '%s'", recbuf.c_str());
1948     elem = parser.parse(recbuf);
1949     //elem->print();
1950     b64challenge = elem->getTagValue("challenge");
1951     delete elem;
1953     if (b64challenge.size() < 1)
1954         {
1955         error("login: no second SASL challenge offered by server");
1956         return false;
1957         }
1959     challenge = Base64Decoder::decodeToString(b64challenge);
1960     status("md5 challenge: '%s'", challenge.c_str());
1962     if (!saslParse(challenge, attrs))
1963         {
1964         error("login: error parsing SASL challenge");
1965         return false;
1966         }
1968     DOMString rspauth = attrs["rspauth"];
1969     if (rspauth.size()==0)
1970         {
1971         error("login: no SASL respauth sent by server\n");
1972         return false;
1973         }
1975     fmt =
1976     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1977     if (!write(fmt))
1978         return false;
1980     recbuf = readStanza();
1981     status("SASL recv: '%s", recbuf.c_str());
1982     elem = parser.parse(recbuf);
1983     //elem->print();
1984     b64challenge = elem->getTagValue("challenge");
1985     bool success = (elem->findElements("success").size() > 0);
1986     delete elem;
1988     return success;
1993 /**
1994  *  Attempt to authentication using the SASL PLAIN mechanism.  This
1995  *  is used most commonly my Google Talk.
1996  */
1997 bool XmppClient::saslPlainAuthenticate()
1999     Parser parser;
2001     DOMString id = username;
2002     //id.append("@");
2003     //id.append(host);
2004     Base64Encoder encoder;
2005     encoder.append('\0');
2006     encoder.append(id);
2007     encoder.append('\0');
2008     encoder.append(password);
2009     DOMString base64Auth = encoder.finish();
2010     //printf("authbuf:%s\n", base64Auth.c_str());
2012     char *fmt =
2013     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
2014     "mechanism='PLAIN'>%s</auth>\n";
2015     if (!write(fmt, base64Auth.c_str()))
2016         return false;
2017     DOMString recbuf = readStanza();
2018     status("challenge received: '%s'", recbuf.c_str());
2019     Element *elem = parser.parse(recbuf);
2021     bool success = (elem->findElements("success").size() > 0);
2022     delete elem;
2024     return success;
2029 /**
2030  * Handshake with SASL, and use one of its offered mechanisms to
2031  * authenticate.
2032  */
2033 bool XmppClient::saslAuthenticate()
2035     Parser parser;
2037     DOMString recbuf = readStanza();
2038     status("RECV: '%s'\n", recbuf.c_str());
2039     Element *elem = parser.parse(recbuf);
2040     //elem->print();
2042     //Check for starttls
2043     bool wantStartTls = false;
2044     if (elem->findElements("starttls").size() > 0)
2045         {
2046         wantStartTls = true;
2047         if (elem->findElements("required").size() > 0)
2048             status("login: STARTTLS required");
2049         else
2050             status("login: STARTTLS available");
2051         }
2053     if (wantStartTls && !sock->getEnableSSL())
2054         {
2055         delete elem;
2056         char *fmt =
2057         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
2058         if (!write(fmt))
2059             return false;
2060         recbuf = readStanza();
2061         status("RECV: '%s'\n", recbuf.c_str());
2062         elem = parser.parse(recbuf);
2063         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
2064             {
2065             error("Server rejected TLS negotiation");
2066             disconnect();
2067             return false;
2068             }
2069         delete elem;
2070         if (!sock->startTls())
2071             {
2072             error("Could not start TLS");
2073             disconnect();
2074             return false;
2075             }
2077         fmt =
2078          "<stream:stream xmlns='jabber:client' "
2079          "xmlns:stream='http://etherx.jabber.org/streams' "
2080          "to='%s' version='1.0'>\n\n";
2081         if (!write(fmt, realm.c_str()))
2082             return false;
2084         recbuf = readStanza();
2085         status("RECVx: '%s'", recbuf.c_str());
2086         recbuf.append("</stream:stream>");
2087         elem = parser.parse(recbuf);
2088         bool success =
2089         (elem->getTagAttribute("stream:stream", "id").size()>0);
2090         if (!success)
2091             {
2092             error("STARTTLS negotiation failed");
2093             disconnect();
2094             return false;
2095             }
2096         delete elem;
2097         recbuf = readStanza();
2098         status("RECV: '%s'\n", recbuf.c_str());
2099         elem = parser.parse(recbuf);
2101         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2102         dispatchXmppEvent(event);
2103         }
2105     //register, if user requests
2106     if (doRegister)
2107         {
2108         if (!inBandRegistrationNew())
2109             return false;
2110         }
2112     //check for sasl authentication mechanisms
2113     std::vector<Element *> elems =
2114                elem->findElements("mechanism");
2115     if (elems.size() < 1)
2116         {
2117         error("login: no SASL mechanism offered by server");
2118         return false;
2119         }
2120     bool md5Found = false;
2121     bool plainFound = false;
2122     for (unsigned int i=0 ; i<elems.size() ; i++)
2123         {
2124         DOMString mech = elems[i]->getValue();
2125         if (mech == "DIGEST-MD5")
2126             {
2127             status("MD5 authentication offered");
2128             md5Found = true;
2129             }
2130         else if (mech == "PLAIN")
2131             {
2132             status("PLAIN authentication offered");
2133             plainFound = true;
2134             }
2135         }
2136     delete elem;
2138     bool success = false;
2139     if (md5Found)
2140         {
2141         success = saslMd5Authenticate();
2142         }
2143     else if (plainFound)
2144         {
2145         success = saslPlainAuthenticate();
2146         }
2147     else
2148         {
2149         error("not able to handle sasl authentication mechanisms");
2150         return false;
2151         }
2153     if (success)
2154         status("###### SASL authentication success\n");
2155     else
2156         error("###### SASL authentication failure\n");
2158     return success;
2166 //########################################################################
2167 //# CONNECT
2168 //########################################################################
2171 /**
2172  * Check if we are connected, and fail with an error if we are not
2173  */
2174 bool XmppClient::checkConnect()
2176     if (!connected)
2177         {
2178         XmppEvent evt(XmppEvent::EVENT_ERROR);
2179         evt.setData("Attempted operation while disconnected");
2180         dispatchXmppEvent(evt);
2181         return false;
2182         }
2183     return true;
2188 /**
2189  * Create an XMPP session with a server.  This
2190  * is basically the transport layer of XMPP.
2191  */
2192 bool XmppClient::createSession()
2195     Parser parser;
2196     if (port==443 || port==5223)
2197         sock->enableSSL(true);
2198     if (!sock->connect(host, port))
2199         {
2200         return false;
2201         }
2203     if (sock->getEnableSSL())
2204         {
2205         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2206         dispatchXmppEvent(event);
2207         }
2209     char *fmt =
2210      "<stream:stream "
2211           "to='%s' "
2212           "xmlns='jabber:client' "
2213           "xmlns:stream='http://etherx.jabber.org/streams' "
2214           "version='1.0'>\n\n";
2215     if (!write(fmt, realm.c_str()))
2216         return false;
2218     DOMString recbuf = readStanza();
2219     //printf("received: '%s'\n", recbuf.c_str());
2220     recbuf.append("</stream:stream>");
2221     Element *elem = parser.parse(recbuf);
2222     //elem->print();
2223     bool useSasl = false;
2224     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2225     //printf("### StreamID: %s\n", streamId.c_str());
2226     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2227     if (streamVersion == "1.0")
2228         useSasl = true;
2230     if (useSasl)
2231         {
2232         if (!saslAuthenticate())
2233             return false;
2234         fmt =
2235           "<stream:stream "
2236           "to='%s' "
2237           "xmlns='jabber:client' "
2238           "xmlns:stream='http://etherx.jabber.org/streams' "
2239           "version='1.0'>\n\n";
2241         if (!write(fmt, realm.c_str()))
2242             return false;
2243         recbuf = readStanza();
2244         recbuf.append("</stream:stream>\n");
2245         //printf("now server says:: '%s'\n", recbuf.c_str());
2246         elem = parser.parse(recbuf);
2247         //elem->print();
2248         delete elem;
2250         recbuf = readStanza();
2251         //printf("now server says:: '%s'\n", recbuf.c_str());
2252         elem = parser.parse(recbuf);
2253         bool hasBind = (elem->findElements("bind").size() > 0);
2254         //elem->print();
2255         delete elem;
2257         if (!hasBind)
2258             {
2259             error("no binding provided by server");
2260             return false;
2261             }
2264         }
2265     else // not SASL
2266         {
2267         if (!iqAuthenticate(streamId))
2268             return false;
2269         }
2272     //### Resource binding
2273     fmt =
2274     "<iq type='set' id='bind%d'>"
2275     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2276     "<resource>%s</resource>"
2277     "</bind></iq>\n";
2278     if (!write(fmt, msgId++, resource.c_str()))
2279         return false;
2281     recbuf = readStanza();
2282     status("bind result: '%s'", recbuf.c_str());
2283     elem = parser.parse(recbuf);
2284     //elem->print();
2285     DOMString bindType = elem->getTagAttribute("iq", "type");
2286     //printf("##bindType:%s\n", bindType.c_str());
2287     delete elem;
2289     if (bindType != "result")
2290         {
2291         error("no binding with server failed");
2292         return false;
2293         }
2295     fmt =
2296     "<iq type='set' id='sess%d'>"
2297     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2298     "</iq>\n";
2299     if (!write(fmt, msgId++))
2300         return false;
2302     recbuf = readStanza();
2303     status("session received: '%s'", recbuf.c_str());
2304     elem = parser.parse(recbuf);
2305     //elem->print();
2306     DOMString sessionType = elem->getTagAttribute("iq", "type");
2307     //printf("##sessionType:%s\n", sessionType.c_str());
2308     delete elem;
2310     if (sessionType != "result")
2311         {
2312         error("no session provided by server");
2313         return false;
2314         }
2316     //printf("########## COOL #########\n");
2317     //Now that we are bound, we have a valid JID
2318     jid = username;
2319     jid.append("@");
2320     jid.append(realm);
2321     jid.append("/");
2322     jid.append(resource);
2324     //We are now done with the synchronous handshaking.  Let's go into
2325     //async mode
2327     fmt =
2328      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2329     if (!write(fmt, msgId++))
2330         return false;
2332     fmt =
2333      "<iq type='get' id='discoItems%d' to='%s'>"
2334      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2335     if (!write(fmt, msgId++, realm.c_str()))
2336         return false;
2338     fmt =
2339     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2340     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2341     if (!write(fmt, msgId++, realm.c_str()))
2342         return false;
2344     fmt =
2345      "<presence/>\n";
2346     if (!write(fmt))
2347         return false;
2349     /*
2350     recbuf = readStanza();
2351     status("stream received: '%s'", recbuf.c_str());
2352     elem = parser.parse(recbuf);
2353     //elem->print();
2354     delete elem;
2355     */
2357     //We are now logged in
2358     status("Connected");
2359     connected = true;
2360     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2361     evt.setData(host);
2362     dispatchXmppEvent(evt);
2363     //Thread::sleep(1000000);
2365     sock->setReceiveTimeout(1000);
2366     ReceiverThread runner(*this);
2367     Thread thread(runner);
2368     thread.start();
2370     return true;
2375 /**
2376  * Public call to connect
2377  */
2378 bool XmppClient::connect()
2380     if (!createSession())
2381         {
2382         disconnect();
2383         return false;
2384         }
2385     return true;
2389 /**
2390  * Public call to connect
2391  */
2392 bool XmppClient::connect(DOMString hostArg, int portArg,
2393                          DOMString usernameArg,
2394                          DOMString passwordArg,
2395                          DOMString resourceArg)
2397     host     = hostArg;
2398     port     = portArg;
2399     password = passwordArg;
2400     resource = resourceArg;
2402     //parse this one
2403     setUsername(usernameArg);
2405     bool ret = connect();
2406     return ret;
2411 /**
2412  * Public call to disconnect
2413  */
2414 bool XmppClient::disconnect()
2416     if (connected)
2417         {
2418         char *fmt =
2419         "<presence from='%s' type='unavailable'/>\n";
2420         write(fmt, jid.c_str());
2421         }
2422     keepGoing = false;
2423     connected = false;
2424     Thread::sleep(2000); //allow receiving thread to quit
2425     sock->disconnect();
2426     roster.clear();
2427     groupChatsClear();
2428     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2429     event.setData(host);
2430     dispatchXmppEvent(event);
2431     return true;
2438 //########################################################################
2439 //# ROSTER
2440 //########################################################################
2442 /**
2443  *  Add an XMPP id to your roster
2444  */
2445 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2446                            const DOMString &otherJid,
2447                            const DOMString &name)
2449     if (!checkConnect())
2450         return false;
2451     char *fmt =
2452     "<iq from='%s' type='set' id='roster_%d'>"
2453     "<query xmlns='jabber:iq:roster'>"
2454     "<item jid='%s' name='%s'><group>%s</group></item>"
2455     "</query></iq>\n";
2456     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
2457          name.c_str(), rosterGroup.c_str()))
2458         {
2459         return false;
2460         }
2461     return true;
2466 /**
2467  *  Delete an XMPP id from your roster.
2468  */
2469 bool XmppClient::rosterDelete(const DOMString &otherJid)
2471     if (!checkConnect())
2472         return false;
2473     char *fmt =
2474     "<iq from='%s' type='set' id='roster_%d'>"
2475     "<query xmlns='jabber:iq:roster'>"
2476     "<item jid='%s' subscription='remove'><group>%s</group></item>"
2477     "</query></iq>\n";
2478     if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
2479         {
2480         return false;
2481         }
2482     return true;
2486 /**
2487  *  Comparison method for sort() call below
2488  */
2489 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2491     DOMString s1 = p1.group;
2492     DOMString s2 = p2.group;
2493     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2494         {
2495         int comp = tolower(s1[len]) - tolower(s2[len]);
2496         if (comp)
2497             return (comp<0);
2498         }
2500     s1 = p1.jid;
2501     s2 = p2.jid;
2502     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2503         {
2504         int comp = tolower(s1[len]) - tolower(s2[len]);
2505         if (comp)
2506             return (comp<0);
2507         }
2508     return false;
2513 /**
2514  *  Sort and return the roster that has just been reported by
2515  *  an XmppEvent::EVENT_ROSTER event.
2516  */
2517 std::vector<XmppUser> XmppClient::getRoster()
2519     std::vector<XmppUser> ros = roster;
2520     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2521     return ros;
2525 /**
2526  *
2527  */
2528 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2530     DOMString theShow = show;
2531     if (theShow == "")
2532         theShow = "available";
2534     std::vector<XmppUser>::iterator iter;
2535     for (iter=roster.begin() ; iter != roster.end() ; iter++)
2536         {
2537         if (iter->jid == jid)
2538             iter->show = theShow;
2539         }
2547 //########################################################################
2548 //# CHAT (individual)
2549 //########################################################################
2551 /**
2552  * Send a message to an xmpp jid
2553  */
2554 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2555                          const DOMString &msg)
2557     if (!checkConnect())
2558         return false;
2560     DOMString xmlSubj = toXml(subj);
2561     DOMString xmlMsg  = toXml(msg);
2563     if (xmlSubj.size() > 0)
2564         {
2565         char *fmt =
2566         "<message from='%s' to='%s' type='chat'>"
2567         "<subject>%s</subject><body>%s</body></message>\n";
2568         if (!write(fmt, jid.c_str(), user.c_str(),
2569                 xmlSubj.c_str(), xmlMsg.c_str()))
2570             return false;
2571         }
2572     else
2573         {
2574         char *fmt =
2575         "<message from='%s' to='%s'>"
2576         "<body>%s</body></message>\n";
2577         if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
2578             return false;
2579         }
2580     return true;
2585 /**
2586  *
2587  */
2588 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2590     return message(user, "", msg);
2595 /**
2596  *
2597  */
2598 bool XmppClient::presence(const DOMString &presence)
2600     if (!checkConnect())
2601         return false;
2603     DOMString xmlPres = toXml(presence);
2605     char *fmt =
2606     "<presence from='%s'><show>%s</show></presence>\n";
2607     if (!write(fmt, jid.c_str(), xmlPres.c_str()))
2608         return false;
2609     return true;
2617 //########################################################################
2618 //# GROUP  CHAT
2619 //########################################################################
2621 /**
2622  *
2623  */
2624 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2626     std::vector<XmppGroupChat *>::iterator iter;
2627     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2628         {
2629         if ((*iter)->getGroupJid() == groupJid)
2630             {
2631             error("Group chat '%s' already exists", groupJid.c_str());
2632             return false;
2633             }
2634         }
2635     XmppGroupChat *chat = new XmppGroupChat(groupJid);
2636     groupChats.push_back(chat);
2637     return true;
2642 /**
2643  *
2644  */
2645 void XmppClient::groupChatDelete(const DOMString &groupJid)
2647     std::vector<XmppGroupChat *>::iterator iter;
2648     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2649         {
2650         XmppGroupChat *chat = *iter;
2651         if (chat->getGroupJid() == groupJid)
2652             {
2653             iter = groupChats.erase(iter);
2654             delete chat;
2655             }
2656         else
2657             iter++;
2658         }
2663 /**
2664  *
2665  */
2666 bool XmppClient::groupChatExists(const DOMString &groupJid)
2668     std::vector<XmppGroupChat *>::iterator iter;
2669     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2670         if ((*iter)->getGroupJid() == groupJid)
2671             return true;
2672     return false;
2677 /**
2678  *
2679  */
2680 void XmppClient::groupChatsClear()
2682     std::vector<XmppGroupChat *>::iterator iter;
2683     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2684         delete (*iter);
2685     groupChats.clear();
2691 /**
2692  *
2693  */
2694 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2695                                   const DOMString &nick,
2696                                   const DOMString &jid)
2698     std::vector<XmppGroupChat *>::iterator iter;
2699     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2700         {
2701         if ((*iter)->getGroupJid() == groupJid)
2702             {
2703             (*iter)->userAdd(nick, jid);
2704             }
2705         }
2710 /**
2711  *
2712  */
2713 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2714                                    const DOMString &nick,
2715                                    const DOMString &show)
2717     std::vector<XmppGroupChat *>::iterator iter;
2718     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2719         {
2720         if ((*iter)->getGroupJid() == groupJid)
2721             {
2722             (*iter)->userShow(nick, show);
2723             }
2724         }
2730 /**
2731  *
2732  */
2733 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2734                                      const DOMString &nick)
2736     std::vector<XmppGroupChat *>::iterator iter;
2737     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2738         {
2739         if ((*iter)->getGroupJid() == groupJid)
2740             {
2741             (*iter)->userDelete(nick);
2742             }
2743         }
2748 /**
2749  *  Comparison method for the sort() below
2750  */
2751 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2753     DOMString s1 = p1.nick;
2754     DOMString s2 = p2.nick;
2755     int comp = 0;
2756     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2757         {
2758         comp = tolower(s1[len]) - tolower(s2[len]);
2759         if (comp)
2760             break;
2761         }
2762     return (comp<0);
2767 /**
2768  *  Return the user list for the named group
2769  */
2770 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2771                               const DOMString &groupJid)
2773     if (!checkConnect())
2774         {
2775         std::vector<XmppUser> dummy;
2776         return dummy;
2777         }
2779     std::vector<XmppGroupChat *>::iterator iter;
2780     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2781         {
2782         if ((*iter)->getGroupJid() == groupJid )
2783             {
2784             std::vector<XmppUser> uList = (*iter)->getUserList();
2785             std::sort(uList.begin(), uList.end(), xmppUserCompare);
2786             return uList;
2787             }
2788         }
2789     std::vector<XmppUser> dummy;
2790     return dummy;
2796 /**
2797  *  Try to join a group
2798  */
2799 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2800                                const DOMString &nick,
2801                                const DOMString &pass)
2803     if (!checkConnect())
2804         return false;
2806     DOMString user = nick;
2807     if (user.size()<1)
2808         user = username;
2810     char *fmt =
2811     "<presence to='%s/%s'>"
2812     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2813     if (!write(fmt, groupJid.c_str(), user.c_str()))
2814         return false;
2815     return true;
2821 /**
2822  * Leave a group
2823  */
2824 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2825                                 const DOMString &nick)
2827     if (!checkConnect())
2828         return false;
2830     DOMString user = nick;
2831     if (user.size()<1)
2832         user = username;
2834     char *fmt =
2835     "<presence to='%s/%s' type='unavailable'>"
2836     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2837     if (!write(fmt, groupJid.c_str(), user.c_str()))
2838         return false;
2839     return true;
2845 /**
2846  *  Send a message to a group
2847  */
2848 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2849                                   const DOMString &msg)
2851     if (!checkConnect())
2852         {
2853         return false;
2854         }
2856     DOMString xmlMsg = toXml(msg);
2858     char *fmt =
2859     "<message from='%s' to='%s' type='groupchat'>"
2860     "<body>%s</body></message>\n";
2861     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2862         return false;
2863     return true;
2869 /**
2870  *  Send a message to an individual in a group
2871  */
2872 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2873                                          const DOMString &toNick,
2874                                          const DOMString &msg)
2876     if (!checkConnect())
2877         return false;
2879     DOMString xmlMsg = toXml(msg);
2881     char *fmt =
2882     "<message from='%s' to='%s/%s' type='chat'>"
2883     "<body>%s</body></message>\n";
2884     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2885                toNick.c_str(), xmlMsg.c_str()))
2886         return false;
2887     return true;
2893 /**
2894  *  Change your presence within a group
2895  */
2896 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2897                                    const DOMString &myNick,
2898                                    const DOMString &presence)
2900     if (!checkConnect())
2901         return false;
2903     DOMString user = myNick;
2904     if (user.size()<1)
2905         user = username;
2907     DOMString xmlPresence = toXml(presence);
2909     char *fmt =
2910     "<presence from='%s' to='%s/%s' type='unavailable'>"
2911     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2912     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2913                user.c_str(), xmlPresence.c_str()))
2914         return true;
2915     return true;
2922 //########################################################################
2923 //# S T R E A M S
2924 //########################################################################
2927 /**
2928  *
2929  */
2930 int XmppClient::outputStreamOpen(const DOMString &destId,
2931                                  const DOMString &streamIdArg)
2933     int i;
2934     for (i=0; i<outputStreamCount ; i++)
2935         if (outputStreams[i]->getState() == STREAM_AVAILABLE)
2936             break;
2937     if (i>=outputStreamCount)
2938         {
2939         error("No available output streams");
2940         return -1;
2941         }
2942     int streamNr = i;
2943     XmppStream *outs = outputStreams[streamNr];
2945     outs->setState(STREAM_OPENING);
2947     char buf[32];
2948     snprintf(buf, 31, "inband%d", getMsgId());
2949     DOMString iqId = buf;
2951     DOMString streamId = streamIdArg;
2952     if (streamId.size()<1)
2953         {
2954         snprintf(buf, 31, "stream%d", getMsgId());
2955         DOMString streamId = buf;
2956         }
2957     outs->setIqId(iqId);
2958     outs->setStreamId(streamId);
2959     outs->setPeerId(destId);
2961     char *fmt =
2962     "<iq type='set' from='%s' to='%s' id='%s'>"
2963     "<open sid='%s' block-size='4096'"
2964     " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
2965     if (!write(fmt, jid.c_str(),
2966               destId.c_str(), iqId.c_str(),
2967               streamId.c_str()))
2968         {
2969         outs->reset();
2970         return -1;
2971         }
2973     int state = outs->getState();
2974     for (int tim=0 ; tim<20 ; tim++)
2975         {
2976         if (state == STREAM_OPEN)
2977             break;
2978         else if (state == STREAM_ERROR)
2979             {
2980             printf("ERROR\n");
2981             outs->reset();
2982             return -1;
2983             }
2984         Thread::sleep(1000);
2985         state = outs->getState();
2986         }
2987     if (state != STREAM_OPEN)
2988         {
2989         printf("TIMEOUT ERROR\n");
2990         outs->reset();
2991         return -1;
2992         }
2994     return streamNr;
2997 /**
2998  *
2999  */
3000 int XmppClient::outputStreamWrite(int streamNr,
3001                       const unsigned char *buf, unsigned long len)
3003     XmppStream *outs = outputStreams[streamNr];
3005     unsigned long outLen = 0;
3006     unsigned char *p = (unsigned char *)buf;
3008     while (outLen < len)
3009         {
3010         unsigned long chunksize = 1024;
3011         if (chunksize + outLen > len)
3012             chunksize = len - outLen;
3014         Base64Encoder encoder;
3015         encoder.append(p, chunksize);
3016         DOMString b64data = encoder.finish();
3017         p      += chunksize;
3018         outLen += chunksize;
3020         char *fmt =
3021         "<message from='%s' to='%s' id='msg%d'>"
3022         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
3023         "%s"
3024         "</data>"
3025         "<amp xmlns='http://jabber.org/protocol/amp'>"
3026         "<rule condition='deliver-at' value='stored' action='error'/>"
3027         "<rule condition='match-resource' value='exact' action='error'/>"
3028         "</amp>"
3029         "</message>\n";
3030         if (!write(fmt, jid.c_str(),
3031               outs->getPeerId().c_str(),
3032               getMsgId(),
3033               outs->getStreamId().c_str(),
3034               outs->getSeqNr(),
3035               b64data.c_str()))
3036             {
3037             outs->reset();
3038             return -1;
3039             }
3040         pause(5000);
3041         }
3042     return outLen;
3045 /**
3046  *
3047  */
3048 int XmppClient::outputStreamClose(int streamNr)
3050     XmppStream *outs = outputStreams[streamNr];
3052     char buf[32];
3053     snprintf(buf, 31, "inband%d", getMsgId());
3054     DOMString iqId = buf;
3055     outs->setIqId(iqId);
3057     outs->setState(STREAM_CLOSING);
3058     char *fmt =
3059     "<iq type='set' from='%s' to='%s' id='%s'>"
3060     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
3061     if (!write(fmt, jid.c_str(),
3062                     outs->getPeerId().c_str(),
3063                     iqId.c_str(),
3064                     outs->getStreamId().c_str()))
3065         return false;
3067     int state = outs->getState();
3068     for (int tim=0 ; tim<20 ; tim++)
3069         {
3070         if (state == STREAM_CLOSED)
3071             break;
3072         else if (state == STREAM_ERROR)
3073             {
3074             printf("ERROR\n");
3075             outs->reset();
3076             return -1;
3077             }
3078         Thread::sleep(1000);
3079         state = outs->getState();
3080         }
3081     if (state != STREAM_CLOSED)
3082         {
3083         printf("TIMEOUT ERROR\n");
3084         outs->reset();
3085         return -1;
3086         }
3088     outs->reset();
3089     return 1;
3093 /**
3094  *
3095  */
3096 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
3097                                 const DOMString &iqId)
3099     int i;
3100     for (i=0 ; i<inputStreamCount ; i++)
3101         {
3102         if (inputStreams[i]->getState() == STREAM_AVAILABLE)
3103             break;
3104         }
3105     if (i>=inputStreamCount)
3106         {
3107         error("No available input streams");
3108         return -1;
3109         }
3110     int streamNr = i;
3111     XmppStream *ins = inputStreams[streamNr];
3112     ins->reset();
3113     ins->setPeerId(fromJid);
3114     ins->setState(STREAM_CLOSED);
3115     ins->setStreamId(streamId);
3117     int state = ins->getState();
3118     for (int tim=0 ; tim<20 ; tim++)
3119         {
3120         if (state == STREAM_OPENING)
3121             break;
3122         else if (state == STREAM_ERROR)
3123             {
3124             printf("ERROR\n");
3125             ins->reset();
3126             return -1;
3127             }
3128         Thread::sleep(1000);
3129         state = ins->getState();
3130         }
3131     if (state != STREAM_OPENING)
3132         {
3133         printf("TIMEOUT ERROR\n");
3134         ins->reset();
3135         return -1;
3136         }
3137     char *fmt =
3138     "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3139     if (!write(fmt, jid.c_str(),  fromJid.c_str(), ins->getIqId().c_str()))
3140         {
3141         return -1;
3142         }
3144     ins->setState(STREAM_OPEN);
3145     return streamNr;
3150 /**
3151  *
3152  */
3153 int XmppClient::inputStreamAvailable(int streamNr)
3155     XmppStream *ins = inputStreams[streamNr];
3156     return ins->available();
3159 /**
3160  *
3161  */
3162 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
3164     XmppStream *ins = inputStreams[streamNr];
3165     return ins->read();
3168 /**
3169  *
3170  */
3171 bool XmppClient::inputStreamClosing(int streamNr)
3173     XmppStream *ins = inputStreams[streamNr];
3174     if (ins->getState() == STREAM_CLOSING)
3175         return true;
3176     return false;
3180 /**
3181  *
3182  */
3183 int XmppClient::inputStreamClose(int streamNr)
3185     int ret=1;
3186     XmppStream *ins = inputStreams[streamNr];
3187     if (ins->getState() == STREAM_CLOSING)
3188         {
3189         char *fmt =
3190         "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3191         if (!write(fmt, jid.c_str(),  ins->getPeerId().c_str(),
3192                     ins->getIqId().c_str()))
3193             {
3194             ret = -1;
3195             }
3196         }
3197     ins->reset();
3198     return ret;
3206 //########################################################################
3207 //# FILE   TRANSFERS
3208 //########################################################################
3211 /**
3212  *
3213  */
3214 bool XmppClient::fileSend(const DOMString &destJidArg,
3215                           const DOMString &offeredNameArg,
3216                           const DOMString &fileNameArg,
3217                           const DOMString &descriptionArg)
3219     DOMString destJid     = destJidArg;
3220     DOMString offeredName = offeredNameArg;
3221     DOMString fileName    = fileNameArg;
3222     DOMString description = descriptionArg;
3224     int i;
3225     for (i=0; i<fileSendCount ; i++)
3226         if (fileSends[i]->getState() == STREAM_AVAILABLE)
3227             break;
3228     if (i>=fileSendCount)
3229         {
3230         error("No available file send streams");
3231         return false;
3232         }
3233     int fileSendNr = i;
3234     XmppStream *outf = fileSends[fileSendNr];
3236     outf->setState(STREAM_OPENING);
3238     struct stat finfo;
3239     if (stat(fileName.c_str(), &finfo)<0)
3240         {
3241         error("Cannot stat file '%s' for sending", fileName.c_str());
3242         return false;
3243         }
3244     long fileLen = finfo.st_size;
3245     if (!fileLen > 1000000)
3246         {
3247         error("'%s' too large", fileName.c_str());
3248         return false;
3249         }
3250     if (!S_ISREG(finfo.st_mode))
3251         {
3252         error("'%s' is not a regular file", fileName.c_str());
3253         return false;
3254         }
3255     FILE *f = fopen(fileName.c_str(), "rb");
3256     if (!f)
3257         {
3258         error("cannot open '%s' for sending", fileName.c_str());
3259         return false;
3260         }
3261     unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
3262     if (!sendBuf)
3263         {
3264         error("cannot cannot allocate send buffer for %s", fileName.c_str());
3265         return false;
3266         }
3267     for (long i=0 ; i<fileLen && !feof(f); i++)
3268         {
3269         sendBuf[i] = fgetc(f);
3270         }
3271     fclose(f);
3273     //## get the last path segment from the whole path
3274     if (offeredName.size()<1)
3275         {
3276         int slashPos = -1;
3277         for (unsigned int i=0 ; i<fileName.size() ; i++)
3278             {
3279             int ch = fileName[i];
3280             if (ch == '/' || ch == '\\')
3281                 slashPos = i;
3282             }
3283         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3284             {
3285             offeredName = fileName.substr(slashPos+1,
3286                                           fileName.size()-slashPos-1);
3287             printf("offeredName:%s\n", offeredName.c_str());
3288             }
3289         }
3291     char buf[32];
3292     snprintf(buf, 31, "file%d", getMsgId());
3293     DOMString iqId = buf;
3294     outf->setIqId(iqId);
3296     snprintf(buf, 31, "stream%d", getMsgId());
3297     DOMString streamId = buf;
3298     //outf->setStreamId(streamId);
3300     DOMString hash = Md5::hashHex(sendBuf, fileLen);
3301     printf("Hash:%s\n", hash.c_str());
3303     outf->setPeerId(destJid);
3305     char dtgBuf[81];
3306     struct tm *timeVal = gmtime(&(finfo.st_mtime));
3307     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3309     char *fmt =
3310     "<iq type='set' id='%s' to='%s'>"
3311     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3312       " mime-type='text/plain'"
3313       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3314     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3315           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3316     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3317     "<x xmlns='jabber:x:data' type='form'>"
3318     "<field var='stream-method' type='list-single'>"
3319     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3320     "<option><value>http://jabber.org/protocol/ibb</value></option>"
3321     "</field></x></feature></si></iq>\n";
3322     if (!write(fmt, iqId.c_str(), destJid.c_str(),
3323          streamId.c_str(), offeredName.c_str(), fileLen,
3324          hash.c_str(), dtgBuf, description.c_str()))
3325         {
3326         free(sendBuf);
3327         return false;
3328         }
3330     int state = outf->getState();
3331     for (int tim=0 ; tim<20 ; tim++)
3332         {
3333         printf("##### waiting for open\n");
3334         if (state == STREAM_OPEN)
3335             {
3336             outf->reset();
3337             break;
3338             }
3339         else if (state == STREAM_ERROR)
3340             {
3341             printf("ERROR\n");
3342             outf->reset();
3343             return false;
3344             }
3345         Thread::sleep(1000);
3346         state = outf->getState();
3347         }
3348     if (state != STREAM_OPEN)
3349         {
3350         printf("TIMEOUT ERROR\n");
3351         outf->reset();
3352         return false;
3353         }
3355     //free up this reqource
3356     outf->reset();
3358     int  streamNr = outputStreamOpen(destJid, streamId);
3359     if (streamNr<0)
3360         {
3361         error("cannot open output stream %s", streamId.c_str());
3362         outf->reset();
3363         return false;
3364         }
3366     int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
3368     if (ret<0)
3369         {
3370         }
3372     outputStreamClose(streamNr);
3374     free(sendBuf);
3375     return true;
3379 class FileSendThread : public Thread
3381 public:
3383     FileSendThread(XmppClient &par,
3384                    const DOMString &destJidArg,
3385                    const DOMString &offeredNameArg,
3386                    const DOMString &fileNameArg,
3387                    const DOMString &descriptionArg) : client(par)
3388         {
3389         destJid     = destJidArg;
3390         offeredName = offeredNameArg;
3391         fileName    = fileNameArg;
3392         description = descriptionArg;
3393         }
3395     virtual ~FileSendThread() {}
3397     void run()
3398       {
3399       client.fileSend(destJid, offeredName,
3400                       fileName, description);
3401       }
3403 private:
3405     XmppClient &client;
3406     DOMString destJid;
3407     DOMString offeredName;
3408     DOMString fileName;
3409     DOMString description;
3410 };
3412 /**
3413  *
3414  */
3415 bool XmppClient::fileSendBackground(const DOMString &destJid,
3416                                     const DOMString &offeredName,
3417                                     const DOMString &fileName,
3418                                     const DOMString &description)
3420     FileSendThread thread(*this, destJid, offeredName,
3421                            fileName, description);
3422     thread.start();
3423     return true;
3427 /**
3428  *
3429  */
3430 bool XmppClient::fileReceive(const DOMString &fromJid,
3431                              const DOMString &iqId,
3432                              const DOMString &streamId,
3433                              const DOMString &fileName,
3434                              long  fileSize,
3435                              const DOMString &fileHash)
3437     char *fmt =
3438     "<iq type='result' to='%s' id='%s'>"
3439     "<si xmlns='http://jabber.org/protocol/si'>"
3440     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3441     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3442     "<x xmlns='jabber:x:data' type='submit'>"
3443     "<field var='stream-method'>"
3444     "<value>http://jabber.org/protocol/ibb</value>"
3445     "</field></x></feature></si></iq>\n";
3446     if (!write(fmt, fromJid.c_str(), iqId.c_str()))
3447         {
3448         return false;
3449         }
3451     int streamNr = inputStreamOpen(fromJid, streamId, iqId);
3452     if (streamNr < 0)
3453         {
3454         return false;
3455         }
3458     Md5 md5;
3459     FILE *f = fopen(fileName.c_str(), "wb");
3460     if (!f)
3461         {
3462         return false;
3463         }
3465     while (true)
3466         {
3467         if (inputStreamAvailable(streamNr)<1)
3468             {
3469             if (inputStreamClosing(streamNr))
3470                 break;
3471             pause(100);
3472             continue;
3473             }
3474         std::vector<unsigned char> ret = inputStreamRead(streamNr);
3475         std::vector<unsigned char>::iterator iter;
3476         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3477             {
3478             unsigned char ch = *iter;
3479             md5.append(&ch, 1);
3480             fwrite(&ch, 1, 1, f);
3481             }
3482         }
3484     inputStreamClose(streamNr);
3485     fclose(f);
3487     DOMString hash = md5.finishHex();
3488     printf("received file hash:%s\n", hash.c_str());
3490     return true;
3495 class FileReceiveThread : public Thread
3497 public:
3499     FileReceiveThread(XmppClient &par,
3500                       const DOMString &fromJidArg,
3501                       const DOMString &iqIdArg,
3502                       const DOMString &streamIdArg,
3503                       const DOMString &fileNameArg,
3504                       long  fileSizeArg,
3505                       const DOMString &fileHashArg) : client(par)
3506         {
3507         fromJid     = fromJidArg;
3508         iqId        = iqIdArg;
3509         streamId    = streamIdArg;
3510         fileName    = fileNameArg;
3511         fileSize    = fileSizeArg;
3512         fileHash    = fileHashArg;
3513         }
3515     virtual ~FileReceiveThread() {}
3517     void run()
3518       {
3519       client.fileReceive(fromJid, iqId, streamId,
3520                         fileName, fileSize, fileHash);
3521       }
3523 private:
3525     XmppClient &client;
3526     DOMString fromJid;
3527     DOMString iqId;
3528     DOMString streamId;
3529     DOMString fileName;
3530     long      fileSize;
3531     DOMString fileHash;
3532 };
3534 /**
3535  *
3536  */
3537 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3538                                        const DOMString &iqId,
3539                                        const DOMString &streamId,
3540                                        const DOMString &fileName,
3541                                        long  fileSize,
3542                                        const DOMString &fileHash)
3544     FileReceiveThread thread(*this, fromJid, iqId, streamId,
3545                   fileName, fileSize, fileHash);
3546     thread.start();
3547     return true;
3552 //########################################################################
3553 //# X M P P    G R O U P    C H A T
3554 //########################################################################
3556 /**
3557  *
3558  */
3559 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3561     groupJid = groupJidArg;
3564 /**
3565  *
3566  */
3567 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3569     groupJid = other.groupJid;
3570     userList = other.userList;
3573 /**
3574  *
3575  */
3576 XmppGroupChat::~XmppGroupChat()
3581 /**
3582  *
3583  */
3584 DOMString XmppGroupChat::getGroupJid()
3586     return groupJid;
3590 void XmppGroupChat::userAdd(const DOMString &nick,
3591                             const DOMString &jid)
3593     std::vector<XmppUser>::iterator iter;
3594     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3595         {
3596         if (iter->nick == nick)
3597             return;
3598         }
3599     XmppUser user(jid, nick);
3600     userList.push_back(user);
3603 void XmppGroupChat::userShow(const DOMString &nick,
3604                              const DOMString &show)
3606     DOMString theShow = show;
3607     if (theShow == "")
3608         theShow = "available"; // a join message will now have a show
3609     std::vector<XmppUser>::iterator iter;
3610     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3611         {
3612         if (iter->nick == nick)
3613             iter->show = theShow;
3614         }
3617 void XmppGroupChat::userDelete(const DOMString &nick)
3619     std::vector<XmppUser>::iterator iter;
3620     for (iter= userList.begin() ; iter!=userList.end() ; )
3621         {
3622         if (iter->nick == nick)
3623             iter = userList.erase(iter);
3624         else
3625             iter++;
3626         }
3629 std::vector<XmppUser> XmppGroupChat::getUserList() const
3631     return userList;
3642 } //namespace Pedro
3643 //########################################################################
3644 //# E N D    O F     F I L E
3645 //########################################################################