Code

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