Code

4082881875bbf237dafebcf3430647dd3477e419
[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()
413         { reset(); }
415     /**
416      *
417      */
418     XmppStream(const XmppStream &other)
419         { assign(other); }
421     /**
422      *
423      */
424     XmppStream &operator=(const XmppStream &other)
425         { assign(other); return *this; }
427     /**
428      *
429      */
430     virtual ~XmppStream()
431         {}
433     /**
434      *
435      */
436     virtual void reset()
437         {
438         state     = XmppClient::STREAM_AVAILABLE;
439         seqNr     = 0;
440         messageId = "";
441         sourceId  = "";
442         data.clear();
443         }
445     /**
446      *
447      */
448     virtual int getState()
449         { return state; }
451     /**
452      *
453      */
454     virtual void setState(int val)
455         { state = val; }
457     /**
458      *
459      */
460     virtual DOMString getStreamId()
461         { return streamId; }
463     /**
464      *
465      */
466     void setStreamId(const DOMString &val)
467         { streamId = val; }
469     /**
470      *
471      */
472     virtual DOMString getMessageId()
473         { return messageId; }
475     /**
476      *
477      */
478     void setMessageId(const DOMString &val)
479         { messageId = val; }
481     /**
482      *
483      */
484     virtual int getSeqNr()
485         {
486         seqNr++;
487         if (seqNr >= 65535)
488             seqNr = 0;
489         return seqNr;
490         }
492     /**
493      *
494      */
495     virtual DOMString getPeerId()
496         { return sourceId; }
498     /**
499      *
500      */
501     virtual void setPeerId(const DOMString &val)
502         { sourceId = val; }
504     /**
505      *
506      */
507     int available()
508         { return data.size(); }
510     /**
511      *
512      */
513     void receiveData(std::vector<unsigned char> &newData)
514         {
515         std::vector<unsigned char>::iterator iter;
516         for (iter=newData.begin() ; iter!=newData.end() ; iter++)
517             data.push_back(*iter);
518         }
520     /**
521      *
522      */
523     std::vector<unsigned char> read()
524         {
525         if (state != XmppClient::STREAM_OPEN)
526             {
527             std::vector<unsigned char>dummy;
528             return dummy;
529             }
530         std::vector<unsigned char> ret = data;
531         data.clear();
532         return ret;
533         }
535 private:
537     void assign(const XmppStream &other)
538        {
539        streamId  = other.streamId;
540        messageId = other.messageId;
541        sourceId  = other.sourceId;
542        state     = other.state;
543        seqNr     = other.seqNr;
544        data      = other.data;
545        }
548     DOMString streamId;
550     DOMString messageId;
552     DOMString sourceId;
554     int state;
556     long seqNr;
558     std::vector<unsigned char> data;
559 };
570 //########################################################################
571 //########################################################################
572 //# X M P P    C L I E N T
573 //########################################################################
574 //########################################################################
576 class ReceiverThread : public Runnable
578 public:
580     ReceiverThread(XmppClient &par) : client(par) {}
582     virtual ~ReceiverThread() {}
584     void run()
585       { client.receiveAndProcessLoop(); }
587 private:
589     XmppClient &client;
590 };
596 //########################################################################
597 //# CONSTRUCTORS
598 //########################################################################
600 XmppClient::XmppClient()
602     init();
606 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
608     init();
609     assign(other);
612 void XmppClient::assign(const XmppClient &other)
614     msgId         = other.msgId;
615     host          = other.host;
616     realm         = other.realm;
617     port          = other.port;
618     username      = other.username;
619     password      = other.password;
620     resource      = other.resource;
621     connected     = other.connected;
622     doRegister    = other.doRegister;
623     groupChats    = other.groupChats;
624     streamPacket  = other.streamPacket;
628 void XmppClient::init()
630     sock          = new TcpSocket();
631     msgId         = 0;
632     connected     = false;
633     doRegister    = false;
634     streamPacket  = "message";
638 XmppClient::~XmppClient()
640     disconnect();
641     delete sock;
642     std::map<DOMString, XmppStream *>::iterator iter;
643     for (iter = outputStreams.begin(); iter!=outputStreams.end() ; iter++)
644         delete iter->second;
645     for (iter = inputStreams.begin(); iter!=inputStreams.end() ; iter++)
646         delete iter->second;
647     for (iter = fileSends.begin(); iter!=fileSends.end() ; iter++)
648         delete iter->second;
649     groupChatsClear();
657 //########################################################################
658 //# UTILILY
659 //########################################################################
661 /**
662  *
663  */
664 bool XmppClient::pause(unsigned long millis)
666     Thread::sleep(millis);
667     return true;
671 static int strIndex(const DOMString &str, char *key)
673     unsigned int p = str.find(key);
674     if (p == str.npos)
675         return -1;
676     return p;
680 DOMString XmppClient::toXml(const DOMString &str)
682     return Parser::encode(str);
687 static DOMString trim(const DOMString &str)
689     unsigned int i;
690     for (i=0 ; i<str.size() ; i++)
691         if (!isspace(str[i]))
692             break;
693     int start = i;
694     for (i=str.size() ; i>0 ; i--)
695         if (!isspace(str[i-1]))
696             break;
697     int end = i;
698     if (start>=end)
699         return "";
700     return str.substr(start, end);
707 //########################################################################
708 //# VARIABLES  (ones that need special handling)
709 //########################################################################
711 /**
712  *
713  */
714 DOMString XmppClient::getUsername()
716     return username;
719 /**
720  *
721  */
722 void XmppClient::setUsername(const DOMString &val)
724     int p = strIndex(val, "@");
725     if (p > 0)
726         {
727         username = val.substr(0, p);
728         realm    = val.substr(p+1, jid.size()-p-1);
729         }
730     else
731        {
732        realm    = host;
733        username = val;
734        }
744 //########################################################################
745 //# RECEIVING
746 //########################################################################
749 DOMString XmppClient::readStanza()
752     int  openCount    = 0;
753     bool inTag        = false;
754     bool slashSeen    = false;
755     bool trivialTag   = false;
756     bool querySeen    = false;
757     bool inQuote      = false;
758     bool textSeen     = false;
759     DOMString buf;
762     time_t timeout = time((time_t *)0) + 180;
764     while (true)
765         {
766         int ch = sock->read();
767         //printf("%c", ch); fflush(stdout);
768         if (ch<0)
769             {
770             if (ch == -2) //a simple timeout, not an error
771                 {
772                 //Since we are timed out, let's assume that we
773                 //are between chunks of text.  Let's reset all states.
774                 //printf("-----#### Timeout\n");
775                 time_t currentTime = time((time_t *)0);
776                 if (currentTime > timeout)
777                     {
778                     timeout = currentTime + 180;
779                     if (!write("\n"))
780                         {
781                         error("ping send error");
782                         disconnect();
783                         return "";
784                         }
785                     }
786                 continue;
787                 }
788             else
789                 {
790                 keepGoing = false;
791                 if (!sock->isConnected())
792                     {
793                     disconnect();
794                     return "";
795                     }
796                 else
797                     {
798                     error("socket read error");
799                     disconnect();
800                     return "";
801                     }
802                 }
803             }
804         buf.push_back(ch);
805         if (ch == '<')
806             {
807             inTag      = true;
808             slashSeen  = false;
809             querySeen  = false;
810             inQuote    = false;
811             textSeen   = false;
812             trivialTag = false;
813             }
814         else if (ch == '>')
815             {
816             if (!inTag)  //unescaped '>' in pcdata? horror
817                 continue;
818             inTag     = false;
819             if (!trivialTag && !querySeen)
820                 {
821                 if (slashSeen)
822                     openCount--;
823                 else
824                     openCount++;
825                 }
826             //printf("# openCount:%d t:%d q:%d\n",
827             //      openCount, trivialTag, querySeen);
828             //check if we are 'balanced', but not a <?version?> tag
829             if (openCount <= 0 && !querySeen)
830                 {
831                 break;
832                 }
833             //we know that this one will be open-ended
834             if (strIndex(buf, "<stream:stream") >= 0)
835                 {
836                 buf.append("</stream:stream>");
837                 break;
838                 }
839             }
840         else if (ch == '/')
841             {
842             if (inTag && !inQuote)
843                 {
844                 slashSeen = true;
845                 if (textSeen) // <tagName/>  <--looks like this
846                     trivialTag = true;
847                 }
848             }
849         else if (ch == '?')
850             {
851             if (inTag && !inQuote)
852                 querySeen = true;
853             }
854         else if (ch == '"' || ch == '\'')
855             {
856             if (inTag)
857                 inQuote = !inQuote;
858             }
859         else
860             {
861             if (inTag && !inQuote && !isspace(ch))
862                 textSeen = true;
863             }
864         }
865     return buf;
870 static bool isGroupChat(Element *root)
872     if (!root)
873         return false;
874     std::vector<Element *>elems = root->findElements("x");
875     for (unsigned int i=0 ; i<elems.size() ; i++)
876         {
877         DOMString xmlns = elems[i]->getAttribute("xmlns");
878         //printf("### XMLNS ### %s\n", xmlns.c_str());
879         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
880             return true;
881         }
882    return false;
888 static bool parseJid(const DOMString &fullJid,
889              DOMString &jid, DOMString &resource)
891     DOMString str = fullJid;
892     jid.clear();
893     resource.clear();
894     unsigned int p = str.size();
895     unsigned int p2 = str.rfind('/', p);
896     if (p2 != str.npos)
897         {
898         resource = str.substr(p2+1, p-(p2+1));
899         str = str.substr(0, p);
900         p = p2;
901         }
902     jid = str.substr(0, p);
903     printf("fullJid:%s jid:%s rsrc:%s\n",
904         fullJid.c_str(), jid.c_str(), resource.c_str());
905     return true;
911 bool XmppClient::processMessage(Element *root)
913     DOMString from    = root->getTagAttribute("message", "from");
914     DOMString to      = root->getTagAttribute("message", "to");
915     DOMString type    = root->getTagAttribute("message", "type");
917     //####Check for embedded namespaces here
918     //### FILE TRANSFERS
919     if (processFileMessage(root))
920         return true;
922     //### STREAMS
923     if (processInBandByteStreamMessage(root))
924         return true;
927     //#### NORMAL MESSAGES
928     DOMString subject = root->getTagValue("subject");
929     DOMString body    = root->getTagValue("body");
930     DOMString thread  = root->getTagValue("thread");
931     //##rfc 3921, para 2.4.  ignore if no recognizable info
932     //if (subject.size() < 1 && thread.size()<1)
933     //    return true;
935     if (type == "groupchat")
936         {
937         DOMString fromGid;
938         DOMString fromNick;
939         parseJid(from, fromGid, fromNick);
940         //printf("fromGid:%s  fromNick:%s\n",
941         //        fromGid.c_str(), fromNick.c_str());
942         DOMString toGid;
943         DOMString toNick;
944         parseJid(to, toGid, toNick);
945         //printf("toGid:%s  toNick:%s\n",
946         //        toGid.c_str(), toNick.c_str());
948         if (fromNick.size() > 0)//normal group message
949             {
950             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
951             event.setGroup(fromGid);
952             event.setFrom(fromNick);
953             event.setData(body);
954             event.setDOM(root);
955             dispatchXmppEvent(event);
956             }
957         else // from the server itself
958             {
959             //note the space before, so it doesnt match 'unlocked'
960             if (strIndex(body, " locked") >= 0)
961                 {
962                 printf("LOCKED!! ;)\n");
963                 char *fmt =
964                 "<iq id='create%d' to='%s' type='set'>"
965                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
966                 "<x xmlns='jabber:x:data' type='submit'/>"
967                 "</query></iq>\n";
968                 if (!write(fmt, msgId++, fromGid.c_str()))
969                     return false;
970                 }
971             }
972         }
973     else
974         {
975         XmppEvent event(XmppEvent::EVENT_MESSAGE);
976         event.setFrom(from);
977         event.setData(body);
978         event.setDOM(root);
979         dispatchXmppEvent(event);
980         }
982     return true;
988 bool XmppClient::processPresence(Element *root)
991     DOMString fullJid     = root->getTagAttribute("presence", "from");
992     DOMString to          = root->getTagAttribute("presence", "to");
993     DOMString presenceStr = root->getTagAttribute("presence", "type");
994     bool presence = true;
995     if (presenceStr == "unavailable")
996         presence = false;
997     DOMString status      = root->getTagValue("status");
998     DOMString show        = root->getTagValue("show");
1000     if (isGroupChat(root))
1001         {
1002         DOMString fromGid;
1003         DOMString fromNick;
1004         parseJid(fullJid, fromGid, fromNick);
1005         //printf("fromGid:%s  fromNick:%s\n",
1006         //        fromGid.c_str(), fromNick.c_str());
1007         DOMString item_jid = root->getTagAttribute("item", "jid");
1008         if (item_jid == jid || item_jid == to) //Me
1009             {
1010             if (presence)
1011                 {
1012                 groupChatCreate(fromGid);
1013                 groupChatUserAdd(fromGid, fromNick, "");
1014                 groupChatUserShow(fromGid, fromNick, "available");
1016                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
1017                 event.setGroup(fromGid);
1018                 event.setFrom(fromNick);
1019                 event.setPresence(presence);
1020                 event.setShow(show);
1021                 event.setStatus(status);
1022                 dispatchXmppEvent(event);
1023                 }
1024             else
1025                 {
1026                 groupChatDelete(fromGid);
1027                 groupChatUserDelete(fromGid, fromNick);
1029                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
1030                 event.setGroup(fromGid);
1031                 event.setFrom(fromNick);
1032                 event.setPresence(presence);
1033                 event.setShow(show);
1034                 event.setStatus(status);
1035                 dispatchXmppEvent(event);
1036                 }
1037             }
1038         else // someone else
1039             {
1040             if (presence)
1041                 {
1042                 groupChatUserAdd(fromGid, fromNick, "");
1043                 }
1044             else
1045                 groupChatUserDelete(fromGid, fromNick);
1046             groupChatUserShow(fromGid, fromNick, show);
1047             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
1048             event.setGroup(fromGid);
1049             event.setFrom(fromNick);
1050             event.setPresence(presence);
1051             event.setShow(show);
1052             event.setStatus(status);
1053             dispatchXmppEvent(event);
1054             }
1055         }
1056     else
1057         {
1058         DOMString shortJid;
1059         DOMString dummy;
1060         parseJid(fullJid, shortJid, dummy);
1061         rosterShow(shortJid, show); //users in roster do not have resource
1063         XmppEvent event(XmppEvent::EVENT_PRESENCE);
1064         event.setFrom(fullJid);
1065         event.setPresence(presence);
1066         event.setShow(show);
1067         event.setStatus(status);
1068         dispatchXmppEvent(event);
1069         }
1071     return true;
1076 bool XmppClient::processIq(Element *root)
1078     DOMString from  = root->getTagAttribute("iq", "from");
1079     DOMString id    = root->getTagAttribute("iq", "id");
1080     DOMString type  = root->getTagAttribute("iq", "type");
1081     DOMString xmlns = root->getTagAttribute("query", "xmlns");
1083     if (id.size()<1)
1084         return true;
1086     //Group chat
1087     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
1088         {
1089         printf("results of MUC query\n");
1090         }
1091     //printf("###IQ xmlns:%s\n", xmlns.c_str());
1093     //### FILE TRANSFERS
1094     if (processFileMessage(root))
1095         return true;
1097     //### STREAMS
1098     if (processInBandByteStreamMessage(root))
1099         return true;
1100         
1102     //###Normal Roster stuff
1103     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
1104         {
1105         roster.clear();
1106         std::vector<Element *>elems = root->findElements("item");
1107         for (unsigned int i=0 ; i<elems.size() ; i++)
1108             {
1109             Element *item = elems[i];
1110             DOMString userJid      = item->getAttribute("jid");
1111             DOMString name         = item->getAttribute("name");
1112             DOMString subscription = item->getAttribute("subscription");
1113             DOMString group        = item->getTagValue("group");
1114             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
1115             //        subscription.c_str(), group.c_str());
1116             XmppUser user(userJid, name, subscription, group);
1117             roster.push_back(user);
1118             }
1119         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
1120         dispatchXmppEvent(event);
1121         }
1123     else if (id.find("regnew") != id.npos)
1124         {
1126         }
1128     else if (id.find("regpass") != id.npos)
1129         {
1130         std::vector<Element *> list = root->findElements("error");
1131         if (list.size()==0)
1132             {
1133             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
1134             evt.setTo(username);
1135             evt.setFrom(host);
1136             dispatchXmppEvent(evt);
1137             return true;
1138             }
1140         Element *errElem = list[0];
1141         DOMString errMsg = "Password change error: ";
1142         if (errElem->findElements("bad-request").size()>0)
1143             {
1144             errMsg.append("password change does not contain complete information");
1145             }
1146         else if (errElem->findElements("not-authorized").size()>0)
1147             {
1148             errMsg.append("server does not consider the channel safe "
1149                           "enough to enable a password change");
1150             }
1151         else if (errElem->findElements("not-allowed").size()>0)
1152             {
1153             errMsg.append("server does not allow password changes");
1154             }
1155         else if (errElem->findElements("unexpected-request").size()>0)
1156             {
1157             errMsg.append(
1158              "IQ set does not contain a 'from' address because "
1159              "the entity is not registered with the server");
1160             }
1161         error((char *)errMsg.c_str());
1162         }
1164     else if (id.find("regcancel") != id.npos)
1165         {
1166         std::vector<Element *> list = root->findElements("error");
1167         if (list.size()==0)
1168             {
1169             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
1170             evt.setTo(username);
1171             evt.setFrom(host);
1172             dispatchXmppEvent(evt);
1173             return true;
1174             }
1176         Element *errElem = list[0];
1177         DOMString errMsg = "Registration cancel error: ";
1178         if (errElem->findElements("bad-request").size()>0)
1179             {
1180             errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
1181             }
1182         else if (errElem->findElements("forbidden").size()>0)
1183             {
1184             errMsg.append("sender does not have sufficient permissions to cancel the registration");
1185             }
1186         else if (errElem->findElements("not-allowed").size()>0)
1187             {
1188             errMsg.append("not allowed to cancel registrations in-band");
1189             }
1190         else if (errElem->findElements("registration-required").size()>0)
1191             {
1192             errMsg.append("not previously registered");
1193             }
1194         else if (errElem->findElements("unexpected-request").size()>0)
1195             {
1196             errMsg.append(
1197                  "IQ set does not contain a 'from' address because "
1198                  "the entity is not registered with the server");
1199             }
1200         error((char *)errMsg.c_str());
1201         }
1203     return true;
1210 bool XmppClient::receiveAndProcess()
1212     if (!keepGoing)
1213         return false;
1215     Parser parser;
1217     DOMString recvBuf = readStanza();
1218     recvBuf = trim(recvBuf);
1219     if (recvBuf.size() < 1)
1220         return true;
1222     //Ugly hack.  Apparently the first char can be dropped on timeouts
1223     //if (recvBuf[0] != '<')
1224     //    recvBuf.insert(0, "<");
1226     status("RECV: %s", recvBuf.c_str());
1227     Element *root = parser.parse(recvBuf);
1228     if (!root)
1229         {
1230         printf("Bad elem\n");
1231         return true;
1232         }
1234     //#### MESSAGE
1235     std::vector<Element *>elems = root->findElements("message");
1236     if (elems.size()>0)
1237         {
1238         if (!processMessage(root))
1239             return false;
1240         }
1242     //#### PRESENCE
1243     elems = root->findElements("presence");
1244     if (elems.size()>0)
1245         {
1246         if (!processPresence(root))
1247             return false;
1248         }
1250     //#### INFO
1251     elems = root->findElements("iq");
1252     if (elems.size()>0)
1253         {
1254         if (!processIq(root))
1255             return false;
1256         }
1258     delete root;
1260     return true;
1264 bool XmppClient::receiveAndProcessLoop()
1266     keepGoing = true;
1267     while (true)
1268         {
1269         if (!keepGoing)
1270             {
1271             status("Abort requested");
1272             break;
1273             }
1274         if (!receiveAndProcess())
1275             return false;
1276         }
1277     return true;
1283 //########################################################################
1284 //# SENDING
1285 //########################################################################
1288 bool XmppClient::write(char *fmt, ...)
1290     va_list args;
1291     va_start(args,fmt);
1292     vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
1293     va_end(args) ;
1294     status("SEND: %s", writeBuf);
1295     if (!sock->write((char *)writeBuf))
1296         {
1297         error("Cannot write to socket");
1298         return false;
1299         }
1300     return true;
1308 //########################################################################
1309 //# R E G I S T R A T I O N
1310 //########################################################################
1312 /**
1313  * Perform JEP-077 In-Band Registration.  Performed synchronously after SSL,
1314  * before authentication
1315  */
1316 bool XmppClient::inBandRegistrationNew()
1318     Parser parser;
1320     char *fmt =
1321      "<iq type='get' id='regnew%d'>"
1322          "<query xmlns='jabber:iq:register'/>"
1323          "</iq>\n\n";
1324     if (!write(fmt, msgId++))
1325         return false;
1327     DOMString recbuf = readStanza();
1328     status("RECV reg: %s", recbuf.c_str());
1329     Element *elem = parser.parse(recbuf);
1330     //elem->print();
1332     //# does the entity send the newer "instructions" tag?
1333     std::vector<Element *> fields = elem->findElements("field");
1334     std::vector<DOMString> fnames;
1335     for (unsigned int i=0; i<fields.size() ; i++)
1336         {
1337         DOMString fname = fields[i]->getAttribute("var");
1338         if (fname == "FORM_TYPE")
1339             continue;
1340         fnames.push_back(fname);
1341         status("field name:%s", fname.c_str());
1342         }
1344     //Do we have any fields?
1345     if (fnames.size() == 0)
1346         {
1347         //If no fields, maybe the older method was offered
1348         if (elem->findElements("username").size() == 0 ||
1349             elem->findElements("password").size() == 0)
1350             {
1351             error("server did not offer registration");
1352             delete elem;
1353             return false;
1354             }
1355         }
1357     delete elem;
1359     fmt =
1360      "<iq type='set' id='regnew%d'>"
1361          "<query xmlns='jabber:iq:register'>"
1362          "<username>%s</username>"
1363          "<password>%s</password>"
1364          "<email/><name/>"
1365          "</query>"
1366          "</iq>\n\n";
1367     if (!write(fmt, msgId++, toXml(username).c_str(),
1368                     toXml(password).c_str() ))
1369         return false;
1372     recbuf = readStanza();
1373     status("RECV reg: %s", recbuf.c_str());
1374     elem = parser.parse(recbuf);
1375     //elem->print();
1377     std::vector<Element *> list = elem->findElements("error");
1378     if (list.size()>0)
1379         {
1380         Element *errElem = list[0];
1381         DOMString code = errElem->getAttribute("code");
1382         DOMString errMsg = "Registration error: ";
1383         if (code == "409")
1384             {
1385             errMsg.append("conflict with existing user name");
1386             }
1387         else if (code == "406")
1388             {
1389             errMsg.append("some registration information was not provided");
1390             }
1391         error((char *)errMsg.c_str());
1392         delete elem;
1393         return false;
1394         }
1396     delete elem;
1398     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1399     evt.setTo(username);
1400     evt.setFrom(host);
1401     dispatchXmppEvent(evt);
1403     return true;
1407 /**
1408  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1409  * See processIq() for response handling.
1410  */
1411 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1413     Parser parser;
1415     //# Let's try it form-style to allow the common old/new password thing
1416     char *fmt =
1417       "<iq type='set' id='regpass%d' to='%s'>"
1418       "  <query xmlns='jabber:iq:register'>"
1419       "    <x xmlns='jabber:x:data' type='form'>"
1420       "      <field type='hidden' var='FORM_TYPE'>"
1421       "        <value>jabber:iq:register:changepassword</value>"
1422       "      </field>"
1423       "      <field type='text-single' var='username'>"
1424       "        <value>%s</value>"
1425       "      </field>"
1426       "      <field type='text-private' var='old_password'>"
1427       "        <value>%s</value>"
1428       "      </field>"
1429       "      <field type='text-private' var='password'>"
1430       "        <value>%s</value>"
1431       "      </field>"
1432       "    </x>"
1433       "  </query>"
1434       "</iq>\n\n";
1436     if (!write(fmt, msgId++, host.c_str(),
1437              username.c_str(), password.c_str(), newpassword.c_str()))
1438         return false;
1440     return true;
1445 /**
1446  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1447  * See processIq() for response handling.
1448  */
1449 bool XmppClient::inBandRegistrationCancel()
1451     Parser parser;
1453     char *fmt =
1454      "<iq type='set' id='regcancel%d'>"
1455           "<query xmlns='jabber:iq:register'><remove/></query>"
1456           "</iq>\n\n";  
1457     if (!write(fmt, msgId++))
1458         return false;
1460     return true;
1467 //########################################################################
1468 //# A U T H E N T I C A T E
1469 //########################################################################
1471 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1473     Parser parser;
1475     char *fmt =
1476     "<iq type='get' to='%s' id='auth%d'>"
1477     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1478     "</iq>\n";
1479     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1480         return false;
1482     DOMString recbuf = readStanza();
1483     //printf("iq received: '%s'\n", recbuf.c_str());
1484     Element *elem = parser.parse(recbuf);
1485     //elem->print();
1486     DOMString iqType = elem->getTagAttribute("iq", "type");
1487     //printf("##iqType:%s\n", iqType.c_str());
1488     delete elem;
1490     if (iqType != "result")
1491         {
1492         error("error:server does not allow login");
1493         return false;
1494         }
1496     bool digest = true;
1497     if (digest)
1498         {
1499         //## Digest authentication
1500         DOMString digest = streamId;
1501         digest.append(password);
1502         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
1503         //printf("digest:%s\n", digest.c_str());
1504         fmt =
1505         "<iq type='set' id='auth%d'>"
1506         "<query xmlns='jabber:iq:auth'>"
1507         "<username>%s</username>"
1508         "<digest>%s</digest>"
1509         "<resource>%s</resource>"
1510         "</query>"
1511         "</iq>\n";
1512         if (!write(fmt, msgId++, username.c_str(),
1513                     digest.c_str(), resource.c_str()))
1514             return false;
1515         }
1516     else
1517         {
1519         //## Plaintext authentication
1520         fmt =
1521         "<iq type='set' id='auth%d'>"
1522         "<query xmlns='jabber:iq:auth'>"
1523         "<username>%s</username>"
1524         "<password>%s</password>"
1525         "<resource>%s</resource>"
1526         "</query>"
1527         "</iq>\n";
1528         if (!write(fmt, msgId++, username.c_str(),
1529                    password.c_str(), resource.c_str()))
1530             return false;
1531         }
1533     recbuf = readStanza();
1534     //printf("iq received: '%s'\n", recbuf.c_str());
1535     elem = parser.parse(recbuf);
1536     //elem->print();
1537     iqType = elem->getTagAttribute("iq", "type");
1538     //printf("##iqType:%s\n", iqType.c_str());
1539     delete elem;
1541     if (iqType != "result")
1542         {
1543         error("server does not allow login");
1544         return false;
1545         }
1547     return true;
1551 /**
1552  * Parse a sasl challenge to retrieve all of its key=value pairs
1553  */
1554 static bool saslParse(const DOMString &s, 
1555                       std::map<DOMString, DOMString> &vals)
1558     vals.clear();
1560     int p  = 0;
1561     int siz = s.size();
1563     while (p < siz)
1564         {
1565         DOMString key;
1566         DOMString value;
1567         char ch = '\0';
1569         //# Parse key
1570         while (p<siz)
1571             {
1572             ch = s[p++];
1573             if (ch == '=')
1574                 break;
1575             key.push_back(ch);
1576             }
1578         //No value?
1579         if (ch != '=')
1580             break;
1582         //# Parse value
1583         bool quoted = false;
1584         while (p<siz)
1585             {
1586             ch = s[p++];
1587             if (ch == '"')
1588                 quoted = !quoted;
1589             else if (ch == ',' && !quoted)
1590                 break;
1591             else
1592                 value.push_back(ch);
1593             }
1595         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
1596         vals[key] = value;
1597         if (ch != ',')
1598             break;
1599         }
1601     return true;
1606 /**
1607  * Attempt suthentication using the MD5 SASL mechanism
1608  */
1609 bool XmppClient::saslMd5Authenticate()
1611     Parser parser;
1612     char *fmt =
1613     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1614         "mechanism='DIGEST-MD5'/>\n";
1615     if (!write(fmt))
1616         return false;
1618     DOMString recbuf = readStanza();
1619     status("challenge received: '%s'", recbuf.c_str());
1620     Element *elem = parser.parse(recbuf);
1621     //elem->print();
1622     DOMString b64challenge = elem->getTagValue("challenge");
1623     delete elem;
1625     if (b64challenge.size() < 1)
1626         {
1627         error("login: no SASL challenge offered by server");
1628         return false;
1629         }
1630     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1631     status("md5 challenge:'%s'", challenge.c_str());
1633     std::map<DOMString, DOMString> attrs;
1634     if (!saslParse(challenge, attrs))
1635         {
1636         error("login: error parsing SASL challenge");
1637         return false;
1638         }
1640     DOMString nonce = attrs["nonce"];
1641     if (nonce.size()==0)
1642         {
1643         error("login: no SASL nonce sent by server");
1644         return false;
1645         }
1647     DOMString realm = attrs["realm"];
1648     if (realm.size()==0)
1649         {
1650         //Apparently this is not a problem
1651         //error("login: no SASL realm sent by server");
1652         //return false;
1653         }
1655     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1657     char idBuf[10];
1658     snprintf(idBuf, 9, "%dsasl", msgId++);
1659     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
1660     DOMString authzid = username; authzid.append("@"); authzid.append(host);
1661     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1663     //## Make A1
1664     Md5 md5;
1665     md5.append(username);
1666     md5.append(":");
1667     md5.append(realm);
1668     md5.append(":");
1669     md5.append(password);
1670     unsigned char a1tmp[16];
1671     md5.finish(a1tmp);
1672     md5.init();
1673     md5.append(a1tmp, 16);
1674     md5.append(":");
1675     md5.append(nonce);
1676     md5.append(":");
1677     md5.append(cnonce);
1678     //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1679     //md5.append(":");
1680     //md5.append(authzid);
1681     md5.append("");
1682     DOMString a1 = md5.finishHex();
1683     status("##a1:'%s'", a1.c_str());
1685     //# Make A2
1686     md5.init();
1687     md5.append("AUTHENTICATE:");
1688     md5.append(digest_uri);
1689     DOMString a2 = md5.finishHex();
1690     status("##a2:'%s'", a2.c_str());
1692     //# Now make the response
1693     md5.init();
1694     md5.append(a1);
1695     md5.append(":");
1696     md5.append(nonce);
1697     md5.append(":");
1698     md5.append("00000001");//nc
1699     md5.append(":");
1700     md5.append(cnonce);
1701     md5.append(":");
1702     md5.append("auth");//qop
1703     md5.append(":");
1704     md5.append(a2);
1705     DOMString response = md5.finishHex();
1707     DOMString resp;
1708     resp.append("username=\""); resp.append(username); resp.append("\",");
1709     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
1710     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
1711     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
1712     resp.append("nc=00000001,qop=auth,");
1713     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1714     //resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
1715     resp.append("response=");   resp.append(response); resp.append(",");
1716     resp.append("charset=utf-8");
1717     status("sending response:'%s'", resp.c_str());
1718     resp = Base64Encoder::encode(resp);
1719     status("base64 response:'%s'", resp.c_str());
1720     fmt =
1721     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1722     if (!write(fmt, resp.c_str()))
1723         return false;
1725     recbuf = readStanza();
1726     status("server says:: '%s'", recbuf.c_str());
1727     elem = parser.parse(recbuf);
1728     //elem->print();
1729     b64challenge = elem->getTagValue("challenge");
1730     delete elem;
1732     if (b64challenge.size() < 1)
1733         {
1734         error("login: no second SASL challenge offered by server");
1735         return false;
1736         }
1738     challenge = Base64Decoder::decodeToString(b64challenge);
1739     status("md5 challenge: '%s'", challenge.c_str());
1741     if (!saslParse(challenge, attrs))
1742         {
1743         error("login: error parsing SASL challenge");
1744         return false;
1745         }
1747     DOMString rspauth = attrs["rspauth"];
1748     if (rspauth.size()==0)
1749         {
1750         error("login: no SASL respauth sent by server\n");
1751         return false;
1752         }
1754     fmt =
1755     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1756     if (!write(fmt))
1757         return false;
1759     recbuf = readStanza();
1760     status("SASL recv: '%s", recbuf.c_str());
1761     elem = parser.parse(recbuf);
1762     //elem->print();
1763     b64challenge = elem->getTagValue("challenge");
1764     bool success = (elem->findElements("success").size() > 0);
1765     delete elem;
1767     return success;
1772 /**
1773  *  Attempt to authentication using the SASL PLAIN mechanism.  This
1774  *  is used most commonly my Google Talk.
1775  */
1776 bool XmppClient::saslPlainAuthenticate()
1778     Parser parser;
1780     DOMString id = username;
1781     //id.append("@");
1782     //id.append(host);
1783     Base64Encoder encoder;
1784     encoder.append('\0');
1785     encoder.append(id);
1786     encoder.append('\0');
1787     encoder.append(password);
1788     DOMString base64Auth = encoder.finish();
1789     //printf("authbuf:%s\n", base64Auth.c_str());
1791     char *fmt =
1792     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1793     "mechanism='PLAIN'>%s</auth>\n";
1794     if (!write(fmt, base64Auth.c_str()))
1795         return false;
1796     DOMString recbuf = readStanza();
1797     status("challenge received: '%s'", recbuf.c_str());
1798     Element *elem = parser.parse(recbuf);
1800     bool success = (elem->findElements("success").size() > 0);
1801     delete elem;
1803     return success;
1808 /**
1809  * Handshake with SASL, and use one of its offered mechanisms to
1810  * authenticate.
1811  */
1812 bool XmppClient::saslAuthenticate()
1814     Parser parser;
1816     DOMString recbuf = readStanza();
1817     status("RECV: '%s'\n", recbuf.c_str());
1818     Element *elem = parser.parse(recbuf);
1819     //elem->print();
1821     //Check for starttls
1822     bool wantStartTls = false;
1823     if (elem->findElements("starttls").size() > 0)
1824         {
1825         wantStartTls = true;
1826         if (elem->findElements("required").size() > 0)
1827             status("login: STARTTLS required");
1828         else
1829             status("login: STARTTLS available");
1830         }
1832     //# do we want TLS, are we not already running SSL, and can
1833     //# the client actually do an ssl connection?
1834     if (wantStartTls && !sock->getEnableSSL() && sock->getHaveSSL())
1835         {
1836         delete elem;
1837         char *fmt =
1838         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
1839         if (!write(fmt))
1840             return false;
1841         recbuf = readStanza();
1842         status("RECV: '%s'\n", recbuf.c_str());
1843         elem = parser.parse(recbuf);
1844         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
1845             {
1846             error("Server rejected TLS negotiation");
1847             disconnect();
1848             return false;
1849             }
1850         delete elem;
1851         if (!sock->startTls())
1852             {
1853             error("Could not start TLS");
1854             disconnect();
1855             return false;
1856             }
1858         fmt =
1859          "<stream:stream xmlns='jabber:client' "
1860          "xmlns:stream='http://etherx.jabber.org/streams' "
1861          "to='%s' version='1.0'>\n\n";
1862         if (!write(fmt, realm.c_str()))
1863             return false;
1865         recbuf = readStanza();
1866         status("RECVx: '%s'", recbuf.c_str());
1867         recbuf.append("</stream:stream>");
1868         elem = parser.parse(recbuf);
1869         bool success =
1870         (elem->getTagAttribute("stream:stream", "id").size()>0);
1871         if (!success)
1872             {
1873             error("STARTTLS negotiation failed");
1874             disconnect();
1875             return false;
1876             }
1877         delete elem;
1878         recbuf = readStanza();
1879         status("RECV: '%s'\n", recbuf.c_str());
1880         elem = parser.parse(recbuf);
1882         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
1883         dispatchXmppEvent(event);
1884         }
1886     //register, if user requests
1887     if (doRegister)
1888         {
1889         if (!inBandRegistrationNew())
1890             return false;
1891         }
1893     //check for sasl authentication mechanisms
1894     std::vector<Element *> elems =
1895                elem->findElements("mechanism");
1896     if (elems.size() < 1)
1897         {
1898         error("login: no SASL mechanism offered by server");
1899         return false;
1900         }
1901     bool md5Found = false;
1902     bool plainFound = false;
1903     for (unsigned int i=0 ; i<elems.size() ; i++)
1904         {
1905         DOMString mech = elems[i]->getValue();
1906         if (mech == "DIGEST-MD5")
1907             {
1908             status("MD5 authentication offered");
1909             md5Found = true;
1910             }
1911         else if (mech == "PLAIN")
1912             {
1913             status("PLAIN authentication offered");
1914             plainFound = true;
1915             }
1916         }
1917     delete elem;
1919     bool success = false;
1920     if (md5Found)
1921         {
1922         success = saslMd5Authenticate();
1923         }
1924     else if (plainFound)
1925         {
1926         success = saslPlainAuthenticate();
1927         }
1928     else
1929         {
1930         error("not able to handle sasl authentication mechanisms");
1931         return false;
1932         }
1934     if (success)
1935         status("###### SASL authentication success\n");
1936     else
1937         error("###### SASL authentication failure\n");
1939     return success;
1947 //########################################################################
1948 //# CONNECT
1949 //########################################################################
1952 /**
1953  * Check if we are connected, and fail with an error if we are not
1954  */
1955 bool XmppClient::checkConnect()
1957     if (!connected)
1958         {
1959         XmppEvent evt(XmppEvent::EVENT_ERROR);
1960         evt.setData("Attempted operation while disconnected");
1961         dispatchXmppEvent(evt);
1962         return false;
1963         }
1964     return true;
1969 /**
1970  * Create an XMPP session with a server.  This
1971  * is basically the transport layer of XMPP.
1972  */
1973 bool XmppClient::createSession()
1976     Parser parser;
1977     if (port==443 || port==5223)
1978         sock->enableSSL(true);
1979     if (!sock->connect(host, port))
1980         {
1981         return false;
1982         }
1984     if (sock->getEnableSSL())
1985         {
1986         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
1987         dispatchXmppEvent(event);
1988         }
1990     char *fmt =
1991      "<stream:stream "
1992           "to='%s' "
1993           "xmlns='jabber:client' "
1994           "xmlns:stream='http://etherx.jabber.org/streams' "
1995           "version='1.0'>\n\n";
1996     if (!write(fmt, realm.c_str()))
1997         return false;
1999     DOMString recbuf = readStanza();
2000     //printf("received: '%s'\n", recbuf.c_str());
2001     recbuf.append("</stream:stream>");
2002     Element *elem = parser.parse(recbuf);
2003     //elem->print();
2004     bool useSasl = false;
2005     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2006     //printf("### StreamID: %s\n", streamId.c_str());
2007     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2008     if (streamVersion == "1.0")
2009         useSasl = true;
2011     if (useSasl)
2012         {
2013         if (!saslAuthenticate())
2014             return false;
2015         fmt =
2016           "<stream:stream "
2017           "to='%s' "
2018           "xmlns='jabber:client' "
2019           "xmlns:stream='http://etherx.jabber.org/streams' "
2020           "version='1.0'>\n\n";
2022         if (!write(fmt, realm.c_str()))
2023             return false;
2024         recbuf = readStanza();
2025         recbuf.append("</stream:stream>\n");
2026         //printf("now server says:: '%s'\n", recbuf.c_str());
2027         elem = parser.parse(recbuf);
2028         //elem->print();
2029         delete elem;
2031         recbuf = readStanza();
2032         //printf("now server says:: '%s'\n", recbuf.c_str());
2033         elem = parser.parse(recbuf);
2034         bool hasBind = (elem->findElements("bind").size() > 0);
2035         //elem->print();
2036         delete elem;
2038         if (!hasBind)
2039             {
2040             error("no binding provided by server");
2041             return false;
2042             }
2045         }
2046     else // not SASL
2047         {
2048         if (!iqAuthenticate(streamId))
2049             return false;
2050         }
2053     //### Resource binding
2054     fmt =
2055     "<iq type='set' id='bind%d'>"
2056     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2057     "<resource>%s</resource>"
2058     "</bind></iq>\n";
2059     if (!write(fmt, msgId++, resource.c_str()))
2060         return false;
2062     recbuf = readStanza();
2063     status("bind result: '%s'", recbuf.c_str());
2064     elem = parser.parse(recbuf);
2065     //elem->print();
2066     DOMString bindType = elem->getTagAttribute("iq", "type");
2067     //printf("##bindType:%s\n", bindType.c_str());
2068     DOMString givenFullJid = elem->getTagValue("jid");
2069     delete elem;
2071     if (bindType != "result")
2072         {
2073         error("no binding with server failed");
2074         return false;
2075         }
2076         
2077     //The server sent us a JID.  We need to listen.
2078     if (givenFullJid.size()>0)
2079         {
2080         DOMString givenJid, givenResource;
2081         parseJid(givenFullJid, givenJid, givenResource);
2082         status("given user: %s realm: %s, rsrc: %s",
2083            givenJid.c_str(), givenResource.c_str());
2084         setResource(givenResource);
2085         }
2086         
2088     fmt =
2089     "<iq type='set' id='sess%d'>"
2090     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2091     "</iq>\n";
2092     if (!write(fmt, msgId++))
2093         return false;
2095     recbuf = readStanza();
2096     status("session received: '%s'", recbuf.c_str());
2097     elem = parser.parse(recbuf);
2098     //elem->print();
2099     DOMString sessionType = elem->getTagAttribute("iq", "type");
2100     //printf("##sessionType:%s\n", sessionType.c_str());
2101     delete elem;
2103     if (sessionType != "result")
2104         {
2105         error("no session provided by server");
2106         return false;
2107         }
2109     //printf("########## COOL #########\n");
2110     //Now that we are bound, we have a valid JID
2111     jid = username;
2112     jid.append("@");
2113     jid.append(realm);
2114     jid.append("/");
2115     jid.append(resource);
2117     //We are now done with the synchronous handshaking.  Let's go into
2118     //async mode
2120     fmt =
2121      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2122     if (!write(fmt, msgId++))
2123         return false;
2125     fmt =
2126      "<iq type='get' id='discoItems%d' to='%s'>"
2127      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2128     if (!write(fmt, msgId++, realm.c_str()))
2129         return false;
2131     fmt =
2132     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2133     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2134     if (!write(fmt, msgId++, realm.c_str()))
2135         return false;
2137     fmt =
2138      "<presence/>\n";
2139     if (!write(fmt))
2140         return false;
2142     /*
2143     recbuf = readStanza();
2144     status("stream received: '%s'", recbuf.c_str());
2145     elem = parser.parse(recbuf);
2146     //elem->print();
2147     delete elem;
2148     */
2150     //We are now logged in
2151     status("Connected");
2152     connected = true;
2153     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2154     evt.setData(host);
2155     dispatchXmppEvent(evt);
2156     //Thread::sleep(1000000);
2158     sock->setReceiveTimeout(1000);
2159     ReceiverThread runner(*this);
2160     Thread thread(runner);
2161     thread.start();
2163     return true;
2168 /**
2169  * Public call to connect
2170  */
2171 bool XmppClient::connect()
2173     if (!createSession())
2174         {
2175         disconnect();
2176         return false;
2177         }
2178     return true;
2182 /**
2183  * Public call to connect
2184  */
2185 bool XmppClient::connect(DOMString hostArg, int portArg,
2186                          DOMString usernameArg,
2187                          DOMString passwordArg,
2188                          DOMString resourceArg)
2190     host     = hostArg;
2191     port     = portArg;
2192     password = passwordArg;
2193     resource = resourceArg;
2195     //parse this one
2196     setUsername(usernameArg);
2198     bool ret = connect();
2199     return ret;
2204 /**
2205  * Public call to disconnect
2206  */
2207 bool XmppClient::disconnect()
2209     if (connected)
2210         {
2211         char *fmt =
2212         "<presence type='unavailable'/>\n";
2213         write(fmt);
2214         }
2215     keepGoing = false;
2216     connected = false;
2217     Thread::sleep(2000); //allow receiving thread to quit
2218     sock->disconnect();
2219     roster.clear();
2220     groupChatsClear();
2221     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2222     event.setData(host);
2223     dispatchXmppEvent(event);
2224     return true;
2231 //########################################################################
2232 //# ROSTER
2233 //########################################################################
2235 /**
2236  *  Add an XMPP id to your roster
2237  */
2238 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2239                            const DOMString &otherJid,
2240                            const DOMString &name)
2242     if (!checkConnect())
2243         return false;
2244     char *fmt =
2245     "<iq type='set' id='roster_%d'>"
2246     "<query xmlns='jabber:iq:roster'>"
2247     "<item jid='%s' name='%s'><group>%s</group></item>"
2248     "</query></iq>\n";
2249     if (!write(fmt, msgId++, otherJid.c_str(),
2250          name.c_str(), rosterGroup.c_str()))
2251         {
2252         return false;
2253         }
2254     return true;
2259 /**
2260  *  Delete an XMPP id from your roster.
2261  */
2262 bool XmppClient::rosterDelete(const DOMString &otherJid)
2264     if (!checkConnect())
2265         return false;
2266     char *fmt =
2267     "<iq type='set' id='roster_%d'>"
2268     "<query xmlns='jabber:iq:roster'>"
2269     "<item jid='%s' subscription='remove'><group>%s</group></item>"
2270     "</query></iq>\n";
2271     if (!write(fmt, msgId++, otherJid.c_str()))
2272         {
2273         return false;
2274         }
2275     return true;
2279 /**
2280  *  Comparison method for sort() call below
2281  */
2282 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2284     DOMString s1 = p1.group;
2285     DOMString s2 = p2.group;
2286     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2287         {
2288         int comp = tolower(s1[len]) - tolower(s2[len]);
2289         if (comp)
2290             return (comp<0);
2291         }
2293     s1 = p1.jid;
2294     s2 = p2.jid;
2295     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2296         {
2297         int comp = tolower(s1[len]) - tolower(s2[len]);
2298         if (comp)
2299             return (comp<0);
2300         }
2301     return false;
2306 /**
2307  *  Sort and return the roster that has just been reported by
2308  *  an XmppEvent::EVENT_ROSTER event.
2309  */
2310 std::vector<XmppUser> XmppClient::getRoster()
2312     std::vector<XmppUser> ros = roster;
2313     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2314     return ros;
2318 /**
2319  *
2320  */
2321 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2323     DOMString theShow = show;
2324     if (theShow == "")
2325         theShow = "available";
2327     std::vector<XmppUser>::iterator iter;
2328     for (iter=roster.begin() ; iter != roster.end() ; iter++)
2329         {
2330         if (iter->jid == jid)
2331             iter->show = theShow;
2332         }
2340 //########################################################################
2341 //# CHAT (individual)
2342 //########################################################################
2344 /**
2345  * Send a message to an xmpp jid
2346  */
2347 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2348                          const DOMString &msg)
2350     if (!checkConnect())
2351         return false;
2353     DOMString xmlSubj = toXml(subj);
2354     DOMString xmlMsg  = toXml(msg);
2356     if (xmlSubj.size() > 0)
2357         {
2358         char *fmt =
2359         "<message to='%s' from='%s' type='chat'>"
2360         "<subject>%s</subject><body>%s</body></message>\n";
2361         if (!write(fmt, user.c_str(), jid.c_str(),
2362                 xmlSubj.c_str(), xmlMsg.c_str()))
2363             return false;
2364         }
2365     else
2366         {
2367         char *fmt =
2368         "<message to='%s' from='%s'>"
2369         "<body>%s</body></message>\n";
2370         if (!write(fmt, user.c_str(), jid.c_str(), xmlMsg.c_str()))
2371             return false;
2372         }
2373     return true;
2378 /**
2379  *
2380  */
2381 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2383     return message(user, "", msg);
2388 /**
2389  *
2390  */
2391 bool XmppClient::presence(const DOMString &presence)
2393     if (!checkConnect())
2394         return false;
2396     DOMString xmlPres = toXml(presence);
2398     char *fmt =
2399     "<presence><show>%s</show></presence>\n";
2400     if (!write(fmt, xmlPres.c_str()))
2401         return false;
2402     return true;
2410 //########################################################################
2411 //# GROUP  CHAT
2412 //########################################################################
2414 /**
2415  *
2416  */
2417 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2419     std::vector<XmppGroupChat *>::iterator iter;
2420     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2421         {
2422         if ((*iter)->getGroupJid() == groupJid)
2423             {
2424             //error("Group chat '%s' already exists", groupJid.c_str());
2425             return false;
2426             }
2427         }
2428     XmppGroupChat *chat = new XmppGroupChat(groupJid);
2429     groupChats.push_back(chat);
2430     return true;
2435 /**
2436  *
2437  */
2438 void XmppClient::groupChatDelete(const DOMString &groupJid)
2440     std::vector<XmppGroupChat *>::iterator iter;
2441     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2442         {
2443         XmppGroupChat *chat = *iter;
2444         if (chat->getGroupJid() == groupJid)
2445             {
2446             iter = groupChats.erase(iter);
2447             delete chat;
2448             }
2449         else
2450             iter++;
2451         }
2456 /**
2457  *
2458  */
2459 bool XmppClient::groupChatExists(const DOMString &groupJid)
2461     std::vector<XmppGroupChat *>::iterator iter;
2462     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2463         if ((*iter)->getGroupJid() == groupJid)
2464             return true;
2465     return false;
2470 /**
2471  *
2472  */
2473 void XmppClient::groupChatsClear()
2475     std::vector<XmppGroupChat *>::iterator iter;
2476     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2477         delete (*iter);
2478     groupChats.clear();
2484 /**
2485  *
2486  */
2487 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2488                                   const DOMString &nick,
2489                                   const DOMString &jid)
2491     std::vector<XmppGroupChat *>::iterator iter;
2492     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2493         {
2494         if ((*iter)->getGroupJid() == groupJid)
2495             {
2496             (*iter)->userAdd(nick, jid);
2497             }
2498         }
2503 /**
2504  *
2505  */
2506 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2507                                    const DOMString &nick,
2508                                    const DOMString &show)
2510     std::vector<XmppGroupChat *>::iterator iter;
2511     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2512         {
2513         if ((*iter)->getGroupJid() == groupJid)
2514             {
2515             (*iter)->userShow(nick, show);
2516             }
2517         }
2523 /**
2524  *
2525  */
2526 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2527                                      const DOMString &nick)
2529     std::vector<XmppGroupChat *>::iterator iter;
2530     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2531         {
2532         if ((*iter)->getGroupJid() == groupJid)
2533             {
2534             (*iter)->userDelete(nick);
2535             }
2536         }
2541 /**
2542  *  Comparison method for the sort() below
2543  */
2544 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2546     DOMString s1 = p1.nick;
2547     DOMString s2 = p2.nick;
2548     int comp = 0;
2549     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2550         {
2551         comp = tolower(s1[len]) - tolower(s2[len]);
2552         if (comp)
2553             break;
2554         }
2555     return (comp<0);
2560 /**
2561  *  Return the user list for the named group
2562  */
2563 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2564                               const DOMString &groupJid)
2566     if (!checkConnect())
2567         {
2568         std::vector<XmppUser> dummy;
2569         return dummy;
2570         }
2572     std::vector<XmppGroupChat *>::iterator iter;
2573     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2574         {
2575         if ((*iter)->getGroupJid() == groupJid )
2576             {
2577             std::vector<XmppUser> uList = (*iter)->getUserList();
2578             std::sort(uList.begin(), uList.end(), xmppUserCompare);
2579             return uList;
2580             }
2581         }
2582     std::vector<XmppUser> dummy;
2583     return dummy;
2589 /**
2590  *  Try to join a group
2591  */
2592 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2593                                const DOMString &nick,
2594                                const DOMString &pass)
2596     if (!checkConnect())
2597         return false;
2599     DOMString user = nick;
2600     if (user.size()<1)
2601         user = username;
2603     char *fmt =
2604     "<presence to='%s/%s'>"
2605     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2606     if (!write(fmt, groupJid.c_str(), user.c_str()))
2607         return false;
2608     return true;
2614 /**
2615  * Leave a group
2616  */
2617 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2618                                 const DOMString &nick)
2620     if (!checkConnect())
2621         return false;
2623     DOMString user = nick;
2624     if (user.size()<1)
2625         user = username;
2627     char *fmt =
2628     "<presence to='%s/%s' type='unavailable'>"
2629     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2630     if (!write(fmt, groupJid.c_str(), user.c_str()))
2631         return false;
2632     return true;
2638 /**
2639  *  Send a message to a group
2640  */
2641 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2642                                   const DOMString &msg)
2644     if (!checkConnect())
2645         {
2646         return false;
2647         }
2649     DOMString xmlMsg = toXml(msg);
2651     char *fmt =
2652     "<message from='%s' to='%s' type='groupchat'>"
2653     "<body>%s</body></message>\n";
2654     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2655         return false;
2656     /*
2657     char *fmt =
2658     "<message to='%s' type='groupchat'>"
2659     "<body>%s</body></message>\n";
2660     if (!write(fmt, groupJid.c_str(), xmlMsg.c_str()))
2661         return false;
2662     */
2663     return true;
2669 /**
2670  *  Send a message to an individual in a group
2671  */
2672 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2673                                          const DOMString &toNick,
2674                                          const DOMString &msg)
2676     if (!checkConnect())
2677         return false;
2679     DOMString xmlMsg = toXml(msg);
2681     /*
2682     char *fmt =
2683     "<message from='%s' to='%s/%s' type='chat'>"
2684     "<body>%s</body></message>\n";
2685     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2686                toNick.c_str(), xmlMsg.c_str()))
2687         return false;
2688     */
2689     char *fmt =
2690     "<message to='%s/%s' type='chat'>"
2691     "<body>%s</body></message>\n";
2692     if (!write(fmt, groupJid.c_str(),
2693                toNick.c_str(), xmlMsg.c_str()))
2694         return false;
2695     return true;
2701 /**
2702  *  Change your presence within a group
2703  */
2704 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2705                                    const DOMString &myNick,
2706                                    const DOMString &presence)
2708     if (!checkConnect())
2709         return false;
2711     DOMString user = myNick;
2712     if (user.size()<1)
2713         user = username;
2715     DOMString xmlPresence = toXml(presence);
2717     char *fmt =
2718     "<presence to='%s/%s' type='%s'>"
2719     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2720     if (!write(fmt, groupJid.c_str(),
2721                user.c_str(), xmlPresence.c_str()))
2722         return true;
2723     return true;
2730 //########################################################################
2731 //# S T R E A M S
2732 //########################################################################
2735 bool XmppClient::processInBandByteStreamMessage(Element *root)
2737     DOMString from  = root->getAttribute("from");
2738     DOMString id    = root->getAttribute("id");
2739     DOMString type  = root->getAttribute("type");
2741     //### Incoming stream requests
2742     //Input streams are id's by stream id
2743     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2745     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2746         {
2747         DOMString streamId = root->getTagAttribute("open", "sid");
2748         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2749         dispatchXmppEvent(event);
2750         std::map<DOMString, XmppStream *>::iterator iter =
2751                      inputStreams.find(streamId);
2752         if (iter != inputStreams.end())
2753             {
2754             XmppStream *ins = iter->second;
2755             ins->setState(STREAM_OPENING);
2756             ins->setMessageId(id);
2757             return true;
2758             }
2759         return true;
2760         }
2762     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2763         {
2764         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2765         dispatchXmppEvent(event);
2766         DOMString streamId = root->getTagAttribute("close", "sid");
2767         std::map<DOMString, XmppStream *>::iterator iter =
2768                      inputStreams.find(streamId);
2769         if (iter != inputStreams.end())
2770             {
2771             XmppStream *ins = iter->second;
2772             if (from == ins->getPeerId())
2773                 {
2774                 ins->setState(STREAM_CLOSING);
2775                 ins->setMessageId(id);
2776                 return true;
2777                 }
2778             }
2779         return true;
2780         }
2782     else if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2783         {
2784         DOMString streamId = root->getTagAttribute("data", "sid");
2785         std::map<DOMString, XmppStream *>::iterator iter =
2786                      inputStreams.find(streamId);
2787         if (iter != inputStreams.end())
2788             {
2789             XmppStream *ins = iter->second;
2790             if (ins->getState() != STREAM_OPEN)
2791                 {
2792                 XmppEvent event(XmppEvent::EVENT_ERROR);
2793                 event.setFrom(from);
2794                 event.setData("received unrequested stream data");
2795                 dispatchXmppEvent(event);
2796                 return true;
2797                 }
2798             DOMString data = root->getTagValue("data");
2799             std::vector<unsigned char>binData =
2800                        Base64Decoder::decode(data);
2801             ins->receiveData(binData);
2802             }
2803         }
2805     //### Responses to outgoing requests
2806     //Output streams are id's by message id
2807     std::map<DOMString, XmppStream *>::iterator iter =
2808                      outputStreams.find(id);
2809     if (iter != outputStreams.end())
2810         {
2811         XmppStream *outs = iter->second;
2812         if (type == "error")
2813             {
2814             outs->setState(STREAM_ERROR);
2815             return true;
2816             }
2817         else if (type == "result")
2818             {
2819             if (outs->getState() == STREAM_OPENING)
2820                 {
2821                 outs->setState(STREAM_OPEN);
2822                 }
2823             else if (outs->getState() == STREAM_CLOSING)
2824                 {
2825                 outs->setState(STREAM_CLOSED);
2826                 }
2827             return true;
2828             }
2829         }
2831     return false;
2835 /**
2836  *
2837  */
2838 bool XmppClient::outputStreamOpen(const DOMString &destId,
2839                                  const DOMString &streamIdArg)
2841     char buf[32];
2842     snprintf(buf, 31, "inband%d", getMsgId());
2843     DOMString messageId = buf;
2845     //Output streams are id's by message id
2846     XmppStream *outs = new XmppStream();
2847     outputStreams[messageId] = outs;
2849     outs->setState(STREAM_OPENING);
2851     DOMString streamId = streamIdArg;
2852     if (streamId.size()<1)
2853         {
2854         snprintf(buf, 31, "stream%d", getMsgId());
2855         DOMString streamId = buf;
2856         }
2857     outs->setMessageId(messageId);
2858     outs->setStreamId(streamId);
2859     outs->setPeerId(destId);
2862     char *fmt =
2863     "<%s type='set' to='%s' id='%s'>"
2864     "<open sid='%s' block-size='4096'"
2865     " xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
2866     if (!write(fmt,
2867               streamPacket.c_str(),
2868               destId.c_str(), messageId.c_str(),
2869               streamId.c_str(),
2870               streamPacket.c_str()))
2871         {
2872         outs->reset();
2873         return -1;
2874         }
2876     int state = outs->getState();
2877     for (int tim=0 ; tim<20 ; tim++)
2878         {
2879         if (state == STREAM_OPEN)
2880             break;
2881         else if (state == STREAM_ERROR)
2882             {
2883             printf("ERROR\n");
2884             outs->reset();
2885             return false;
2886             }
2887         Thread::sleep(1000);
2888         state = outs->getState();
2889         }
2890     if (state != STREAM_OPEN)
2891         {
2892         printf("TIMEOUT ERROR\n");
2893         outs->reset();
2894         return -1;
2895         }
2897     return true;
2900 /**
2901  *
2902  */
2903 bool XmppClient::outputStreamWrite(const DOMString &streamId,
2904                const std::vector<unsigned char> &buf)
2906     std::map<DOMString, XmppStream *>::iterator iter =
2907         outputStreams.find(streamId);
2908     if (iter == outputStreams.end())
2909         return false;
2910     XmppStream *outs = iter->second;
2912     unsigned int len = buf.size();
2913     unsigned int pos = 0;
2915     while (pos < len)
2916         {
2917         unsigned int pos2 = pos + 1024;
2918         if (pos2>len)
2919             pos2 = len;
2921         Base64Encoder encoder;
2922         for (unsigned int i=pos ; i<pos2 ; i++)
2923             encoder.append(buf[i]);
2924         DOMString b64data = encoder.finish();
2927         char *fmt =
2928         "<message to='%s' id='msg%d'>"
2929         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
2930         "%s"
2931         "</data>"
2932         "<amp xmlns='http://jabber.org/protocol/amp'>"
2933         "<rule condition='deliver-at' value='stored' action='error'/>"
2934         "<rule condition='match-resource' value='exact' action='error'/>"
2935         "</amp>"
2936         "</message>\n";
2937         if (!write(fmt,
2938               outs->getPeerId().c_str(),
2939               getMsgId(),
2940               outs->getStreamId().c_str(),
2941               outs->getSeqNr(),
2942               b64data.c_str()))
2943             {
2944             outs->reset();
2945             return false;
2946             }
2947         pause(5000);
2948         
2949         pos = pos2;
2950         }
2951         
2952     return true;
2955 /**
2956  *
2957  */
2958 bool XmppClient::outputStreamClose(const DOMString &streamId)
2960     std::map<DOMString, XmppStream *>::iterator iter =
2961         outputStreams.find(streamId);
2962     if (iter == outputStreams.end())
2963         return false;
2964     XmppStream *outs = iter->second;
2966     char buf[32];
2967     snprintf(buf, 31, "inband%d", getMsgId());
2968     DOMString messageId = buf;
2969     outs->setMessageId(messageId);
2971     outs->setState(STREAM_CLOSING);
2972     char *fmt =
2973     "<%s type='set' to='%s' id='%s'>"
2974     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
2975     if (!write(fmt,
2976             streamPacket.c_str(),
2977             outs->getPeerId().c_str(),
2978             messageId.c_str(),
2979             outs->getStreamId().c_str(),
2980             streamPacket.c_str()
2981             ))
2982         return false;
2984     int state = outs->getState();
2985     for (int tim=0 ; tim<20 ; tim++)
2986         {
2987         if (state == STREAM_CLOSED)
2988             break;
2989         else if (state == STREAM_ERROR)
2990             {
2991             printf("ERROR\n");
2992             outs->reset();
2993             return false;
2994             }
2995         Thread::sleep(1000);
2996         state = outs->getState();
2997         }
2998     if (state != STREAM_CLOSED)
2999         {
3000         printf("TIMEOUT ERROR\n");
3001         outs->reset();
3002         return false;
3003         }
3005     delete outs;
3006     outputStreams.erase(streamId);
3008     return true;
3012 /**
3013  *
3014  */
3015 bool XmppClient::inputStreamOpen(const DOMString &fromJid,
3016                                 const DOMString &streamId,
3017                                 const DOMString &iqId)
3019     XmppStream *ins = new XmppStream();
3021     inputStreams[streamId] = ins;
3022     ins->reset();
3023     ins->setPeerId(fromJid);
3024     ins->setState(STREAM_CLOSED);
3025     ins->setStreamId(streamId);
3027     int state = ins->getState();
3028     for (int tim=0 ; tim<20 ; tim++)
3029         {
3030         if (state == STREAM_OPENING)
3031             break;
3032         else if (state == STREAM_ERROR)
3033             {
3034             printf("ERROR\n");
3035             ins->reset();
3036             return false;
3037             }
3038         Thread::sleep(1000);
3039         state = ins->getState();
3040         }
3041     if (state != STREAM_OPENING)
3042         {
3043         printf("TIMEOUT ERROR\n");
3044         ins->reset();
3045         return false;
3046         }
3047     char *fmt =
3048     "<%s type='result' to='%s' id='%s'/>\n";
3049     if (!write(fmt, streamPacket.c_str(),
3050            fromJid.c_str(), ins->getMessageId().c_str()))
3051         {
3052         return false;
3053         }
3055     ins->setState(STREAM_OPEN);
3056     return true;
3061 /**
3062  *
3063  */
3064 bool XmppClient::inputStreamClose(const DOMString &streamId)
3066     std::map<DOMString, XmppStream *>::iterator iter =
3067         inputStreams.find(streamId);
3068     if (iter == inputStreams.end())
3069         return false;
3070     XmppStream *ins = iter->second;
3072     if (ins->getState() == STREAM_CLOSING)
3073         {
3074         char *fmt =
3075         "<iq type='result' to='%s' id='%s'/>\n";
3076         if (!write(fmt, ins->getPeerId().c_str(),
3077                     ins->getMessageId().c_str()))
3078             {
3079             return false;
3080             }
3081         }
3082     inputStreams.erase(streamId);
3083     delete ins;
3085     return true;
3093 //########################################################################
3094 //# FILE   TRANSFERS
3095 //########################################################################
3098 bool XmppClient::processFileMessage(Element *root)
3100     DOMString siNamespace = "http://jabber.org/protocol/si";
3101     if (root->getTagAttribute("si", "xmlns") != siNamespace)
3102         return false;
3104     
3105     Element *mainElement = root->getFirstChild();
3106     if (!mainElement)
3107         return false;
3109     DOMString from  = mainElement->getAttribute("from");
3110     DOMString id    = mainElement->getAttribute("id");
3111     DOMString type  = mainElement->getAttribute("type");
3113     status("received file message from %s", from.c_str());
3115     if (type == "set")
3116         {
3117         DOMString streamId = root->getTagAttribute("si", "id");
3118         DOMString fname    = root->getTagAttribute("file", "name");
3119         DOMString sizeStr  = root->getTagAttribute("file", "size");
3120         DOMString hash     = root->getTagAttribute("file", "hash");
3121         XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
3122         event.setFrom(from);
3123         event.setIqId(id);
3124         event.setStreamId(streamId);
3125         event.setFileName(fname);
3126         event.setFileHash(hash);
3127         event.setFileSize(atol(sizeStr.c_str()));
3128         dispatchXmppEvent(event);
3129         return true;
3130         }
3132     //##expecting result or error
3133     //file sends id'd by message id's
3134     std::map<DOMString, XmppStream *>::iterator iter =
3135         fileSends.find(id);
3136     if (iter != fileSends.end())
3137         {
3138         XmppStream *outf = iter->second;
3139         if (from != outf->getPeerId())
3140             return true;
3141         if (type == "error")
3142             {
3143             outf->setState(STREAM_ERROR);
3144             error("user '%s' rejected file", from.c_str());
3145             return true;
3146             }
3147         else if (type == "result")
3148             {
3149             if (outf->getState() == STREAM_OPENING)
3150                 {
3151                 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
3152                 event.setFrom(from);
3153                 dispatchXmppEvent(event);
3154                 outf->setState(STREAM_OPEN);
3155                 }
3156             else if (outf->getState() == STREAM_CLOSING)
3157                 {
3158                 outf->setState(STREAM_CLOSED);
3159                 }
3160             return true;
3161             }
3162         }
3164     return true;
3172 /**
3173  *
3174  */
3175 bool XmppClient::fileSend(const DOMString &destJidArg,
3176                           const DOMString &offeredNameArg,
3177                           const DOMString &fileNameArg,
3178                           const DOMString &descriptionArg)
3180     DOMString destJid     = destJidArg;
3181     DOMString offeredName = offeredNameArg;
3182     DOMString fileName    = fileNameArg;
3183     DOMString description = descriptionArg;
3185     struct stat finfo;
3186     if (stat(fileName.c_str(), &finfo)<0)
3187         {
3188         error("Cannot stat file '%s' for sending", fileName.c_str());
3189         return false;
3190         }
3191     long fileLen = finfo.st_size;
3192     if (!fileLen > 1000000)
3193         {
3194         error("'%s' too large", fileName.c_str());
3195         return false;
3196         }
3197     if (!S_ISREG(finfo.st_mode))
3198         {
3199         error("'%s' is not a regular file", fileName.c_str());
3200         return false;
3201         }
3202     FILE *f = fopen(fileName.c_str(), "rb");
3203     if (!f)
3204         {
3205         error("cannot open '%s' for sending", fileName.c_str());
3206         return false;
3207         }
3208     std::vector<unsigned char> sendBuf;
3209     Md5 md5hash;
3210     for (long i=0 ; i<fileLen && !feof(f); i++)
3211         {
3212         int ch = fgetc(f);
3213         if (ch<0)
3214             break;
3215         md5hash.append((unsigned char)ch);
3216         sendBuf.push_back((unsigned char)ch);
3217         }
3218     fclose(f);
3219     DOMString hash = md5hash.finishHex();
3220     printf("Hash:%s\n", hash.c_str());
3222    
3223     //## get the last path segment from the whole path
3224     if (offeredName.size()<1)
3225         {
3226         int slashPos = -1;
3227         for (unsigned int i=0 ; i<fileName.size() ; i++)
3228             {
3229             int ch = fileName[i];
3230             if (ch == '/' || ch == '\\')
3231                 slashPos = i;
3232             }
3233         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3234             {
3235             offeredName = fileName.substr(slashPos+1,
3236                                           fileName.size()-slashPos-1);
3237             printf("offeredName:%s\n", offeredName.c_str());
3238             }
3239         }
3241     char buf[32];
3242     snprintf(buf, 31, "file%d", getMsgId());
3243     DOMString messageId = buf;
3245     XmppStream *outf = new XmppStream();
3247     outf->setState(STREAM_OPENING);
3248     outf->setMessageId(messageId);
3249     fileSends[messageId] = outf;
3251     snprintf(buf, 31, "stream%d", getMsgId());
3252     DOMString streamId = buf;
3253     //outf->setStreamId(streamId);
3255     outf->setPeerId(destJid);
3257     char dtgBuf[81];
3258     struct tm *timeVal = gmtime(&(finfo.st_mtime));
3259     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3261     char *fmt =
3262     "<%s type='set' id='%s' to='%s'>"
3263     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3264       " mime-type='text/plain'"
3265       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3266     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3267           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3268     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3269     "<x xmlns='jabber:x:data' type='form'>"
3270     "<field var='stream-method' type='list-single'>"
3271     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3272     "<option><value>http://jabber.org/protocol/ibb</value></option>"
3273     "</field></x></feature></si></%s>\n";
3274     if (!write(fmt, streamPacket.c_str(),
3275          messageId.c_str(), destJid.c_str(),
3276          streamId.c_str(), offeredName.c_str(), fileLen,
3277          hash.c_str(), dtgBuf, description.c_str(),
3278          streamPacket.c_str()))
3279         {
3280         return false;
3281         }
3283     int ret = true;
3284     int state = outf->getState();
3285     for (int tim=0 ; tim<20 ; tim++)
3286         {
3287         printf("##### waiting for open\n");
3288         if (state == STREAM_OPEN)
3289             {
3290             outf->reset();
3291             break;
3292             }
3293         else if (state == STREAM_ERROR)
3294             {
3295             printf("ERROR\n");
3296             outf->reset();
3297             ret = false;
3298             }
3299         Thread::sleep(1000);
3300         state = outf->getState();
3301         }
3302     if (state != STREAM_OPEN)
3303         {
3304         printf("TIMEOUT ERROR\n");
3305         ret = false;
3306         }
3308     //free up this resource
3309     fileSends.erase(messageId);
3310     delete outf;
3312     if (!outputStreamOpen(destJid, streamId))
3313         {
3314         error("cannot open output stream %s", streamId.c_str());
3315         return false;
3316         }
3318     if (!outputStreamWrite(streamId, sendBuf))
3319         {
3320         }
3322     if (!outputStreamClose(streamId))
3323         {
3324         }
3326     return true;
3330 class FileSendThread : public Thread
3332 public:
3334     FileSendThread(XmppClient &par,
3335                    const DOMString &destJidArg,
3336                    const DOMString &offeredNameArg,
3337                    const DOMString &fileNameArg,
3338                    const DOMString &descriptionArg) : client(par)
3339         {
3340         destJid     = destJidArg;
3341         offeredName = offeredNameArg;
3342         fileName    = fileNameArg;
3343         description = descriptionArg;
3344         }
3346     virtual ~FileSendThread() {}
3348     void run()
3349       {
3350       client.fileSend(destJid, offeredName,
3351                       fileName, description);
3352       }
3354 private:
3356     XmppClient &client;
3357     DOMString destJid;
3358     DOMString offeredName;
3359     DOMString fileName;
3360     DOMString description;
3361 };
3363 /**
3364  *
3365  */
3366 bool XmppClient::fileSendBackground(const DOMString &destJid,
3367                                     const DOMString &offeredName,
3368                                     const DOMString &fileName,
3369                                     const DOMString &description)
3371     FileSendThread thread(*this, destJid, offeredName,
3372                            fileName, description);
3373     thread.start();
3374     return true;
3378 /**
3379  *
3380  */
3381 bool XmppClient::fileReceive(const DOMString &fromJid,
3382                              const DOMString &iqId,
3383                              const DOMString &streamId,
3384                              const DOMString &fileName,
3385                              long  fileSize,
3386                              const DOMString &fileHash)
3388     char *fmt =
3389     "<%s type='result' to='%s' id='%s'>"
3390     "<si xmlns='http://jabber.org/protocol/si'>"
3391     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3392     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3393     "<x xmlns='jabber:x:data' type='submit'>"
3394     "<field var='stream-method'>"
3395     "<value>http://jabber.org/protocol/ibb</value>"
3396     "</field></x></feature></si></%s>\n";
3397     if (!write(fmt, streamPacket.c_str(),
3398                     fromJid.c_str(), iqId.c_str(),
3399                     streamPacket.c_str()))
3400         {
3401         return false;
3402         }
3404     if (!inputStreamOpen(fromJid, streamId, iqId))
3405         {
3406         return false;
3407         }
3409     XmppStream *ins = inputStreams[streamId];
3411     Md5 md5;
3412     FILE *f = fopen(fileName.c_str(), "wb");
3413     if (!f)
3414         {
3415         return false;
3416         }
3418     while (true)
3419         {
3420         if (ins->available()<1)
3421             {
3422             if (ins->getState() == STREAM_CLOSING)
3423                 break;
3424             pause(100);
3425             continue;
3426             }
3427         std::vector<unsigned char> ret = ins->read();
3428         std::vector<unsigned char>::iterator iter;
3429         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3430             {
3431             unsigned char ch = *iter;
3432             md5.append(&ch, 1);
3433             fwrite(&ch, 1, 1, f);
3434             }
3435         }
3437     inputStreamClose(streamId);
3438     fclose(f);
3440     DOMString hash = md5.finishHex();
3441     printf("received file hash:%s\n", hash.c_str());
3443     return true;
3448 class FileReceiveThread : public Thread
3450 public:
3452     FileReceiveThread(XmppClient &par,
3453                       const DOMString &fromJidArg,
3454                       const DOMString &iqIdArg,
3455                       const DOMString &streamIdArg,
3456                       const DOMString &fileNameArg,
3457                       long  fileSizeArg,
3458                       const DOMString &fileHashArg) : client(par)
3459         {
3460         fromJid     = fromJidArg;
3461         iqId        = iqIdArg;
3462         streamId    = streamIdArg;
3463         fileName    = fileNameArg;
3464         fileSize    = fileSizeArg;
3465         fileHash    = fileHashArg;
3466         }
3468     virtual ~FileReceiveThread() {}
3470     void run()
3471       {
3472       client.fileReceive(fromJid, iqId, streamId,
3473                         fileName, fileSize, fileHash);
3474       }
3476 private:
3478     XmppClient &client;
3479     DOMString fromJid;
3480     DOMString iqId;
3481     DOMString streamId;
3482     DOMString fileName;
3483     long      fileSize;
3484     DOMString fileHash;
3485 };
3487 /**
3488  *
3489  */
3490 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3491                                        const DOMString &iqId,
3492                                        const DOMString &streamId,
3493                                        const DOMString &fileName,
3494                                        long  fileSize,
3495                                        const DOMString &fileHash)
3497     FileReceiveThread thread(*this, fromJid, iqId, streamId,
3498                   fileName, fileSize, fileHash);
3499     thread.start();
3500     return true;
3505 //########################################################################
3506 //# X M P P    G R O U P    C H A T
3507 //########################################################################
3509 /**
3510  *
3511  */
3512 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3514     groupJid = groupJidArg;
3517 /**
3518  *
3519  */
3520 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3522     groupJid = other.groupJid;
3523     userList = other.userList;
3526 /**
3527  *
3528  */
3529 XmppGroupChat::~XmppGroupChat()
3534 /**
3535  *
3536  */
3537 DOMString XmppGroupChat::getGroupJid()
3539     return groupJid;
3543 void XmppGroupChat::userAdd(const DOMString &nick,
3544                             const DOMString &jid)
3546     std::vector<XmppUser>::iterator iter;
3547     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3548         {
3549         if (iter->nick == nick)
3550             return;
3551         }
3552     XmppUser user(jid, nick);
3553     userList.push_back(user);
3556 void XmppGroupChat::userShow(const DOMString &nick,
3557                              const DOMString &show)
3559     DOMString theShow = show;
3560     if (theShow == "")
3561         theShow = "available"; // a join message will now have a show
3562     std::vector<XmppUser>::iterator iter;
3563     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3564         {
3565         if (iter->nick == nick)
3566             iter->show = theShow;
3567         }
3570 void XmppGroupChat::userDelete(const DOMString &nick)
3572     std::vector<XmppUser>::iterator iter;
3573     for (iter= userList.begin() ; iter!=userList.end() ; )
3574         {
3575         if (iter->nick == nick)
3576             iter = userList.erase(iter);
3577         else
3578             iter++;
3579         }
3582 std::vector<XmppUser> XmppGroupChat::getUserList() const
3584     return userList;
3595 } //namespace Pedro
3596 //########################################################################
3597 //# E N D    O F     F I L E
3598 //########################################################################