Code

fix shift+middle button zoom area when there are other modifiers in the mask; safe...
[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-2008 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 <algorithm>
26 #include <cstdio>
27 #include <stdarg.h>
28 #include <stdlib.h>
30 #include <sys/stat.h>
32 #include <time.h>
34 #include "pedroxmpp.h"
35 #include "pedrodom.h"
36 #include "pedroutil.h"
38 #include <map>
42 namespace Pedro
43 {
46 //########################################################################
47 //########################################################################
48 //# X M P P    E V E N T
49 //########################################################################
50 //########################################################################
53 XmppEvent::XmppEvent(int type)
54 {
55     eventType = type;
56     presence  = false;
57     dom       = NULL;
58 }
60 XmppEvent::XmppEvent(const XmppEvent &other)
61 {
62     assign(other);
63 }
65 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
66 {
67     assign(other);
68     return (*this);
69 }
71 XmppEvent::~XmppEvent()
72 {
73     if (dom)
74         delete dom;
75 }
77 void XmppEvent::assign(const XmppEvent &other)
78 {
79     eventType = other.eventType;
80     presence  = other.presence;
81     status    = other.status;
82     show      = other.show;
83     to        = other.to;
84     from      = other.from;
85     group     = other.group;
86     data      = other.data;
87     fileName  = other.fileName;
88     fileDesc  = other.fileDesc;
89     fileSize  = other.fileSize;
90     fileHash  = other.fileHash;
91     setDOM(other.dom);
92 }
94 int XmppEvent::getType() const
95 {
96     return eventType;
97 }
99 DOMString XmppEvent::getIqId() const
101     return iqId;
104 void XmppEvent::setIqId(const DOMString &val)
106     iqId = val;
109 DOMString XmppEvent::getStreamId() const
111     return streamId;
114 void XmppEvent::setStreamId(const DOMString &val)
116     streamId = val;
119 bool XmppEvent::getPresence() const
121     return presence;
124 void XmppEvent::setPresence(bool val)
126     presence = val;
129 DOMString XmppEvent::getShow() const
131     return show;
134 void XmppEvent::setShow(const DOMString &val)
136     show = val;
139 DOMString XmppEvent::getStatus() const
141     return status;
144 void XmppEvent::setStatus(const DOMString &val)
146     status = val;
149 DOMString XmppEvent::getTo() const
151     return to;
154 void XmppEvent::setTo(const DOMString &val)
156     to = val;
159 DOMString XmppEvent::getFrom() const
161     return from;
164 void XmppEvent::setFrom(const DOMString &val)
166     from = val;
169 DOMString XmppEvent::getGroup() const
171     return group;
174 void XmppEvent::setGroup(const DOMString &val)
176     group = val;
179 DOMString XmppEvent::getData() const
181     return data;
184 void XmppEvent::setData(const DOMString &val)
186     data = val;
189 DOMString XmppEvent::getFileName() const
191     return fileName;
194 void XmppEvent::setFileName(const DOMString &val)
196     fileName = val;
199 DOMString XmppEvent::getFileDesc() const
201     return fileDesc;
204 void XmppEvent::setFileDesc(const DOMString &val)
206     fileDesc = val;
209 long XmppEvent::getFileSize() const
211     return fileSize;
214 void XmppEvent::setFileSize(long val)
216     fileSize = val;
219 DOMString XmppEvent::getFileHash() const
221     return fileHash;
224 void XmppEvent::setFileHash(const DOMString &val)
226     fileHash = val;
229 Element *XmppEvent::getDOM() const
231     return dom;
234 void XmppEvent::setDOM(const Element *val)
236     if (!val)
237         dom = NULL;
238     else
239         dom = ((Element *)val)->clone();
243 std::vector<XmppUser> XmppEvent::getUserList() const
245     return userList;
248 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
250     userList = val;
261 //########################################################################
262 //########################################################################
263 //# X M P P    E V E N T    T A R G E T
264 //########################################################################
265 //########################################################################
268 //###########################
269 //# CONSTRUCTORS
270 //###########################
272 XmppEventTarget::XmppEventTarget()
274     eventQueueEnabled = false;
278 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
280     listeners         = other.listeners;
281     eventQueueEnabled = other.eventQueueEnabled;
284 XmppEventTarget::~XmppEventTarget()
289 //###########################
290 //# M E S S A G E S
291 //###########################
293 /**
294  *  Print a printf()-like formatted error message
295  */
296 void XmppEventTarget::error(const char *fmt, ...)
298     va_list args;
299     va_start(args,fmt);
300     gchar * buffer = g_strdup_vprintf(fmt, args);
301     va_end(args) ;
302     fprintf(stderr, "Error:%s\n", buffer);
303     XmppEvent evt(XmppEvent::EVENT_ERROR);
304     evt.setData(buffer);
305     dispatchXmppEvent(evt);
306     g_free(buffer);
311 /**
312  *  Print a printf()-like formatted trace message
313  */
314 void XmppEventTarget::status(const char *fmt, ...)
316     va_list args;
317     va_start(args,fmt);
318     gchar * buffer = g_strdup_vprintf(fmt, args);
319     va_end(args) ;
320     //printf("Status:%s\n", buffer);
321     XmppEvent evt(XmppEvent::EVENT_STATUS);
322     evt.setData(buffer);
323     dispatchXmppEvent(evt);
324     g_free(buffer);
329 //###########################
330 //# L I S T E N E R S
331 //###########################
333 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
335     std::vector<XmppEventListener *>::iterator iter;
336     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
337         (*iter)->processXmppEvent(event);
338     if (eventQueueEnabled)
339         eventQueue.push_back(event);
342 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
344     XmppEventListener *lsnr = (XmppEventListener *)&listener;
345     std::vector<XmppEventListener *>::iterator iter;
346     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
347         if (*iter == lsnr)
348             return;
349     listeners.push_back(lsnr);
352 void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
354     XmppEventListener *lsnr = (XmppEventListener *)&listener;
355     std::vector<XmppEventListener *>::iterator iter;
356     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
357         if (*iter == lsnr)
358             listeners.erase(iter);
361 void XmppEventTarget::clearXmppEventListeners()
363     listeners.clear();
367 //###########################
368 //# E V E N T    Q U E U E
369 //###########################
371 void XmppEventTarget::eventQueueEnable(bool val)
373     eventQueueEnabled = val;
374     if (!eventQueueEnabled)
375         eventQueue.clear();
378 int XmppEventTarget::eventQueueAvailable()
380     return eventQueue.size();
383 XmppEvent XmppEventTarget::eventQueuePop()
385     if (!eventQueueEnabled || eventQueue.size()<1)
386         {
387         XmppEvent dummy(XmppEvent::EVENT_NONE);
388         return dummy;
389         }
390     XmppEvent event = *(eventQueue.begin());
391     eventQueue.erase(eventQueue.begin());
392     return event;
399 //########################################################################
400 //########################################################################
401 //# X M P P    S T R E A M
402 //########################################################################
403 //########################################################################
406 /**
407  *
408  */
409 class XmppStream
411 public:
413     /**
414      *
415      */
416     XmppStream()
417         { reset(); }
419     /**
420      *
421      */
422     XmppStream(const XmppStream &other)
423         { assign(other); }
425     /**
426      *
427      */
428     XmppStream &operator=(const XmppStream &other)
429         { assign(other); return *this; }
431     /**
432      *
433      */
434     virtual ~XmppStream()
435         {}
437     /**
438      *
439      */
440     virtual void reset()
441         {
442         state     = XmppClient::STREAM_AVAILABLE;
443         seqNr     = 0;
444         messageId = "";
445         sourceId  = "";
446         data.clear();
447         }
449     /**
450      *
451      */
452     virtual int getState()
453         { return state; }
455     /**
456      *
457      */
458     virtual void setState(int val)
459         { state = val; }
461     /**
462      *
463      */
464     virtual DOMString getStreamId()
465         { return streamId; }
467     /**
468      *
469      */
470     void setStreamId(const DOMString &val)
471         { streamId = val; }
473     /**
474      *
475      */
476     virtual DOMString getMessageId()
477         { return messageId; }
479     /**
480      *
481      */
482     void setMessageId(const DOMString &val)
483         { messageId = val; }
485     /**
486      *
487      */
488     virtual int getSeqNr()
489         {
490         seqNr++;
491         if (seqNr >= 65535)
492             seqNr = 0;
493         return seqNr;
494         }
496     /**
497      *
498      */
499     virtual DOMString getPeerId()
500         { return sourceId; }
502     /**
503      *
504      */
505     virtual void setPeerId(const DOMString &val)
506         { sourceId = val; }
508     /**
509      *
510      */
511     int available()
512         { return data.size(); }
514     /**
515      *
516      */
517     void receiveData(std::vector<unsigned char> &newData)
518         {
519         std::vector<unsigned char>::iterator iter;
520         for (iter=newData.begin() ; iter!=newData.end() ; iter++)
521             data.push_back(*iter);
522         }
524     /**
525      *
526      */
527     std::vector<unsigned char> read()
528         {
529         if (state != XmppClient::STREAM_OPEN)
530             {
531             std::vector<unsigned char>dummy;
532             return dummy;
533             }
534         std::vector<unsigned char> ret = data;
535         data.clear();
536         return ret;
537         }
539 private:
541     void assign(const XmppStream &other)
542        {
543        streamId  = other.streamId;
544        messageId = other.messageId;
545        sourceId  = other.sourceId;
546        state     = other.state;
547        seqNr     = other.seqNr;
548        data      = other.data;
549        }
552     DOMString streamId;
554     DOMString messageId;
556     DOMString sourceId;
558     int state;
560     long seqNr;
562     std::vector<unsigned char> data;
563 };
574 //########################################################################
575 //########################################################################
576 //# X M P P    C L I E N T
577 //########################################################################
578 //########################################################################
580 class ReceiverThread : public Runnable
582 public:
584     ReceiverThread(XmppClient &par) : client(par) {}
586     virtual ~ReceiverThread() {}
588     void run()
589       { client.receiveAndProcessLoop(); }
591 private:
593     XmppClient &client;
594 };
600 //########################################################################
601 //# CONSTRUCTORS
602 //########################################################################
604 XmppClient::XmppClient()
606     init();
610 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
612     init();
613     assign(other);
616 void XmppClient::assign(const XmppClient &other)
618     msgId         = other.msgId;
619     host          = other.host;
620     realm         = other.realm;
621     port          = other.port;
622     username      = other.username;
623     password      = other.password;
624     resource      = other.resource;
625     connected     = other.connected;
626     doRegister    = other.doRegister;
627     groupChats    = other.groupChats;
628     streamPacket  = other.streamPacket;
632 void XmppClient::init()
634     sock          = new TcpSocket();
635     msgId         = 0;
636     connected     = false;
637     doRegister    = false;
638     streamPacket  = "message";
642 XmppClient::~XmppClient()
644     disconnect();
645     delete sock;
646     std::map<DOMString, XmppStream *>::iterator iter;
647     for (iter = outputStreams.begin(); iter!=outputStreams.end() ; iter++)
648         delete iter->second;
649     for (iter = inputStreams.begin(); iter!=inputStreams.end() ; iter++)
650         delete iter->second;
651     for (iter = fileSends.begin(); iter!=fileSends.end() ; iter++)
652         delete iter->second;
653     groupChatsClear();
661 //########################################################################
662 //# UTILILY
663 //########################################################################
665 /**
666  *
667  */
668 bool XmppClient::pause(unsigned long millis)
670     Thread::sleep(millis);
671     return true;
675 static int strIndex(const DOMString &str, const char *key)
677     unsigned int p = str.find(key);
678     if (p == str.npos)
679         return -1;
680     return p;
684 DOMString XmppClient::toXml(const DOMString &str)
686     return Parser::encode(str);
691 static DOMString trim(const DOMString &str)
693     unsigned int i;
694     for (i=0 ; i<str.size() ; i++)
695         if (!isspace(str[i]))
696             break;
697     int start = i;
698     for (i=str.size() ; i>0 ; i--)
699         if (!isspace(str[i-1]))
700             break;
701     int end = i;
702     if (start>=end)
703         return "";
704     return str.substr(start, end);
711 //########################################################################
712 //# VARIABLES  (ones that need special handling)
713 //########################################################################
715 /**
716  *
717  */
718 DOMString XmppClient::getUsername()
720     return username;
723 /**
724  *
725  */
726 void XmppClient::setUsername(const DOMString &val)
728     int p = strIndex(val, "@");
729     if (p > 0)
730         {
731         username = val.substr(0, p);
732         realm    = val.substr(p+1, jid.size()-p-1);
733         }
734     else
735        {
736        realm    = host;
737        username = val;
738        }
748 //########################################################################
749 //# RECEIVING
750 //########################################################################
753 DOMString XmppClient::readStanza()
756     int  openCount    = 0;
757     bool inTag        = false;
758     bool slashSeen    = false;
759     bool trivialTag   = false;
760     bool querySeen    = false;
761     bool inQuote      = false;
762     bool textSeen     = false;
763     DOMString buf;
766     time_t timeout = time((time_t *)0) + 180;
768     while (true)
769         {
770         int ch = sock->read();
771         //printf("%c", ch); fflush(stdout);
772         if (ch<0)
773             {
774             if (ch == -2) //a simple timeout, not an error
775                 {
776                 //Since we are timed out, let's assume that we
777                 //are between chunks of text.  Let's reset all states.
778                 //printf("-----#### Timeout\n");
779                 time_t currentTime = time((time_t *)0);
780                 if (currentTime > timeout)
781                     {
782                     timeout = currentTime + 180;
783                     if (!write("\n"))
784                         {
785                         error("ping send error");
786                         disconnect();
787                         return "";
788                         }
789                     }
790                 continue;
791                 }
792             else
793                 {
794                 keepGoing = false;
795                 if (!sock->isConnected())
796                     {
797                     disconnect();
798                     return "";
799                     }
800                 else
801                     {
802                     error("socket read error: %s", sock->getLastError().c_str());
803                     disconnect();
804                     return "";
805                     }
806                 }
807             }
808         buf.push_back(ch);
809         if (ch == '<')
810             {
811             inTag      = true;
812             slashSeen  = false;
813             querySeen  = false;
814             inQuote    = false;
815             textSeen   = false;
816             trivialTag = false;
817             }
818         else if (ch == '>')
819             {
820             if (!inTag)  //unescaped '>' in pcdata? horror
821                 continue;
822             inTag     = false;
823             if (!trivialTag && !querySeen)
824                 {
825                 if (slashSeen)
826                     openCount--;
827                 else
828                     openCount++;
829                 }
830             //printf("# openCount:%d t:%d q:%d\n",
831             //      openCount, trivialTag, querySeen);
832             //check if we are 'balanced', but not a <?version?> tag
833             if (openCount <= 0 && !querySeen)
834                 {
835                 break;
836                 }
837             //we know that this one will be open-ended
838             if (strIndex(buf, "<stream:stream") >= 0)
839                 {
840                 buf.append("</stream:stream>");
841                 break;
842                 }
843             }
844         else if (ch == '/')
845             {
846             if (inTag && !inQuote)
847                 {
848                 slashSeen = true;
849                 if (textSeen) // <tagName/>  <--looks like this
850                     trivialTag = true;
851                 }
852             }
853         else if (ch == '?')
854             {
855             if (inTag && !inQuote)
856                 querySeen = true;
857             }
858         else if (ch == '"' || ch == '\'')
859             {
860             if (inTag)
861                 inQuote = !inQuote;
862             }
863         else
864             {
865             if (inTag && !inQuote && !isspace(ch))
866                 textSeen = true;
867             }
868         }
869     return buf;
874 static bool isGroupChat(Element *root)
876     if (!root)
877         return false;
878     ElementList elems = root->findElements("x");
879     for (unsigned int i=0 ; i<elems.size() ; i++)
880         {
881         DOMString xmlns = elems[i]->getAttribute("xmlns");
882         //printf("### XMLNS ### %s\n", xmlns.c_str());
883         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
884             return true;
885         }
886    return false;
892 static bool parseJid(const DOMString &fullJid,
893              DOMString &jid, DOMString &resource)
895     DOMString str = fullJid;
896     jid.clear();
897     resource.clear();
898     unsigned int p = str.size();
899     unsigned int p2 = str.rfind('/', p);
900     if (p2 != str.npos)
901         {
902         resource = str.substr(p2+1, p-(p2+1));
903         str = str.substr(0, p);
904         p = p2;
905         }
906     jid = str.substr(0, p);
907     printf("fullJid:%s jid:%s rsrc:%s\n",
908         fullJid.c_str(), jid.c_str(), resource.c_str());
909     return true;
915 bool XmppClient::processMessage(Element *root)
917     DOMString from    = root->getTagAttribute("message", "from");
918     DOMString to      = root->getTagAttribute("message", "to");
919     DOMString type    = root->getTagAttribute("message", "type");
921     //####Check for embedded namespaces here
922     //### FILE TRANSFERS
923     if (processFileMessage(root))
924         return true;
926     //### STREAMS
927     if (processInBandByteStreamMessage(root))
928         return true;
931     //#### NORMAL MESSAGES
932     DOMString subject = root->getTagValue("subject");
933     DOMString body    = root->getTagValue("body");
934     DOMString thread  = root->getTagValue("thread");
935     //##rfc 3921, para 2.4.  ignore if no recognizable info
936     //if (subject.size() < 1 && thread.size()<1)
937     //    return true;
939     if (type == "groupchat")
940         {
941         DOMString fromGid;
942         DOMString fromNick;
943         parseJid(from, fromGid, fromNick);
944         //printf("fromGid:%s  fromNick:%s\n",
945         //        fromGid.c_str(), fromNick.c_str());
946         DOMString toGid;
947         DOMString toNick;
948         parseJid(to, toGid, toNick);
949         //printf("toGid:%s  toNick:%s\n",
950         //        toGid.c_str(), toNick.c_str());
952         if (fromNick.size() > 0)//normal group message
953             {
954             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
955             event.setGroup(fromGid);
956             event.setFrom(fromNick);
957             event.setData(body);
958             event.setDOM(root);
959             dispatchXmppEvent(event);
960             }
961         else // from the server itself
962             {
963             //note the space before, so it doesnt match 'unlocked'
964             if (strIndex(body, " locked") >= 0)
965                 {
966                 printf("LOCKED!! ;)\n");
967                 const char *fmt =
968                 "<iq id='create%d' to='%s' type='set'>"
969                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
970                 "<x xmlns='jabber:x:data' type='submit'/>"
971                 "</query></iq>\n";
972                 if (!write(fmt, msgId++, fromGid.c_str()))
973                     return false;
974                 }
975             }
976         }
977     else
978         {
979         XmppEvent event(XmppEvent::EVENT_MESSAGE);
980         event.setFrom(from);
981         event.setData(body);
982         event.setDOM(root);
983         dispatchXmppEvent(event);
984         }
986     return true;
992 bool XmppClient::processPresence(Element *root)
995     DOMString fullJid     = root->getTagAttribute("presence", "from");
996     DOMString to          = root->getTagAttribute("presence", "to");
997     DOMString presenceStr = root->getTagAttribute("presence", "type");
998     bool presence = true;
999     if (presenceStr == "unavailable")
1000         presence = false;
1001     DOMString status      = root->getTagValue("status");
1002     DOMString show        = root->getTagValue("show");
1004     if (isGroupChat(root))
1005         {
1006         DOMString fromGid;
1007         DOMString fromNick;
1008         parseJid(fullJid, fromGid, fromNick);
1009         //printf("fromGid:%s  fromNick:%s\n",
1010         //        fromGid.c_str(), fromNick.c_str());
1011         DOMString item_jid = root->getTagAttribute("item", "jid");
1012         if (item_jid == jid || item_jid == to) //Me
1013             {
1014             if (presence)
1015                 {
1016                 groupChatCreate(fromGid);
1017                 groupChatUserAdd(fromGid, fromNick, "");
1018                 groupChatUserShow(fromGid, fromNick, "available");
1020                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
1021                 event.setGroup(fromGid);
1022                 event.setFrom(fromNick);
1023                 event.setPresence(presence);
1024                 event.setShow(show);
1025                 event.setStatus(status);
1026                 dispatchXmppEvent(event);
1027                 }
1028             else
1029                 {
1030                 groupChatDelete(fromGid);
1031                 groupChatUserDelete(fromGid, fromNick);
1033                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
1034                 event.setGroup(fromGid);
1035                 event.setFrom(fromNick);
1036                 event.setPresence(presence);
1037                 event.setShow(show);
1038                 event.setStatus(status);
1039                 dispatchXmppEvent(event);
1040                 }
1041             }
1042         else // someone else
1043             {
1044             if (presence)
1045                 {
1046                 groupChatUserAdd(fromGid, fromNick, "");
1047                 }
1048             else
1049                 groupChatUserDelete(fromGid, fromNick);
1050             groupChatUserShow(fromGid, fromNick, show);
1051             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
1052             event.setGroup(fromGid);
1053             event.setFrom(fromNick);
1054             event.setPresence(presence);
1055             event.setShow(show);
1056             event.setStatus(status);
1057             dispatchXmppEvent(event);
1058             }
1059         }
1060     else
1061         {
1062         DOMString shortJid;
1063         DOMString dummy;
1064         parseJid(fullJid, shortJid, dummy);
1065         rosterShow(shortJid, show); //users in roster do not have resource
1067         XmppEvent event(XmppEvent::EVENT_PRESENCE);
1068         event.setFrom(fullJid);
1069         event.setPresence(presence);
1070         event.setShow(show);
1071         event.setStatus(status);
1072         dispatchXmppEvent(event);
1073         }
1075     return true;
1080 bool XmppClient::processIq(Element *root)
1082     DOMString from  = root->getTagAttribute("iq", "from");
1083     DOMString id    = root->getTagAttribute("iq", "id");
1084     DOMString type  = root->getTagAttribute("iq", "type");
1085     DOMString xmlns = root->getTagAttribute("query", "xmlns");
1087     if (id.size()<1)
1088         return true;
1090     //Group chat
1091     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
1092         {
1093         printf("results of MUC query\n");
1094         }
1095     //printf("###IQ xmlns:%s\n", xmlns.c_str());
1097     //### FILE TRANSFERS
1098     if (processFileMessage(root))
1099         return true;
1101     //### STREAMS
1102     if (processInBandByteStreamMessage(root))
1103         return true;
1104         
1106     //###Normal Roster stuff
1107     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
1108         {
1109         roster.clear();
1110         ElementList elems = root->findElements("item");
1111         for (unsigned int i=0 ; i<elems.size() ; i++)
1112             {
1113             Element *item = elems[i];
1114             DOMString userJid      = item->getAttribute("jid");
1115             DOMString name         = item->getAttribute("name");
1116             DOMString subscription = item->getAttribute("subscription");
1117             DOMString group        = item->getTagValue("group");
1118             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
1119             //        subscription.c_str(), group.c_str());
1120             XmppUser user(userJid, name, subscription, group);
1121             roster.push_back(user);
1122             }
1123         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
1124         dispatchXmppEvent(event);
1125         }
1127     else if (id.find("regnew") != id.npos)
1128         {
1130         }
1132     else if (id.find("regpass") != id.npos)
1133         {
1134         ElementList list = root->findElements("error");
1135         if (list.size()==0)
1136             {
1137             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
1138             evt.setTo(username);
1139             evt.setFrom(host);
1140             dispatchXmppEvent(evt);
1141             return true;
1142             }
1144         Element *errElem = list[0];
1145         DOMString errMsg = "Password change error: ";
1146         if (errElem->findElements("bad-request").size()>0)
1147             {
1148             errMsg.append("password change does not contain complete information");
1149             }
1150         else if (errElem->findElements("not-authorized").size()>0)
1151             {
1152             errMsg.append("server does not consider the channel safe "
1153                           "enough to enable a password change");
1154             }
1155         else if (errElem->findElements("not-allowed").size()>0)
1156             {
1157             errMsg.append("server does not allow password changes");
1158             }
1159         else if (errElem->findElements("unexpected-request").size()>0)
1160             {
1161             errMsg.append(
1162              "IQ set does not contain a 'from' address because "
1163              "the entity is not registered with the server");
1164             }
1165         error("%s", errMsg.c_str());
1166         }
1168     else if (id.find("regcancel") != id.npos)
1169         {
1170         ElementList list = root->findElements("error");
1171         if (list.size()==0)
1172             {
1173             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
1174             evt.setTo(username);
1175             evt.setFrom(host);
1176             dispatchXmppEvent(evt);
1177             return true;
1178             }
1180         Element *errElem = list[0];
1181         DOMString errMsg = "Registration cancel error: ";
1182         if (errElem->findElements("bad-request").size()>0)
1183             {
1184             errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
1185             }
1186         else if (errElem->findElements("forbidden").size()>0)
1187             {
1188             errMsg.append("sender does not have sufficient permissions to cancel the registration");
1189             }
1190         else if (errElem->findElements("not-allowed").size()>0)
1191             {
1192             errMsg.append("not allowed to cancel registrations in-band");
1193             }
1194         else if (errElem->findElements("registration-required").size()>0)
1195             {
1196             errMsg.append("not previously registered");
1197             }
1198         else if (errElem->findElements("unexpected-request").size()>0)
1199             {
1200             errMsg.append(
1201                  "IQ set does not contain a 'from' address because "
1202                  "the entity is not registered with the server");
1203             }
1204         error("%s", errMsg.c_str());
1205         }
1207     return true;
1214 bool XmppClient::receiveAndProcess()
1216     if (!keepGoing)
1217         return false;
1219     Parser parser;
1221     DOMString recvBuf = readStanza();
1222     recvBuf = trim(recvBuf);
1223     if (recvBuf.size() < 1)
1224         return true;
1226     //Ugly hack.  Apparently the first char can be dropped on timeouts
1227     //if (recvBuf[0] != '<')
1228     //    recvBuf.insert(0, "<");
1230     status("RECV: %s", recvBuf.c_str());
1231     Element *root = parser.parse(recvBuf);
1232     if (!root)
1233         {
1234         printf("Bad elem\n");
1235         return true;
1236         }
1238     //#### MESSAGE
1239     ElementList elems = root->findElements("message");
1240     if (elems.size()>0)
1241         {
1242         if (!processMessage(root))
1243             return false;
1244         }
1246     //#### PRESENCE
1247     elems = root->findElements("presence");
1248     if (elems.size()>0)
1249         {
1250         if (!processPresence(root))
1251             return false;
1252         }
1254     //#### INFO
1255     elems = root->findElements("iq");
1256     if (elems.size()>0)
1257         {
1258         if (!processIq(root))
1259             return false;
1260         }
1262     delete root;
1264     return true;
1268 bool XmppClient::receiveAndProcessLoop()
1270     keepGoing = true;
1271     while (true)
1272         {
1273         if (!keepGoing)
1274             {
1275             status("Abort requested");
1276             break;
1277             }
1278         if (!receiveAndProcess())
1279             return false;
1280         }
1281     return true;
1287 //########################################################################
1288 //# SENDING
1289 //########################################################################
1292 bool XmppClient::write(const char *fmt, ...)
1294     bool rc = true;
1295     va_list args;
1296     va_start(args,fmt);
1297     gchar * buffer = g_strdup_vprintf(fmt,args);
1298     va_end(args) ;
1299     status("SEND: %s", buffer);
1300     if (!sock->write(buffer))
1301         {
1302         error("Cannot write to socket: %s", sock->getLastError().c_str());
1303         rc = false;
1304         }
1305     g_free(buffer);
1306     return rc;
1314 //########################################################################
1315 //# R E G I S T R A T I O N
1316 //########################################################################
1318 /**
1319  * Perform JEP-077 In-Band Registration.  Performed synchronously after SSL,
1320  * before authentication
1321  */
1322 bool XmppClient::inBandRegistrationNew()
1324     Parser parser;
1326     const char *fmt =
1327      "<iq type='get' id='regnew%d'>"
1328          "<query xmlns='jabber:iq:register'/>"
1329          "</iq>\n\n";
1330     if (!write(fmt, msgId++))
1331         return false;
1333     DOMString recbuf = readStanza();
1334     status("RECV reg: %s", recbuf.c_str());
1335     Element *elem = parser.parse(recbuf);
1336     //elem->print();
1338     //# does the entity send the newer "instructions" tag?
1339     ElementList fields = elem->findElements("field");
1340     std::vector<DOMString> fnames;
1341     for (unsigned int i=0; i<fields.size() ; i++)
1342         {
1343         DOMString fname = fields[i]->getAttribute("var");
1344         if (fname == "FORM_TYPE")
1345             continue;
1346         fnames.push_back(fname);
1347         status("field name:%s", fname.c_str());
1348         }
1350     //Do we have any fields?
1351     if (fnames.size() == 0)
1352         {
1353         //If no fields, maybe the older method was offered
1354         if (elem->findElements("username").size() == 0 ||
1355             elem->findElements("password").size() == 0)
1356             {
1357             error("server did not offer registration");
1358             delete elem;
1359             return false;
1360             }
1361         }
1363     delete elem;
1365     fmt =
1366      "<iq type='set' id='regnew%d'>"
1367          "<query xmlns='jabber:iq:register'>"
1368          "<username>%s</username>"
1369          "<password>%s</password>"
1370          "<email/><name/>"
1371          "</query>"
1372          "</iq>\n\n";
1373     if (!write(fmt, msgId++, toXml(username).c_str(),
1374                     toXml(password).c_str() ))
1375         return false;
1378     recbuf = readStanza();
1379     status("RECV reg: %s", recbuf.c_str());
1380     elem = parser.parse(recbuf);
1381     //elem->print();
1383     ElementList list = elem->findElements("error");
1384     if (list.size()>0)
1385         {
1386         Element *errElem = list[0];
1387         DOMString code = errElem->getAttribute("code");
1388         DOMString errMsg = "Registration error: ";
1389         if (code == "409")
1390             {
1391             errMsg.append("conflict with existing user name");
1392             }
1393         else if (code == "406")
1394             {
1395             errMsg.append("some registration information was not provided");
1396             }
1397         error("%s", errMsg.c_str());
1398         delete elem;
1399         return false;
1400         }
1402     delete elem;
1404     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1405     evt.setTo(username);
1406     evt.setFrom(host);
1407     dispatchXmppEvent(evt);
1409     return true;
1413 /**
1414  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1415  * See processIq() for response handling.
1416  */
1417 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1419     Parser parser;
1421     //# Let's try it form-style to allow the common old/new password thing
1422     const char *fmt =
1423       "<iq type='set' id='regpass%d' to='%s'>"
1424       "  <query xmlns='jabber:iq:register'>"
1425       "    <x xmlns='jabber:x:data' type='form'>"
1426       "      <field type='hidden' var='FORM_TYPE'>"
1427       "        <value>jabber:iq:register:changepassword</value>"
1428       "      </field>"
1429       "      <field type='text-single' var='username'>"
1430       "        <value>%s</value>"
1431       "      </field>"
1432       "      <field type='text-private' var='old_password'>"
1433       "        <value>%s</value>"
1434       "      </field>"
1435       "      <field type='text-private' var='password'>"
1436       "        <value>%s</value>"
1437       "      </field>"
1438       "    </x>"
1439       "  </query>"
1440       "</iq>\n\n";
1442     if (!write(fmt, msgId++, host.c_str(),
1443              username.c_str(), password.c_str(), newpassword.c_str()))
1444         return false;
1446     return true;
1451 /**
1452  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1453  * See processIq() for response handling.
1454  */
1455 bool XmppClient::inBandRegistrationCancel()
1457     Parser parser;
1459     const char *fmt =
1460      "<iq type='set' id='regcancel%d'>"
1461      "<query xmlns='jabber:iq:register'><remove/></query>"
1462      "</iq>\n\n";  
1463     if (!write(fmt, msgId++))
1464         return false;
1466     return true;
1473 //########################################################################
1474 //# A U T H E N T I C A T E
1475 //########################################################################
1479 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1481     Parser parser;
1483     const char *fmt =
1484         "<iq type='get' to='%s' id='auth%d'>"
1485         "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1486         "</iq>\n";
1487     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1488         return false;
1490     DOMString recbuf = readStanza();
1491     status("iq auth recv: '%s'\n", recbuf.c_str());
1492     Element *elem = parser.parse(recbuf);
1493     //elem->print();
1494     DOMString iqType = elem->getTagAttribute("iq", "type");
1495     //printf("##iqType:%s\n", iqType.c_str());
1496     delete elem;
1498     if (iqType != "result")
1499         {
1500         error("error:server does not allow login");
1501         return false;
1502         }
1504     bool digest = true;
1505     if (digest)
1506         {
1507         //## Digest authentication
1508         DOMString digest = streamId;
1509         digest.append(password);
1510         digest = Sha1::hashHex(digest);
1511         //printf("digest:%s\n", digest.c_str());
1512         fmt =
1513         "<iq type='set' id='auth%d'>"
1514         "<query xmlns='jabber:iq:auth'>"
1515         "<username>%s</username>"
1516         "<digest>%s</digest>"
1517         "<resource>%s</resource>"
1518         "</query>"
1519         "</iq>\n";
1520         if (!write(fmt, msgId++, username.c_str(),
1521                     digest.c_str(), resource.c_str()))
1522             return false;
1523         }
1524     else
1525         {
1527         //## Plaintext authentication
1528         fmt =
1529         "<iq type='set' id='auth%d'>"
1530         "<query xmlns='jabber:iq:auth'>"
1531         "<username>%s</username>"
1532         "<password>%s</password>"
1533         "<resource>%s</resource>"
1534         "</query>"
1535         "</iq>\n";
1536         if (!write(fmt, msgId++, username.c_str(),
1537                    password.c_str(), resource.c_str()))
1538             return false;
1539         }
1541     recbuf = readStanza();
1542     status("iq auth recv:  '%s'\n", recbuf.c_str());
1543     elem = parser.parse(recbuf);
1544     //elem->print();
1545     iqType = elem->getTagAttribute("iq", "type");
1546     //printf("##iqType:%s\n", iqType.c_str());
1547     delete elem;
1549     if (iqType != "result")
1550         {
1551         error("server does not allow login");
1552         return false;
1553         }
1555     return true;
1559 /**
1560  * Parse a sasl challenge to retrieve all of its key=value pairs
1561  */
1562 static bool saslParse(const DOMString &s, 
1563                       std::map<DOMString, DOMString> &vals)
1566     vals.clear();
1568     int p  = 0;
1569     int siz = s.size();
1571     while (p < siz)
1572         {
1573         DOMString key;
1574         DOMString value;
1575         char ch = '\0';
1577         //# Parse key
1578         while (p<siz)
1579             {
1580             ch = s[p++];
1581             if (ch == '=')
1582                 break;
1583             key.push_back(ch);
1584             }
1586         //No value?
1587         if (ch != '=')
1588             break;
1590         //# Parse value
1591         bool quoted = false;
1592         while (p<siz)
1593             {
1594             ch = s[p++];
1595             if (ch == '"')
1596                 quoted = !quoted;
1597             else if (ch == ',' && !quoted)
1598                 break;
1599             else
1600                 value.push_back(ch);
1601             }
1603         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
1604         vals[key] = value;
1605         if (ch != ',')
1606             break;
1607         }
1609     return true;
1614 /**
1615  * Attempt suthentication using the MD5 SASL mechanism
1616  */
1617 bool XmppClient::saslMd5Authenticate()
1619     Parser parser;
1620     const char *fmt =
1621     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1622         "mechanism='DIGEST-MD5'/>\n";
1623     if (!write("%s",fmt))
1624         return false;
1626     DOMString recbuf = readStanza();
1627     status("challenge received: '%s'", recbuf.c_str());
1628     Element *elem = parser.parse(recbuf);
1629     //elem->print();
1630     DOMString b64challenge = elem->getTagValue("challenge");
1631     delete elem;
1633     if (b64challenge.size() < 1)
1634         {
1635         error("login: no SASL challenge offered by server");
1636         return false;
1637         }
1638     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1639     status("md5 challenge:'%s'", challenge.c_str());
1641     std::map<DOMString, DOMString> attrs;
1642     if (!saslParse(challenge, attrs))
1643         {
1644         error("login: error parsing SASL challenge");
1645         return false;
1646         }
1648     DOMString nonce = attrs["nonce"];
1649     if (nonce.size()==0)
1650         {
1651         error("login: no SASL nonce sent by server");
1652         return false;
1653         }
1655     DOMString realm = attrs["realm"];
1656     if (realm.size()==0)
1657         {
1658         //Apparently this is not a problem
1659         //error("login: no SASL realm sent by server");
1660         //return false;
1661         }
1663     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1665     char idBuf[14];
1666     snprintf(idBuf, 13, "%dsasl", msgId++);
1667     DOMString cnonceStr = idBuf;
1668     DOMString cnonce = Sha1::hashHex(cnonceStr);
1669     DOMString authzid = username; authzid.append("@"); authzid.append(host);
1670     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1672     //## Make A1
1673     Md5 md5;
1674     md5.append(username);
1675     md5.append(":");
1676     md5.append(realm);
1677     md5.append(":");
1678     md5.append(password);
1679     unsigned char a1tmp[16];
1680     md5.finish(a1tmp);
1681     md5.init();
1682     md5.append(a1tmp, 16);
1683     md5.append(":");
1684     md5.append(nonce);
1685     md5.append(":");
1686     md5.append(cnonce);
1687     //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1688     //md5.append(":");
1689     //md5.append(authzid);
1690     md5.append("");
1691     DOMString a1 = md5.finishHex();
1692     status("##a1:'%s'", a1.c_str());
1694     //# Make A2
1695     md5.init();
1696     md5.append("AUTHENTICATE:");
1697     md5.append(digest_uri);
1698     DOMString a2 = md5.finishHex();
1699     status("##a2:'%s'", a2.c_str());
1701     //# Now make the response
1702     md5.init();
1703     md5.append(a1);
1704     md5.append(":");
1705     md5.append(nonce);
1706     md5.append(":");
1707     md5.append("00000001");//nc
1708     md5.append(":");
1709     md5.append(cnonce);
1710     md5.append(":");
1711     md5.append("auth");//qop
1712     md5.append(":");
1713     md5.append(a2);
1714     DOMString response = md5.finishHex();
1716     DOMString resp;
1717     resp.append("username=\""); resp.append(username); resp.append("\",");
1718     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
1719     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
1720     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
1721     resp.append("nc=00000001,qop=auth,");
1722     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1723     //resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
1724     resp.append("response=");   resp.append(response); resp.append(",");
1725     resp.append("charset=utf-8");
1726     status("sending response:'%s'", resp.c_str());
1727     resp = Base64Encoder::encode(resp);
1728     status("base64 response:'%s'", resp.c_str());
1729     fmt =
1730     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1731     if (!write(fmt, resp.c_str()))
1732         return false;
1734     recbuf = readStanza();
1735     status("server says: '%s'", recbuf.c_str());
1736     elem = parser.parse(recbuf);
1737     //elem->print();
1738     //# Success or failure already?
1739     if (elem->findElements("success").size() > 0)
1740         {
1741         delete elem;
1742         return true;
1743         }
1744     else
1745         {
1746         ElementList list = elem->findElements("failure");
1747         if (list.size() > 0)
1748             {
1749             DOMString errmsg = "";
1750             Element *errmsgElem = list[0]->getFirstChild();
1751             if (errmsgElem)
1752                 errmsg = errmsgElem->getName();
1753             error("login: initial md5 authentication failed: %s", errmsg.c_str());
1754             delete elem;
1755             return false;
1756             }
1757         }
1758     //# Continue for one more SASL cycle
1759     b64challenge = elem->getTagValue("challenge");
1760     delete elem;
1762     if (b64challenge.size() < 1)
1763         {
1764         error("login: no second SASL challenge offered by server");
1765         return false;
1766         }
1768     challenge = Base64Decoder::decodeToString(b64challenge);
1769     status("md5 challenge: '%s'", challenge.c_str());
1771     if (!saslParse(challenge, attrs))
1772         {
1773         error("login: error parsing SASL challenge");
1774         return false;
1775         }
1777     DOMString rspauth = attrs["rspauth"];
1778     if (rspauth.size()==0)
1779         {
1780         error("login: no SASL respauth sent by server\n");
1781         return false;
1782         }
1784     fmt =
1785     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1786     if (!write("%s",fmt))
1787         return false;
1789     recbuf = readStanza();
1790     status("SASL recv: '%s", recbuf.c_str());
1791     elem = parser.parse(recbuf);
1792     //elem->print();
1793     b64challenge = elem->getTagValue("challenge");
1794     bool success = (elem->findElements("success").size() > 0);
1795     delete elem;
1797     return success;
1802 /**
1803  *  Attempt to authentication using the SASL PLAIN mechanism.  This
1804  *  is used most commonly my Google Talk.
1805  */
1806 bool XmppClient::saslPlainAuthenticate()
1808     Parser parser;
1810     DOMString id = username;
1811     //id.append("@");
1812     //id.append(host);
1813     Base64Encoder encoder;
1814     encoder.append('\0');
1815     encoder.append(id);
1816     encoder.append('\0');
1817     encoder.append(password);
1818     DOMString base64Auth = encoder.finish();
1819     //printf("authbuf:%s\n", base64Auth.c_str());
1821     const char *fmt =
1822     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1823     "mechanism='PLAIN'>%s</auth>\n";
1824     if (!write(fmt, base64Auth.c_str()))
1825         return false;
1826     DOMString recbuf = readStanza();
1827     status("challenge received: '%s'", recbuf.c_str());
1828     Element *elem = parser.parse(recbuf);
1830     bool success = (elem->findElements("success").size() > 0);
1831     delete elem;
1833     return success;
1838 /**
1839  * Handshake with SASL, and use one of its offered mechanisms to
1840  * authenticate.
1841  * @param streamId used for iq auth fallback is SASL not supported
1842  */
1843 bool XmppClient::saslAuthenticate(const DOMString &streamId)
1845     Parser parser;
1847     DOMString recbuf = readStanza();
1848     status("RECV: '%s'\n", recbuf.c_str());
1849     Element *elem = parser.parse(recbuf);
1850     //elem->print();
1852     //Check for starttls
1853     bool wantStartTls = false;
1854     if (elem->findElements("starttls").size() > 0)
1855         {
1856         wantStartTls = true;
1857         if (elem->findElements("required").size() > 0)
1858             status("login: STARTTLS required");
1859         else
1860             status("login: STARTTLS available");
1861         }
1863     //# do we want TLS, are we not already running SSL, and can
1864     //# the client actually do an ssl connection?
1865     if (wantStartTls && !sock->getEnableSSL() && sock->getHaveSSL())
1866         {
1867         delete elem;
1868         const char *fmt =
1869         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
1870         if (!write("%s",fmt))
1871             return false;
1872         recbuf = readStanza();
1873         status("RECV: '%s'\n", recbuf.c_str());
1874         elem = parser.parse(recbuf);
1875         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
1876             {
1877             error("Server rejected TLS negotiation");
1878             disconnect();
1879             return false;
1880             }
1881         delete elem;
1882         if (!sock->startTls())
1883             {
1884             DOMString tcperr = sock->getLastError();
1885             error("Could not start TLS: %s", tcperr.c_str());
1886             disconnect();
1887             return false;
1888             }
1890         fmt =
1891          "<stream:stream xmlns='jabber:client' "
1892          "xmlns:stream='http://etherx.jabber.org/streams' "
1893          "to='%s' version='1.0'>\n\n";
1894         if (!write(fmt, realm.c_str()))
1895             return false;
1897         recbuf = readStanza();
1898         status("RECVx: '%s'", recbuf.c_str());
1899         recbuf.append("</stream:stream>");
1900         elem = parser.parse(recbuf);
1901         bool success =
1902         (elem->getTagAttribute("stream:stream", "id").size()>0);
1903         if (!success)
1904             {
1905             error("STARTTLS negotiation failed");
1906             disconnect();
1907             return false;
1908             }
1909         delete elem;
1910         recbuf = readStanza();
1911         status("RECV: '%s'\n", recbuf.c_str());
1912         elem = parser.parse(recbuf);
1914         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
1915         dispatchXmppEvent(event);
1916         }
1918     //register, if user requests
1919     if (doRegister)
1920         {
1921         if (!inBandRegistrationNew())
1922             return false;
1923         }
1925     //check for sasl authentication mechanisms
1926     ElementList elems = elem->findElements("mechanism");
1927     if (elems.size() < 1)
1928         {
1929         status("login: no SASL mechanism offered by server");
1930         //fall back to iq
1931         if (iqAuthenticate(streamId))
1932             return true;
1933         return false;
1934         }
1935     bool md5Found = false;
1936     bool plainFound = false;
1937     for (unsigned int i=0 ; i<elems.size() ; i++)
1938         {
1939         DOMString mech = elems[i]->getValue();
1940         if (mech == "DIGEST-MD5")
1941             {
1942             status("MD5 authentication offered");
1943             md5Found = true;
1944             }
1945         else if (mech == "PLAIN")
1946             {
1947             status("PLAIN authentication offered");
1948             plainFound = true;
1949             }
1950         }
1951     delete elem;
1953     bool success = false;
1954     if (md5Found)
1955         {
1956         success = saslMd5Authenticate();
1957         }
1958     else if (plainFound)
1959         {
1960         success = saslPlainAuthenticate();
1961         }
1962     else
1963         {
1964         error("not able to handle sasl authentication mechanisms");
1965         return false;
1966         }
1968     if (success)
1969         status("###### SASL authentication success\n");
1970     else
1971         error("###### SASL authentication failure\n");
1973     return success;
1981 //########################################################################
1982 //# CONNECT
1983 //########################################################################
1986 /**
1987  * Check if we are connected, and fail with an error if we are not
1988  */
1989 bool XmppClient::checkConnect()
1991     if (!connected)
1992         {
1993         XmppEvent evt(XmppEvent::EVENT_ERROR);
1994         evt.setData("Attempted operation while disconnected");
1995         dispatchXmppEvent(evt);
1996         return false;
1997         }
1998     return true;
2003 /**
2004  * Create an XMPP session with a server.  This
2005  * is basically the transport layer of XMPP.
2006  */
2007 bool XmppClient::createSession()
2010     Parser parser;
2011     if (port==443 || port==5223)
2012         sock->enableSSL(true);
2013     if (!sock->connect(host, port))
2014         {
2015         error("Cannot connect:%s", sock->getLastError().c_str());
2016         return false;
2017         }
2019     if (sock->getEnableSSL())
2020         {
2021         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2022         dispatchXmppEvent(event);
2023         }
2025     const char *fmt =
2026      "<stream:stream "
2027           "to='%s' "
2028           "xmlns='jabber:client' "
2029           "xmlns:stream='http://etherx.jabber.org/streams' "
2030           "version='1.0'>\n\n";
2031     if (!write(fmt, realm.c_str()))
2032         return false;
2034     DOMString recbuf = readStanza();
2035     status("RECV:  '%s'\n", recbuf.c_str());
2036     recbuf.append("</stream:stream>");
2037     Element *elem = parser.parse(recbuf);
2038     //elem->print();
2039     bool useSasl = false;
2040     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2041     //printf("### StreamID: %s\n", streamId.c_str());
2042     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2043     if (streamVersion == "1.0")
2044         useSasl = true;
2046     if (useSasl)
2047         {
2048         if (!saslAuthenticate(streamId))
2049             return false;
2051         fmt =
2052           "<stream:stream "
2053           "to='%s' "
2054           "xmlns='jabber:client' "
2055           "xmlns:stream='http://etherx.jabber.org/streams' "
2056           "version='1.0'>\n\n";
2058         if (!write(fmt, realm.c_str()))
2059             return false;
2060         recbuf = readStanza();
2061         recbuf.append("</stream:stream>\n");
2062         //printf("now server says:: '%s'\n", recbuf.c_str());
2063         elem = parser.parse(recbuf);
2064         //elem->print();
2065         delete elem;
2067         recbuf = readStanza();
2068         //printf("now server says:: '%s'\n", recbuf.c_str());
2069         elem = parser.parse(recbuf);
2070         bool hasBind = (elem->findElements("bind").size() > 0);
2071         //elem->print();
2072         delete elem;
2074         if (!hasBind)
2075             {
2076             error("no binding provided by server");
2077             return false;
2078             }
2081         }
2082     else // not SASL
2083         {
2084         if (!iqAuthenticate(streamId))
2085             return false;
2086         }
2089     //### Resource binding
2090     fmt =
2091     "<iq type='set' id='bind%d'>"
2092     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2093     "<resource>%s</resource>"
2094     "</bind></iq>\n";
2095     if (!write(fmt, msgId++, resource.c_str()))
2096         return false;
2098     recbuf = readStanza();
2099     status("bind result: '%s'", recbuf.c_str());
2100     elem = parser.parse(recbuf);
2101     //elem->print();
2102     DOMString bindType = elem->getTagAttribute("iq", "type");
2103     //printf("##bindType:%s\n", bindType.c_str());
2104     DOMString givenFullJid = elem->getTagValue("jid");
2105     delete elem;
2107     if (bindType != "result")
2108         {
2109         error("no binding with server failed");
2110         return false;
2111         }
2112         
2113     //The server sent us a JID.  We need to listen.
2114     if (givenFullJid.size()>0)
2115         {
2116         DOMString givenJid, givenResource;
2117         parseJid(givenFullJid, givenJid, givenResource);
2118         status("given user: %s realm: %s, rsrc: %s",
2119            givenJid.c_str(), realm.c_str(), givenResource.c_str());
2120         setResource(givenResource);
2121         }
2122         
2124     fmt =
2125     "<iq type='set' id='sess%d'>"
2126     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2127     "</iq>\n";
2128     if (!write(fmt, msgId++))
2129         return false;
2131     recbuf = readStanza();
2132     status("session received: '%s'", recbuf.c_str());
2133     elem = parser.parse(recbuf);
2134     //elem->print();
2135     DOMString sessionType = elem->getTagAttribute("iq", "type");
2136     //printf("##sessionType:%s\n", sessionType.c_str());
2137     delete elem;
2139     if (sessionType != "result")
2140         {
2141         error("no session provided by server");
2142         return false;
2143         }
2145     //printf("########## COOL #########\n");
2146     //Now that we are bound, we have a valid JID
2147     jid = username;
2148     jid.append("@");
2149     jid.append(realm);
2150     jid.append("/");
2151     jid.append(resource);
2153     //We are now done with the synchronous handshaking.  Let's go into
2154     //async mode
2156     fmt =
2157      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2158     if (!write(fmt, msgId++))
2159         return false;
2161     fmt =
2162      "<iq type='get' id='discoItems%d' to='%s'>"
2163      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2164     if (!write(fmt, msgId++, realm.c_str()))
2165         return false;
2167     fmt =
2168     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2169     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2170     if (!write(fmt, msgId++, realm.c_str()))
2171         return false;
2173     fmt =
2174      "<presence/>\n";
2175     if (!write("%s",fmt))
2176         return false;
2178     /*
2179     recbuf = readStanza();
2180     status("stream received: '%s'", recbuf.c_str());
2181     elem = parser.parse(recbuf);
2182     //elem->print();
2183     delete elem;
2184     */
2186     //We are now logged in
2187     status("Connected");
2188     connected = true;
2189     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2190     evt.setData(host);
2191     dispatchXmppEvent(evt);
2192     //Thread::sleep(1000000);
2194     sock->setReceiveTimeout(1000);
2195     ReceiverThread runner(*this);
2196     Thread thread(runner);
2197     thread.start();
2199     return true;
2204 /**
2205  * Public call to connect
2206  */
2207 bool XmppClient::connect()
2209     if (!createSession())
2210         {
2211         disconnect();
2212         return false;
2213         }
2214     return true;
2218 /**
2219  * Public call to connect
2220  */
2221 bool XmppClient::connect(DOMString hostArg, int portArg,
2222                          DOMString usernameArg,
2223                          DOMString passwordArg,
2224                          DOMString resourceArg)
2226     host     = hostArg;
2227     port     = portArg;
2228     password = passwordArg;
2229     resource = resourceArg;
2231     //parse this one
2232     setUsername(usernameArg);
2234     bool ret = connect();
2235     return ret;
2240 /**
2241  * Public call to disconnect
2242  */
2243 bool XmppClient::disconnect()
2245     if (connected)
2246         {
2247         const char *fmt =
2248         "<presence type='unavailable'/>\n";
2249         write("%s",fmt);
2250         }
2251     keepGoing = false;
2252     connected = false;
2253     Thread::sleep(2000); //allow receiving thread to quit
2254     sock->disconnect();
2255     roster.clear();
2256     groupChatsClear();
2257     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2258     event.setData(host);
2259     dispatchXmppEvent(event);
2260     return true;
2267 //########################################################################
2268 //# ROSTER
2269 //########################################################################
2271 /**
2272  *  Add an XMPP id to your roster
2273  */
2274 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2275                            const DOMString &otherJid,
2276                            const DOMString &name)
2278     if (!checkConnect())
2279         return false;
2280     const char *fmt =
2281     "<iq type='set' id='roster_%d'>"
2282     "<query xmlns='jabber:iq:roster'>"
2283     "<item jid='%s' name='%s'><group>%s</group></item>"
2284     "</query></iq>\n";
2285     if (!write(fmt, msgId++, otherJid.c_str(),
2286          name.c_str(), rosterGroup.c_str()))
2287         {
2288         return false;
2289         }
2290     return true;
2295 /**
2296  *  Delete an XMPP id from your roster.
2297  */
2298 bool XmppClient::rosterDelete(const DOMString &otherJid)
2300     if (!checkConnect())
2301         return false;
2302     const char *fmt =
2303     "<iq type='set' id='roster_%d'>"
2304     "<query xmlns='jabber:iq:roster'>"
2305     "<item jid='%s' subscription='remove'><group>%s</group></item>"
2306     "</query></iq>\n";
2307     if (!write(fmt, msgId++, otherJid.c_str()))
2308         {
2309         return false;
2310         }
2311     return true;
2315 /**
2316  *  Comparison method for sort() call below
2317  */
2318 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2320     DOMString s1 = p1.group;
2321     DOMString s2 = p2.group;
2322     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2323         {
2324         int comp = tolower(s1[len]) - tolower(s2[len]);
2325         if (comp)
2326             return (comp<0);
2327         }
2329     s1 = p1.jid;
2330     s2 = p2.jid;
2331     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2332         {
2333         int comp = tolower(s1[len]) - tolower(s2[len]);
2334         if (comp)
2335             return (comp<0);
2336         }
2337     return false;
2342 /**
2343  *  Sort and return the roster that has just been reported by
2344  *  an XmppEvent::EVENT_ROSTER event.
2345  */
2346 std::vector<XmppUser> XmppClient::getRoster()
2348     std::vector<XmppUser> ros = roster;
2349     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2350     return ros;
2354 /**
2355  *
2356  */
2357 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2359     DOMString theShow = show;
2360     if (theShow == "")
2361         theShow = "available";
2363     std::vector<XmppUser>::iterator iter;
2364     for (iter=roster.begin() ; iter != roster.end() ; iter++)
2365         {
2366         if (iter->jid == jid)
2367             iter->show = theShow;
2368         }
2376 //########################################################################
2377 //# CHAT (individual)
2378 //########################################################################
2380 /**
2381  * Send a message to an xmpp jid
2382  */
2383 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2384                          const DOMString &msg)
2386     if (!checkConnect())
2387         return false;
2389     DOMString xmlSubj = toXml(subj);
2390     DOMString xmlMsg  = toXml(msg);
2392     if (xmlSubj.size() > 0)
2393         {
2394         const char *fmt =
2395         "<message to='%s' from='%s' type='chat'>"
2396         "<subject>%s</subject><body>%s</body></message>\n";
2397         if (!write(fmt, user.c_str(), jid.c_str(),
2398                 xmlSubj.c_str(), xmlMsg.c_str()))
2399             return false;
2400         }
2401     else
2402         {
2403         const char *fmt =
2404         "<message to='%s' from='%s'>"
2405         "<body>%s</body></message>\n";
2406         if (!write(fmt, user.c_str(), jid.c_str(), xmlMsg.c_str()))
2407             return false;
2408         }
2409     return true;
2414 /**
2415  *
2416  */
2417 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2419     return message(user, "", msg);
2424 /**
2425  *
2426  */
2427 bool XmppClient::presence(const DOMString &presence)
2429     if (!checkConnect())
2430         return false;
2432     DOMString xmlPres = toXml(presence);
2434     const char *fmt =
2435     "<presence><show>%s</show></presence>\n";
2436     if (!write(fmt, xmlPres.c_str()))
2437         return false;
2438     return true;
2446 //########################################################################
2447 //# GROUP  CHAT
2448 //########################################################################
2450 /**
2451  *
2452  */
2453 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2455     std::vector<XmppGroupChat *>::iterator iter;
2456     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2457         {
2458         if ((*iter)->getGroupJid() == groupJid)
2459             {
2460             //error("Group chat '%s' already exists", groupJid.c_str());
2461             return false;
2462             }
2463         }
2464     XmppGroupChat *chat = new XmppGroupChat(groupJid);
2465     groupChats.push_back(chat);
2466     return true;
2471 /**
2472  *
2473  */
2474 void XmppClient::groupChatDelete(const DOMString &groupJid)
2476     std::vector<XmppGroupChat *>::iterator iter;
2477     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2478         {
2479         XmppGroupChat *chat = *iter;
2480         if (chat->getGroupJid() == groupJid)
2481             {
2482             iter = groupChats.erase(iter);
2483             delete chat;
2484             }
2485         else
2486             iter++;
2487         }
2492 /**
2493  *
2494  */
2495 bool XmppClient::groupChatExists(const DOMString &groupJid)
2497     std::vector<XmppGroupChat *>::iterator iter;
2498     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2499         if ((*iter)->getGroupJid() == groupJid)
2500             return true;
2501     return false;
2506 /**
2507  *
2508  */
2509 void XmppClient::groupChatsClear()
2511     std::vector<XmppGroupChat *>::iterator iter;
2512     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2513         delete (*iter);
2514     groupChats.clear();
2520 /**
2521  *
2522  */
2523 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2524                                   const DOMString &nick,
2525                                   const DOMString &jid)
2527     std::vector<XmppGroupChat *>::iterator iter;
2528     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2529         {
2530         if ((*iter)->getGroupJid() == groupJid)
2531             {
2532             (*iter)->userAdd(nick, jid);
2533             }
2534         }
2539 /**
2540  *
2541  */
2542 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2543                                    const DOMString &nick,
2544                                    const DOMString &show)
2546     std::vector<XmppGroupChat *>::iterator iter;
2547     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2548         {
2549         if ((*iter)->getGroupJid() == groupJid)
2550             {
2551             (*iter)->userShow(nick, show);
2552             }
2553         }
2559 /**
2560  *
2561  */
2562 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2563                                      const DOMString &nick)
2565     std::vector<XmppGroupChat *>::iterator iter;
2566     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2567         {
2568         if ((*iter)->getGroupJid() == groupJid)
2569             {
2570             (*iter)->userDelete(nick);
2571             }
2572         }
2577 /**
2578  *  Comparison method for the sort() below
2579  */
2580 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2582     DOMString s1 = p1.nick;
2583     DOMString s2 = p2.nick;
2584     int comp = 0;
2585     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2586         {
2587         comp = tolower(s1[len]) - tolower(s2[len]);
2588         if (comp)
2589             break;
2590         }
2591     return (comp<0);
2596 /**
2597  *  Return the user list for the named group
2598  */
2599 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2600                               const DOMString &groupJid)
2602     if (!checkConnect())
2603         {
2604         std::vector<XmppUser> dummy;
2605         return dummy;
2606         }
2608     std::vector<XmppGroupChat *>::iterator iter;
2609     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2610         {
2611         if ((*iter)->getGroupJid() == groupJid )
2612             {
2613             std::vector<XmppUser> uList = (*iter)->getUserList();
2614             std::sort(uList.begin(), uList.end(), xmppUserCompare);
2615             return uList;
2616             }
2617         }
2618     std::vector<XmppUser> dummy;
2619     return dummy;
2625 /**
2626  *  Try to join a group
2627  */
2628 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2629                                const DOMString &nick,
2630                                const DOMString &/*pass*/)
2632     if (!checkConnect())
2633         return false;
2635     DOMString user = nick;
2636     if (user.size()<1)
2637         user = username;
2639     const char *fmt =
2640     "<presence to='%s/%s'>"
2641     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2642     if (!write(fmt, groupJid.c_str(), user.c_str()))
2643         return false;
2644     return true;
2650 /**
2651  * Leave a group
2652  */
2653 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2654                                 const DOMString &nick)
2656     if (!checkConnect())
2657         return false;
2659     DOMString user = nick;
2660     if (user.size()<1)
2661         user = username;
2663     const char *fmt =
2664     "<presence to='%s/%s' type='unavailable'>"
2665     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2666     if (!write(fmt, groupJid.c_str(), user.c_str()))
2667         return false;
2668     return true;
2674 /**
2675  *  Send a message to a group
2676  */
2677 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2678                                   const DOMString &msg)
2680     if (!checkConnect())
2681         {
2682         return false;
2683         }
2685     DOMString xmlMsg = toXml(msg);
2687     const char *fmt =
2688     "<message from='%s' to='%s' type='groupchat'>"
2689     "<body>%s</body></message>\n";
2690     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2691         return false;
2692     /*
2693     const char *fmt =
2694     "<message to='%s' type='groupchat'>"
2695     "<body>%s</body></message>\n";
2696     if (!write(fmt, groupJid.c_str(), xmlMsg.c_str()))
2697         return false;
2698     */
2699     return true;
2705 /**
2706  *  Send a message to an individual in a group
2707  */
2708 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2709                                          const DOMString &toNick,
2710                                          const DOMString &msg)
2712     if (!checkConnect())
2713         return false;
2715     DOMString xmlMsg = toXml(msg);
2717     /*
2718     const char *fmt =
2719     "<message from='%s' to='%s/%s' type='chat'>"
2720     "<body>%s</body></message>\n";
2721     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2722                toNick.c_str(), xmlMsg.c_str()))
2723         return false;
2724     */
2725     const char *fmt =
2726     "<message to='%s/%s' type='chat'>"
2727     "<body>%s</body></message>\n";
2728     if (!write(fmt, groupJid.c_str(),
2729                toNick.c_str(), xmlMsg.c_str()))
2730         return false;
2731     return true;
2737 /**
2738  *  Change your presence within a group
2739  */
2740 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2741                                    const DOMString &myNick,
2742                                    const DOMString &presence)
2744     if (!checkConnect())
2745         return false;
2747     DOMString user = myNick;
2748     if (user.size()<1)
2749         user = username;
2751     DOMString xmlPresence = toXml(presence);
2753     const char *fmt =
2754     "<presence to='%s/%s' type='%s'>"
2755     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2756     if (!write(fmt, groupJid.c_str(),
2757                user.c_str(), xmlPresence.c_str()))
2758         return true;
2759     return true;
2766 //########################################################################
2767 //# S T R E A M S
2768 //########################################################################
2771 bool XmppClient::processInBandByteStreamMessage(Element *root)
2773     DOMString from  = root->getAttribute("from");
2774     DOMString id    = root->getAttribute("id");
2775     DOMString type  = root->getAttribute("type");
2777     //### Incoming stream requests
2778     //Input streams are id's by stream id
2779     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2781     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2782         {
2783         DOMString streamId = root->getTagAttribute("open", "sid");
2784         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2785         dispatchXmppEvent(event);
2786         std::map<DOMString, XmppStream *>::iterator iter =
2787                      inputStreams.find(streamId);
2788         if (iter != inputStreams.end())
2789             {
2790             XmppStream *ins = iter->second;
2791             ins->setState(STREAM_OPENING);
2792             ins->setMessageId(id);
2793             return true;
2794             }
2795         return true;
2796         }
2798     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2799         {
2800         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2801         dispatchXmppEvent(event);
2802         DOMString streamId = root->getTagAttribute("close", "sid");
2803         std::map<DOMString, XmppStream *>::iterator iter =
2804                      inputStreams.find(streamId);
2805         if (iter != inputStreams.end())
2806             {
2807             XmppStream *ins = iter->second;
2808             if (from == ins->getPeerId())
2809                 {
2810                 ins->setState(STREAM_CLOSING);
2811                 ins->setMessageId(id);
2812                 return true;
2813                 }
2814             }
2815         return true;
2816         }
2818     else if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2819         {
2820         DOMString streamId = root->getTagAttribute("data", "sid");
2821         std::map<DOMString, XmppStream *>::iterator iter =
2822                      inputStreams.find(streamId);
2823         if (iter != inputStreams.end())
2824             {
2825             XmppStream *ins = iter->second;
2826             if (ins->getState() != STREAM_OPEN)
2827                 {
2828                 XmppEvent event(XmppEvent::EVENT_ERROR);
2829                 event.setFrom(from);
2830                 event.setData("received unrequested stream data");
2831                 dispatchXmppEvent(event);
2832                 return true;
2833                 }
2834             DOMString data = root->getTagValue("data");
2835             std::vector<unsigned char>binData =
2836                        Base64Decoder::decode(data);
2837             ins->receiveData(binData);
2838             }
2839         }
2841     //### Responses to outgoing requests
2842     //Output streams are id's by message id
2843     std::map<DOMString, XmppStream *>::iterator iter =
2844                      outputStreams.find(id);
2845     if (iter != outputStreams.end())
2846         {
2847         XmppStream *outs = iter->second;
2848         if (type == "error")
2849             {
2850             outs->setState(STREAM_ERROR);
2851             return true;
2852             }
2853         else if (type == "result")
2854             {
2855             if (outs->getState() == STREAM_OPENING)
2856                 {
2857                 outs->setState(STREAM_OPEN);
2858                 }
2859             else if (outs->getState() == STREAM_CLOSING)
2860                 {
2861                 outs->setState(STREAM_CLOSED);
2862                 }
2863             return true;
2864             }
2865         }
2867     return false;
2871 /**
2872  *
2873  */
2874 bool XmppClient::outputStreamOpen(const DOMString &destId,
2875                                  const DOMString &streamIdArg)
2877     char buf[32];
2878     snprintf(buf, 31, "inband%d", getMsgId());
2879     DOMString messageId = buf;
2881     //Output streams are id's by message id
2882     XmppStream *outs = new XmppStream();
2883     outputStreams[messageId] = outs;
2885     outs->setState(STREAM_OPENING);
2887     DOMString streamId = streamIdArg;
2888     if (streamId.size()<1)
2889         {
2890         snprintf(buf, 31, "stream%d", getMsgId());
2891         DOMString streamId = buf;
2892         }
2893     outs->setMessageId(messageId);
2894     outs->setStreamId(streamId);
2895     outs->setPeerId(destId);
2898     const char *fmt =
2899     "<%s type='set' to='%s' id='%s'>"
2900     "<open sid='%s' block-size='4096'"
2901     " xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
2902     if (!write(fmt,
2903               streamPacket.c_str(),
2904               destId.c_str(), messageId.c_str(),
2905               streamId.c_str(),
2906               streamPacket.c_str()))
2907         {
2908         outs->reset();
2909         return -1;
2910         }
2912     int state = outs->getState();
2913     for (int tim=0 ; tim<20 ; tim++)
2914         {
2915         if (state == STREAM_OPEN)
2916             break;
2917         else if (state == STREAM_ERROR)
2918             {
2919             printf("ERROR\n");
2920             outs->reset();
2921             return false;
2922             }
2923         Thread::sleep(1000);
2924         state = outs->getState();
2925         }
2926     if (state != STREAM_OPEN)
2927         {
2928         printf("TIMEOUT ERROR\n");
2929         outs->reset();
2930         return -1;
2931         }
2933     return true;
2936 /**
2937  *
2938  */
2939 bool XmppClient::outputStreamWrite(const DOMString &streamId,
2940                const std::vector<unsigned char> &buf)
2942     std::map<DOMString, XmppStream *>::iterator iter =
2943         outputStreams.find(streamId);
2944     if (iter == outputStreams.end())
2945         return false;
2946     XmppStream *outs = iter->second;
2948     unsigned int len = buf.size();
2949     unsigned int pos = 0;
2951     while (pos < len)
2952         {
2953         unsigned int pos2 = pos + 1024;
2954         if (pos2>len)
2955             pos2 = len;
2957         Base64Encoder encoder;
2958         for (unsigned int i=pos ; i<pos2 ; i++)
2959             encoder.append(buf[i]);
2960         DOMString b64data = encoder.finish();
2963         const char *fmt =
2964         "<message to='%s' id='msg%d'>"
2965         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
2966         "%s"
2967         "</data>"
2968         "<amp xmlns='http://jabber.org/protocol/amp'>"
2969         "<rule condition='deliver-at' value='stored' action='error'/>"
2970         "<rule condition='match-resource' value='exact' action='error'/>"
2971         "</amp>"
2972         "</message>\n";
2973         if (!write(fmt,
2974               outs->getPeerId().c_str(),
2975               getMsgId(),
2976               outs->getStreamId().c_str(),
2977               outs->getSeqNr(),
2978               b64data.c_str()))
2979             {
2980             outs->reset();
2981             return false;
2982             }
2983         pause(5000);
2984         
2985         pos = pos2;
2986         }
2987         
2988     return true;
2991 /**
2992  *
2993  */
2994 bool XmppClient::outputStreamClose(const DOMString &streamId)
2996     std::map<DOMString, XmppStream *>::iterator iter =
2997         outputStreams.find(streamId);
2998     if (iter == outputStreams.end())
2999         return false;
3000     XmppStream *outs = iter->second;
3002     char buf[32];
3003     snprintf(buf, 31, "inband%d", getMsgId());
3004     DOMString messageId = buf;
3005     outs->setMessageId(messageId);
3007     outs->setState(STREAM_CLOSING);
3008     const char *fmt =
3009     "<%s type='set' to='%s' id='%s'>"
3010     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
3011     if (!write(fmt,
3012             streamPacket.c_str(),
3013             outs->getPeerId().c_str(),
3014             messageId.c_str(),
3015             outs->getStreamId().c_str(),
3016             streamPacket.c_str()
3017             ))
3018         return false;
3020     int state = outs->getState();
3021     for (int tim=0 ; tim<20 ; tim++)
3022         {
3023         if (state == STREAM_CLOSED)
3024             break;
3025         else if (state == STREAM_ERROR)
3026             {
3027             printf("ERROR\n");
3028             outs->reset();
3029             return false;
3030             }
3031         Thread::sleep(1000);
3032         state = outs->getState();
3033         }
3034     if (state != STREAM_CLOSED)
3035         {
3036         printf("TIMEOUT ERROR\n");
3037         outs->reset();
3038         return false;
3039         }
3041     delete outs;
3042     outputStreams.erase(streamId);
3044     return true;
3048 /**
3049  *
3050  */
3051 bool XmppClient::inputStreamOpen(const DOMString &fromJid,
3052                                 const DOMString &streamId,
3053                                 const DOMString &/*iqId*/)
3055     XmppStream *ins = new XmppStream();
3057     inputStreams[streamId] = ins;
3058     ins->reset();
3059     ins->setPeerId(fromJid);
3060     ins->setState(STREAM_CLOSED);
3061     ins->setStreamId(streamId);
3063     int state = ins->getState();
3064     for (int tim=0 ; tim<20 ; tim++)
3065         {
3066         if (state == STREAM_OPENING)
3067             break;
3068         else if (state == STREAM_ERROR)
3069             {
3070             printf("ERROR\n");
3071             ins->reset();
3072             return false;
3073             }
3074         Thread::sleep(1000);
3075         state = ins->getState();
3076         }
3077     if (state != STREAM_OPENING)
3078         {
3079         printf("TIMEOUT ERROR\n");
3080         ins->reset();
3081         return false;
3082         }
3083     const char *fmt =
3084     "<%s type='result' to='%s' id='%s'/>\n";
3085     if (!write(fmt, streamPacket.c_str(),
3086            fromJid.c_str(), ins->getMessageId().c_str()))
3087         {
3088         return false;
3089         }
3091     ins->setState(STREAM_OPEN);
3092     return true;
3097 /**
3098  *
3099  */
3100 bool XmppClient::inputStreamClose(const DOMString &streamId)
3102     std::map<DOMString, XmppStream *>::iterator iter =
3103         inputStreams.find(streamId);
3104     if (iter == inputStreams.end())
3105         return false;
3106     XmppStream *ins = iter->second;
3108     if (ins->getState() == STREAM_CLOSING)
3109         {
3110         const char *fmt =
3111         "<iq type='result' to='%s' id='%s'/>\n";
3112         if (!write(fmt, ins->getPeerId().c_str(),
3113                     ins->getMessageId().c_str()))
3114             {
3115             return false;
3116             }
3117         }
3118     inputStreams.erase(streamId);
3119     delete ins;
3121     return true;
3129 //########################################################################
3130 //# FILE   TRANSFERS
3131 //########################################################################
3134 bool XmppClient::processFileMessage(Element *root)
3136     DOMString siNamespace = "http://jabber.org/protocol/si";
3137     if (root->getTagAttribute("si", "xmlns") != siNamespace)
3138         return false;
3140     
3141     Element *mainElement = root->getFirstChild();
3142     if (!mainElement)
3143         return false;
3145     DOMString from  = mainElement->getAttribute("from");
3146     DOMString id    = mainElement->getAttribute("id");
3147     DOMString type  = mainElement->getAttribute("type");
3149     status("received file message from %s", from.c_str());
3151     if (type == "set")
3152         {
3153         DOMString streamId = root->getTagAttribute("si", "id");
3154         DOMString fname    = root->getTagAttribute("file", "name");
3155         DOMString sizeStr  = root->getTagAttribute("file", "size");
3156         DOMString hash     = root->getTagAttribute("file", "hash");
3157         XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
3158         event.setFrom(from);
3159         event.setIqId(id);
3160         event.setStreamId(streamId);
3161         event.setFileName(fname);
3162         event.setFileHash(hash);
3163         event.setFileSize(atol(sizeStr.c_str()));
3164         dispatchXmppEvent(event);
3165         return true;
3166         }
3168     //##expecting result or error
3169     //file sends id'd by message id's
3170     std::map<DOMString, XmppStream *>::iterator iter =
3171         fileSends.find(id);
3172     if (iter != fileSends.end())
3173         {
3174         XmppStream *outf = iter->second;
3175         if (from != outf->getPeerId())
3176             return true;
3177         if (type == "error")
3178             {
3179             outf->setState(STREAM_ERROR);
3180             error("user '%s' rejected file", from.c_str());
3181             return true;
3182             }
3183         else if (type == "result")
3184             {
3185             if (outf->getState() == STREAM_OPENING)
3186                 {
3187                 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
3188                 event.setFrom(from);
3189                 dispatchXmppEvent(event);
3190                 outf->setState(STREAM_OPEN);
3191                 }
3192             else if (outf->getState() == STREAM_CLOSING)
3193                 {
3194                 outf->setState(STREAM_CLOSED);
3195                 }
3196             return true;
3197             }
3198         }
3200     return true;
3208 /**
3209  *
3210  */
3211 bool XmppClient::fileSend(const DOMString &destJidArg,
3212                           const DOMString &offeredNameArg,
3213                           const DOMString &fileNameArg,
3214                           const DOMString &descriptionArg)
3216     DOMString destJid     = destJidArg;
3217     DOMString offeredName = offeredNameArg;
3218     DOMString fileName    = fileNameArg;
3219     DOMString description = descriptionArg;
3221     struct stat finfo;
3222     if (stat(fileName.c_str(), &finfo)<0)
3223         {
3224         error("Cannot stat file '%s' for sending", fileName.c_str());
3225         return false;
3226         }
3227     long fileLen = finfo.st_size;
3228     if (!fileLen > 1000000)
3229         {
3230         error("'%s' too large", fileName.c_str());
3231         return false;
3232         }
3233     if (!S_ISREG(finfo.st_mode))
3234         {
3235         error("'%s' is not a regular file", fileName.c_str());
3236         return false;
3237         }
3238     FILE *f = fopen(fileName.c_str(), "rb");
3239     if (!f)
3240         {
3241         error("cannot open '%s' for sending", fileName.c_str());
3242         return false;
3243         }
3244     std::vector<unsigned char> sendBuf;
3245     Md5 md5hash;
3246     for (long i=0 ; i<fileLen && !feof(f); i++)
3247         {
3248         int ch = fgetc(f);
3249         if (ch<0)
3250             break;
3251         md5hash.append((unsigned char)ch);
3252         sendBuf.push_back((unsigned char)ch);
3253         }
3254     fclose(f);
3255     DOMString hash = md5hash.finishHex();
3256     printf("Hash:%s\n", hash.c_str());
3258    
3259     //## get the last path segment from the whole path
3260     if (offeredName.size()<1)
3261         {
3262         int slashPos = -1;
3263         for (unsigned int i=0 ; i<fileName.size() ; i++)
3264             {
3265             int ch = fileName[i];
3266             if (ch == '/' || ch == '\\')
3267                 slashPos = i;
3268             }
3269         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3270             {
3271             offeredName = fileName.substr(slashPos+1,
3272                                           fileName.size()-slashPos-1);
3273             printf("offeredName:%s\n", offeredName.c_str());
3274             }
3275         }
3277     char buf[32];
3278     snprintf(buf, 31, "file%d", getMsgId());
3279     DOMString messageId = buf;
3281     XmppStream *outf = new XmppStream();
3283     outf->setState(STREAM_OPENING);
3284     outf->setMessageId(messageId);
3285     fileSends[messageId] = outf;
3287     snprintf(buf, 31, "stream%d", getMsgId());
3288     DOMString streamId = buf;
3289     //outf->setStreamId(streamId);
3291     outf->setPeerId(destJid);
3293     char dtgBuf[81];
3294     struct tm *timeVal = gmtime(&(finfo.st_mtime));
3295     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3297     const char *fmt =
3298     "<%s type='set' id='%s' to='%s'>"
3299     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3300       " mime-type='text/plain'"
3301       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3302     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3303           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3304     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3305     "<x xmlns='jabber:x:data' type='form'>"
3306     "<field var='stream-method' type='list-single'>"
3307     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3308     "<option><value>http://jabber.org/protocol/ibb</value></option>"
3309     "</field></x></feature></si></%s>\n";
3310     if (!write(fmt, streamPacket.c_str(),
3311          messageId.c_str(), destJid.c_str(),
3312          streamId.c_str(), offeredName.c_str(), fileLen,
3313          hash.c_str(), dtgBuf, description.c_str(),
3314          streamPacket.c_str()))
3315         {
3316         return false;
3317         }
3319     int ret = true;
3320     int state = outf->getState();
3321     for (int tim=0 ; tim<20 ; tim++)
3322         {
3323         printf("##### waiting for open\n");
3324         if (state == STREAM_OPEN)
3325             {
3326             outf->reset();
3327             break;
3328             }
3329         else if (state == STREAM_ERROR)
3330             {
3331             printf("ERROR\n");
3332             outf->reset();
3333             ret = false;
3334             }
3335         Thread::sleep(1000);
3336         state = outf->getState();
3337         }
3338     if (state != STREAM_OPEN)
3339         {
3340         printf("TIMEOUT ERROR\n");
3341         ret = false;
3342         }
3344     //free up this resource
3345     fileSends.erase(messageId);
3346     delete outf;
3348     if (!outputStreamOpen(destJid, streamId))
3349         {
3350         error("cannot open output stream %s", streamId.c_str());
3351         return false;
3352         }
3354     if (!outputStreamWrite(streamId, sendBuf))
3355         {
3356         }
3358     if (!outputStreamClose(streamId))
3359         {
3360         }
3362     return true;
3366 class FileSendThread : public Thread
3368 public:
3370     FileSendThread(XmppClient &par,
3371                    const DOMString &destJidArg,
3372                    const DOMString &offeredNameArg,
3373                    const DOMString &fileNameArg,
3374                    const DOMString &descriptionArg) : client(par)
3375         {
3376         destJid     = destJidArg;
3377         offeredName = offeredNameArg;
3378         fileName    = fileNameArg;
3379         description = descriptionArg;
3380         }
3382     virtual ~FileSendThread() {}
3384     void run()
3385       {
3386       client.fileSend(destJid, offeredName,
3387                       fileName, description);
3388       }
3390 private:
3392     XmppClient &client;
3393     DOMString destJid;
3394     DOMString offeredName;
3395     DOMString fileName;
3396     DOMString description;
3397 };
3399 /**
3400  *
3401  */
3402 bool XmppClient::fileSendBackground(const DOMString &destJid,
3403                                     const DOMString &offeredName,
3404                                     const DOMString &fileName,
3405                                     const DOMString &description)
3407     FileSendThread thread(*this, destJid, offeredName,
3408                            fileName, description);
3409     thread.start();
3410     return true;
3414 /**
3415  *
3416  */
3417 bool XmppClient::fileReceive(const DOMString &fromJid,
3418                              const DOMString &iqId,
3419                              const DOMString &streamId,
3420                              const DOMString &fileName,
3421                              long  /*fileSize*/,
3422                              const DOMString &/*fileHash*/)
3424     const char *fmt =
3425     "<%s type='result' to='%s' id='%s'>"
3426     "<si xmlns='http://jabber.org/protocol/si'>"
3427     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3428     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3429     "<x xmlns='jabber:x:data' type='submit'>"
3430     "<field var='stream-method'>"
3431     "<value>http://jabber.org/protocol/ibb</value>"
3432     "</field></x></feature></si></%s>\n";
3433     if (!write(fmt, streamPacket.c_str(),
3434                     fromJid.c_str(), iqId.c_str(),
3435                     streamPacket.c_str()))
3436         {
3437         return false;
3438         }
3440     if (!inputStreamOpen(fromJid, streamId, iqId))
3441         {
3442         return false;
3443         }
3445     XmppStream *ins = inputStreams[streamId];
3447     Md5 md5;
3448     FILE *f = fopen(fileName.c_str(), "wb");
3449     if (!f)
3450         {
3451         return false;
3452         }
3454     while (true)
3455         {
3456         if (ins->available()<1)
3457             {
3458             if (ins->getState() == STREAM_CLOSING)
3459                 break;
3460             pause(100);
3461             continue;
3462             }
3463         std::vector<unsigned char> ret = ins->read();
3464         std::vector<unsigned char>::iterator iter;
3465         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3466             {
3467             unsigned char ch = *iter;
3468             md5.append(&ch, 1);
3469             fwrite(&ch, 1, 1, f);
3470             }
3471         }
3473     inputStreamClose(streamId);
3474     fclose(f);
3476     DOMString hash = md5.finishHex();
3477     printf("received file hash:%s\n", hash.c_str());
3479     return true;
3484 class FileReceiveThread : public Thread
3486 public:
3488     FileReceiveThread(XmppClient &par,
3489                       const DOMString &fromJidArg,
3490                       const DOMString &iqIdArg,
3491                       const DOMString &streamIdArg,
3492                       const DOMString &fileNameArg,
3493                       long  fileSizeArg,
3494                       const DOMString &fileHashArg) : client(par)
3495         {
3496         fromJid     = fromJidArg;
3497         iqId        = iqIdArg;
3498         streamId    = streamIdArg;
3499         fileName    = fileNameArg;
3500         fileSize    = fileSizeArg;
3501         fileHash    = fileHashArg;
3502         }
3504     virtual ~FileReceiveThread() {}
3506     void run()
3507       {
3508       client.fileReceive(fromJid, iqId, streamId,
3509                         fileName, fileSize, fileHash);
3510       }
3512 private:
3514     XmppClient &client;
3515     DOMString fromJid;
3516     DOMString iqId;
3517     DOMString streamId;
3518     DOMString fileName;
3519     long      fileSize;
3520     DOMString fileHash;
3521 };
3523 /**
3524  *
3525  */
3526 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3527                                        const DOMString &iqId,
3528                                        const DOMString &streamId,
3529                                        const DOMString &fileName,
3530                                        long  fileSize,
3531                                        const DOMString &fileHash)
3533     FileReceiveThread thread(*this, fromJid, iqId, streamId,
3534                   fileName, fileSize, fileHash);
3535     thread.start();
3536     return true;
3541 //########################################################################
3542 //# X M P P    G R O U P    C H A T
3543 //########################################################################
3545 /**
3546  *
3547  */
3548 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3550     groupJid = groupJidArg;
3553 /**
3554  *
3555  */
3556 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3558     groupJid = other.groupJid;
3559     userList = other.userList;
3562 /**
3563  *
3564  */
3565 XmppGroupChat::~XmppGroupChat()
3570 /**
3571  *
3572  */
3573 DOMString XmppGroupChat::getGroupJid()
3575     return groupJid;
3579 void XmppGroupChat::userAdd(const DOMString &nick,
3580                             const DOMString &jid)
3582     std::vector<XmppUser>::iterator iter;
3583     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3584         {
3585         if (iter->nick == nick)
3586             return;
3587         }
3588     XmppUser user(jid, nick);
3589     userList.push_back(user);
3592 void XmppGroupChat::userShow(const DOMString &nick,
3593                              const DOMString &show)
3595     DOMString theShow = show;
3596     if (theShow == "")
3597         theShow = "available"; // a join message will now have a show
3598     std::vector<XmppUser>::iterator iter;
3599     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3600         {
3601         if (iter->nick == nick)
3602             iter->show = theShow;
3603         }
3606 void XmppGroupChat::userDelete(const DOMString &nick)
3608     std::vector<XmppUser>::iterator iter;
3609     for (iter= userList.begin() ; iter!=userList.end() ; )
3610         {
3611         if (iter->nick == nick)
3612             iter = userList.erase(iter);
3613         else
3614             iter++;
3615         }
3618 std::vector<XmppUser> XmppGroupChat::getUserList() const
3620     return userList;
3631 } //namespace Pedro
3632 //########################################################################
3633 //# E N D    O F     F I L E
3634 //########################################################################