Code

Changed preference to use file chooser button
[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-2007 Bob Jamison
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2.1 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
25 #include <stdio.h>
26 #include <stdarg.h>
28 #include <sys/stat.h>
30 #include <time.h>
32 #include "pedroxmpp.h"
33 #include "pedrodom.h"
34 #include "pedroutil.h"
36 #include <map>
40 namespace Pedro
41 {
44 //########################################################################
45 //########################################################################
46 //# X M P P    E V E N T
47 //########################################################################
48 //########################################################################
51 XmppEvent::XmppEvent(int type)
52 {
53     eventType = type;
54     presence  = false;
55     dom       = NULL;
56 }
58 XmppEvent::XmppEvent(const XmppEvent &other)
59 {
60     assign(other);
61 }
63 XmppEvent &XmppEvent::operator=(const XmppEvent &other)
64 {
65     assign(other);
66     return (*this);
67 }
69 XmppEvent::~XmppEvent()
70 {
71     if (dom)
72         delete dom;
73 }
75 void XmppEvent::assign(const XmppEvent &other)
76 {
77     eventType = other.eventType;
78     presence  = other.presence;
79     status    = other.status;
80     show      = other.show;
81     to        = other.to;
82     from      = other.from;
83     group     = other.group;
84     data      = other.data;
85     fileName  = other.fileName;
86     fileDesc  = other.fileDesc;
87     fileSize  = other.fileSize;
88     fileHash  = other.fileHash;
89     setDOM(other.dom);
90 }
92 int XmppEvent::getType() const
93 {
94     return eventType;
95 }
97 DOMString XmppEvent::getIqId() const
98 {
99     return iqId;
102 void XmppEvent::setIqId(const DOMString &val)
104     iqId = val;
107 DOMString XmppEvent::getStreamId() const
109     return streamId;
112 void XmppEvent::setStreamId(const DOMString &val)
114     streamId = val;
117 bool XmppEvent::getPresence() const
119     return presence;
122 void XmppEvent::setPresence(bool val)
124     presence = val;
127 DOMString XmppEvent::getShow() const
129     return show;
132 void XmppEvent::setShow(const DOMString &val)
134     show = val;
137 DOMString XmppEvent::getStatus() const
139     return status;
142 void XmppEvent::setStatus(const DOMString &val)
144     status = val;
147 DOMString XmppEvent::getTo() const
149     return to;
152 void XmppEvent::setTo(const DOMString &val)
154     to = val;
157 DOMString XmppEvent::getFrom() const
159     return from;
162 void XmppEvent::setFrom(const DOMString &val)
164     from = val;
167 DOMString XmppEvent::getGroup() const
169     return group;
172 void XmppEvent::setGroup(const DOMString &val)
174     group = val;
177 DOMString XmppEvent::getData() const
179     return data;
182 void XmppEvent::setData(const DOMString &val)
184     data = val;
187 DOMString XmppEvent::getFileName() const
189     return fileName;
192 void XmppEvent::setFileName(const DOMString &val)
194     fileName = val;
197 DOMString XmppEvent::getFileDesc() const
199     return fileDesc;
202 void XmppEvent::setFileDesc(const DOMString &val)
204     fileDesc = val;
207 long XmppEvent::getFileSize() const
209     return fileSize;
212 void XmppEvent::setFileSize(long val)
214     fileSize = val;
217 DOMString XmppEvent::getFileHash() const
219     return fileHash;
222 void XmppEvent::setFileHash(const DOMString &val)
224     fileHash = val;
227 Element *XmppEvent::getDOM() const
229     return dom;
232 void XmppEvent::setDOM(const Element *val)
234     if (!val)
235         dom = NULL;
236     else
237         dom = ((Element *)val)->clone();
241 std::vector<XmppUser> XmppEvent::getUserList() const
243     return userList;
246 void XmppEvent::setUserList(const std::vector<XmppUser> &val)
248     userList = val;
259 //########################################################################
260 //########################################################################
261 //# X M P P    E V E N T    T A R G E T
262 //########################################################################
263 //########################################################################
266 //###########################
267 //# CONSTRUCTORS
268 //###########################
270 XmppEventTarget::XmppEventTarget()
272     eventQueueEnabled = false;
276 XmppEventTarget::XmppEventTarget(const XmppEventTarget &other)
278     listeners         = other.listeners;
279     eventQueueEnabled = other.eventQueueEnabled;
282 XmppEventTarget::~XmppEventTarget()
287 //###########################
288 //# M E S S A G E S
289 //###########################
291 /**
292  *  Print a printf()-like formatted error message
293  */
294 void XmppEventTarget::error(char *fmt, ...)
296     va_list args;
297     va_start(args,fmt);
298     gchar * buffer = g_strdup_vprintf(fmt, args);
299     va_end(args) ;
300     fprintf(stderr, "Error:%s\n", buffer);
301     XmppEvent evt(XmppEvent::EVENT_ERROR);
302     evt.setData(buffer);
303     dispatchXmppEvent(evt);
304     g_free(buffer);
309 /**
310  *  Print a printf()-like formatted trace message
311  */
312 void XmppEventTarget::status(char *fmt, ...)
314     va_list args;
315     va_start(args,fmt);
316     gchar * buffer = g_strdup_vprintf(fmt, args);
317     va_end(args) ;
318     //printf("Status:%s\n", buffer);
319     XmppEvent evt(XmppEvent::EVENT_STATUS);
320     evt.setData(buffer);
321     dispatchXmppEvent(evt);
322     g_free(buffer);
327 //###########################
328 //# L I S T E N E R S
329 //###########################
331 void XmppEventTarget::dispatchXmppEvent(const XmppEvent &event)
333     std::vector<XmppEventListener *>::iterator iter;
334     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
335         (*iter)->processXmppEvent(event);
336     if (eventQueueEnabled)
337         eventQueue.push_back(event);
340 void XmppEventTarget::addXmppEventListener(const XmppEventListener &listener)
342     XmppEventListener *lsnr = (XmppEventListener *)&listener;
343     std::vector<XmppEventListener *>::iterator iter;
344     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
345         if (*iter == lsnr)
346             return;
347     listeners.push_back(lsnr);
350 void XmppEventTarget::removeXmppEventListener(const XmppEventListener &listener)
352     XmppEventListener *lsnr = (XmppEventListener *)&listener;
353     std::vector<XmppEventListener *>::iterator iter;
354     for (iter = listeners.begin(); iter != listeners.end() ; iter++)
355         if (*iter == lsnr)
356             listeners.erase(iter);
359 void XmppEventTarget::clearXmppEventListeners()
361     listeners.clear();
365 //###########################
366 //# E V E N T    Q U E U E
367 //###########################
369 void XmppEventTarget::eventQueueEnable(bool val)
371     eventQueueEnabled = val;
372     if (!eventQueueEnabled)
373         eventQueue.clear();
376 int XmppEventTarget::eventQueueAvailable()
378     return eventQueue.size();
381 XmppEvent XmppEventTarget::eventQueuePop()
383     if (!eventQueueEnabled || eventQueue.size()<1)
384         {
385         XmppEvent dummy(XmppEvent::EVENT_NONE);
386         return dummy;
387         }
388     XmppEvent event = *(eventQueue.begin());
389     eventQueue.erase(eventQueue.begin());
390     return event;
397 //########################################################################
398 //########################################################################
399 //# X M P P    S T R E A M
400 //########################################################################
401 //########################################################################
404 /**
405  *
406  */
407 class XmppStream
409 public:
411     /**
412      *
413      */
414     XmppStream()
415         { reset(); }
417     /**
418      *
419      */
420     XmppStream(const XmppStream &other)
421         { assign(other); }
423     /**
424      *
425      */
426     XmppStream &operator=(const XmppStream &other)
427         { assign(other); return *this; }
429     /**
430      *
431      */
432     virtual ~XmppStream()
433         {}
435     /**
436      *
437      */
438     virtual void reset()
439         {
440         state     = XmppClient::STREAM_AVAILABLE;
441         seqNr     = 0;
442         messageId = "";
443         sourceId  = "";
444         data.clear();
445         }
447     /**
448      *
449      */
450     virtual int getState()
451         { return state; }
453     /**
454      *
455      */
456     virtual void setState(int val)
457         { state = val; }
459     /**
460      *
461      */
462     virtual DOMString getStreamId()
463         { return streamId; }
465     /**
466      *
467      */
468     void setStreamId(const DOMString &val)
469         { streamId = val; }
471     /**
472      *
473      */
474     virtual DOMString getMessageId()
475         { return messageId; }
477     /**
478      *
479      */
480     void setMessageId(const DOMString &val)
481         { messageId = val; }
483     /**
484      *
485      */
486     virtual int getSeqNr()
487         {
488         seqNr++;
489         if (seqNr >= 65535)
490             seqNr = 0;
491         return seqNr;
492         }
494     /**
495      *
496      */
497     virtual DOMString getPeerId()
498         { return sourceId; }
500     /**
501      *
502      */
503     virtual void setPeerId(const DOMString &val)
504         { sourceId = val; }
506     /**
507      *
508      */
509     int available()
510         { return data.size(); }
512     /**
513      *
514      */
515     void receiveData(std::vector<unsigned char> &newData)
516         {
517         std::vector<unsigned char>::iterator iter;
518         for (iter=newData.begin() ; iter!=newData.end() ; iter++)
519             data.push_back(*iter);
520         }
522     /**
523      *
524      */
525     std::vector<unsigned char> read()
526         {
527         if (state != XmppClient::STREAM_OPEN)
528             {
529             std::vector<unsigned char>dummy;
530             return dummy;
531             }
532         std::vector<unsigned char> ret = data;
533         data.clear();
534         return ret;
535         }
537 private:
539     void assign(const XmppStream &other)
540        {
541        streamId  = other.streamId;
542        messageId = other.messageId;
543        sourceId  = other.sourceId;
544        state     = other.state;
545        seqNr     = other.seqNr;
546        data      = other.data;
547        }
550     DOMString streamId;
552     DOMString messageId;
554     DOMString sourceId;
556     int state;
558     long seqNr;
560     std::vector<unsigned char> data;
561 };
572 //########################################################################
573 //########################################################################
574 //# X M P P    C L I E N T
575 //########################################################################
576 //########################################################################
578 class ReceiverThread : public Runnable
580 public:
582     ReceiverThread(XmppClient &par) : client(par) {}
584     virtual ~ReceiverThread() {}
586     void run()
587       { client.receiveAndProcessLoop(); }
589 private:
591     XmppClient &client;
592 };
598 //########################################################################
599 //# CONSTRUCTORS
600 //########################################################################
602 XmppClient::XmppClient()
604     init();
608 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
610     init();
611     assign(other);
614 void XmppClient::assign(const XmppClient &other)
616     msgId         = other.msgId;
617     host          = other.host;
618     realm         = other.realm;
619     port          = other.port;
620     username      = other.username;
621     password      = other.password;
622     resource      = other.resource;
623     connected     = other.connected;
624     doRegister    = other.doRegister;
625     groupChats    = other.groupChats;
626     streamPacket  = other.streamPacket;
630 void XmppClient::init()
632     sock          = new TcpSocket();
633     msgId         = 0;
634     connected     = false;
635     doRegister    = false;
636     streamPacket  = "message";
640 XmppClient::~XmppClient()
642     disconnect();
643     delete sock;
644     std::map<DOMString, XmppStream *>::iterator iter;
645     for (iter = outputStreams.begin(); iter!=outputStreams.end() ; iter++)
646         delete iter->second;
647     for (iter = inputStreams.begin(); iter!=inputStreams.end() ; iter++)
648         delete iter->second;
649     for (iter = fileSends.begin(); iter!=fileSends.end() ; iter++)
650         delete iter->second;
651     groupChatsClear();
659 //########################################################################
660 //# UTILILY
661 //########################################################################
663 /**
664  *
665  */
666 bool XmppClient::pause(unsigned long millis)
668     Thread::sleep(millis);
669     return true;
673 static int strIndex(const DOMString &str, char *key)
675     unsigned int p = str.find(key);
676     if (p == str.npos)
677         return -1;
678     return p;
682 DOMString XmppClient::toXml(const DOMString &str)
684     return Parser::encode(str);
689 static DOMString trim(const DOMString &str)
691     unsigned int i;
692     for (i=0 ; i<str.size() ; i++)
693         if (!isspace(str[i]))
694             break;
695     int start = i;
696     for (i=str.size() ; i>0 ; i--)
697         if (!isspace(str[i-1]))
698             break;
699     int end = i;
700     if (start>=end)
701         return "";
702     return str.substr(start, end);
709 //########################################################################
710 //# VARIABLES  (ones that need special handling)
711 //########################################################################
713 /**
714  *
715  */
716 DOMString XmppClient::getUsername()
718     return username;
721 /**
722  *
723  */
724 void XmppClient::setUsername(const DOMString &val)
726     int p = strIndex(val, "@");
727     if (p > 0)
728         {
729         username = val.substr(0, p);
730         realm    = val.substr(p+1, jid.size()-p-1);
731         }
732     else
733        {
734        realm    = host;
735        username = val;
736        }
746 //########################################################################
747 //# RECEIVING
748 //########################################################################
751 DOMString XmppClient::readStanza()
754     int  openCount    = 0;
755     bool inTag        = false;
756     bool slashSeen    = false;
757     bool trivialTag   = false;
758     bool querySeen    = false;
759     bool inQuote      = false;
760     bool textSeen     = false;
761     DOMString buf;
764     time_t timeout = time((time_t *)0) + 180;
766     while (true)
767         {
768         int ch = sock->read();
769         //printf("%c", ch); fflush(stdout);
770         if (ch<0)
771             {
772             if (ch == -2) //a simple timeout, not an error
773                 {
774                 //Since we are timed out, let's assume that we
775                 //are between chunks of text.  Let's reset all states.
776                 //printf("-----#### Timeout\n");
777                 time_t currentTime = time((time_t *)0);
778                 if (currentTime > timeout)
779                     {
780                     timeout = currentTime + 180;
781                     if (!write("\n"))
782                         {
783                         error("ping send error");
784                         disconnect();
785                         return "";
786                         }
787                     }
788                 continue;
789                 }
790             else
791                 {
792                 keepGoing = false;
793                 if (!sock->isConnected())
794                     {
795                     disconnect();
796                     return "";
797                     }
798                 else
799                     {
800                     error("socket read error");
801                     disconnect();
802                     return "";
803                     }
804                 }
805             }
806         buf.push_back(ch);
807         if (ch == '<')
808             {
809             inTag      = true;
810             slashSeen  = false;
811             querySeen  = false;
812             inQuote    = false;
813             textSeen   = false;
814             trivialTag = false;
815             }
816         else if (ch == '>')
817             {
818             if (!inTag)  //unescaped '>' in pcdata? horror
819                 continue;
820             inTag     = false;
821             if (!trivialTag && !querySeen)
822                 {
823                 if (slashSeen)
824                     openCount--;
825                 else
826                     openCount++;
827                 }
828             //printf("# openCount:%d t:%d q:%d\n",
829             //      openCount, trivialTag, querySeen);
830             //check if we are 'balanced', but not a <?version?> tag
831             if (openCount <= 0 && !querySeen)
832                 {
833                 break;
834                 }
835             //we know that this one will be open-ended
836             if (strIndex(buf, "<stream:stream") >= 0)
837                 {
838                 buf.append("</stream:stream>");
839                 break;
840                 }
841             }
842         else if (ch == '/')
843             {
844             if (inTag && !inQuote)
845                 {
846                 slashSeen = true;
847                 if (textSeen) // <tagName/>  <--looks like this
848                     trivialTag = true;
849                 }
850             }
851         else if (ch == '?')
852             {
853             if (inTag && !inQuote)
854                 querySeen = true;
855             }
856         else if (ch == '"' || ch == '\'')
857             {
858             if (inTag)
859                 inQuote = !inQuote;
860             }
861         else
862             {
863             if (inTag && !inQuote && !isspace(ch))
864                 textSeen = true;
865             }
866         }
867     return buf;
872 static bool isGroupChat(Element *root)
874     if (!root)
875         return false;
876     std::vector<Element *>elems = root->findElements("x");
877     for (unsigned int i=0 ; i<elems.size() ; i++)
878         {
879         DOMString xmlns = elems[i]->getAttribute("xmlns");
880         //printf("### XMLNS ### %s\n", xmlns.c_str());
881         if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
882             return true;
883         }
884    return false;
890 static bool parseJid(const DOMString &fullJid,
891              DOMString &jid, DOMString &resource)
893     DOMString str = fullJid;
894     jid.clear();
895     resource.clear();
896     unsigned int p = str.size();
897     unsigned int p2 = str.rfind('/', p);
898     if (p2 != str.npos)
899         {
900         resource = str.substr(p2+1, p-(p2+1));
901         str = str.substr(0, p);
902         p = p2;
903         }
904     jid = str.substr(0, p);
905     printf("fullJid:%s jid:%s rsrc:%s\n",
906         fullJid.c_str(), jid.c_str(), resource.c_str());
907     return true;
913 bool XmppClient::processMessage(Element *root)
915     DOMString from    = root->getTagAttribute("message", "from");
916     DOMString to      = root->getTagAttribute("message", "to");
917     DOMString type    = root->getTagAttribute("message", "type");
919     //####Check for embedded namespaces here
920     //### FILE TRANSFERS
921     if (processFileMessage(root))
922         return true;
924     //### STREAMS
925     if (processInBandByteStreamMessage(root))
926         return true;
929     //#### NORMAL MESSAGES
930     DOMString subject = root->getTagValue("subject");
931     DOMString body    = root->getTagValue("body");
932     DOMString thread  = root->getTagValue("thread");
933     //##rfc 3921, para 2.4.  ignore if no recognizable info
934     //if (subject.size() < 1 && thread.size()<1)
935     //    return true;
937     if (type == "groupchat")
938         {
939         DOMString fromGid;
940         DOMString fromNick;
941         parseJid(from, fromGid, fromNick);
942         //printf("fromGid:%s  fromNick:%s\n",
943         //        fromGid.c_str(), fromNick.c_str());
944         DOMString toGid;
945         DOMString toNick;
946         parseJid(to, toGid, toNick);
947         //printf("toGid:%s  toNick:%s\n",
948         //        toGid.c_str(), toNick.c_str());
950         if (fromNick.size() > 0)//normal group message
951             {
952             XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
953             event.setGroup(fromGid);
954             event.setFrom(fromNick);
955             event.setData(body);
956             event.setDOM(root);
957             dispatchXmppEvent(event);
958             }
959         else // from the server itself
960             {
961             //note the space before, so it doesnt match 'unlocked'
962             if (strIndex(body, " locked") >= 0)
963                 {
964                 printf("LOCKED!! ;)\n");
965                 char *fmt =
966                 "<iq id='create%d' to='%s' type='set'>"
967                 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
968                 "<x xmlns='jabber:x:data' type='submit'/>"
969                 "</query></iq>\n";
970                 if (!write(fmt, msgId++, fromGid.c_str()))
971                     return false;
972                 }
973             }
974         }
975     else
976         {
977         XmppEvent event(XmppEvent::EVENT_MESSAGE);
978         event.setFrom(from);
979         event.setData(body);
980         event.setDOM(root);
981         dispatchXmppEvent(event);
982         }
984     return true;
990 bool XmppClient::processPresence(Element *root)
993     DOMString fullJid     = root->getTagAttribute("presence", "from");
994     DOMString to          = root->getTagAttribute("presence", "to");
995     DOMString presenceStr = root->getTagAttribute("presence", "type");
996     bool presence = true;
997     if (presenceStr == "unavailable")
998         presence = false;
999     DOMString status      = root->getTagValue("status");
1000     DOMString show        = root->getTagValue("show");
1002     if (isGroupChat(root))
1003         {
1004         DOMString fromGid;
1005         DOMString fromNick;
1006         parseJid(fullJid, fromGid, fromNick);
1007         //printf("fromGid:%s  fromNick:%s\n",
1008         //        fromGid.c_str(), fromNick.c_str());
1009         DOMString item_jid = root->getTagAttribute("item", "jid");
1010         if (item_jid == jid || item_jid == to) //Me
1011             {
1012             if (presence)
1013                 {
1014                 groupChatCreate(fromGid);
1015                 groupChatUserAdd(fromGid, fromNick, "");
1016                 groupChatUserShow(fromGid, fromNick, "available");
1018                 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
1019                 event.setGroup(fromGid);
1020                 event.setFrom(fromNick);
1021                 event.setPresence(presence);
1022                 event.setShow(show);
1023                 event.setStatus(status);
1024                 dispatchXmppEvent(event);
1025                 }
1026             else
1027                 {
1028                 groupChatDelete(fromGid);
1029                 groupChatUserDelete(fromGid, fromNick);
1031                 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
1032                 event.setGroup(fromGid);
1033                 event.setFrom(fromNick);
1034                 event.setPresence(presence);
1035                 event.setShow(show);
1036                 event.setStatus(status);
1037                 dispatchXmppEvent(event);
1038                 }
1039             }
1040         else // someone else
1041             {
1042             if (presence)
1043                 {
1044                 groupChatUserAdd(fromGid, fromNick, "");
1045                 }
1046             else
1047                 groupChatUserDelete(fromGid, fromNick);
1048             groupChatUserShow(fromGid, fromNick, show);
1049             XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
1050             event.setGroup(fromGid);
1051             event.setFrom(fromNick);
1052             event.setPresence(presence);
1053             event.setShow(show);
1054             event.setStatus(status);
1055             dispatchXmppEvent(event);
1056             }
1057         }
1058     else
1059         {
1060         DOMString shortJid;
1061         DOMString dummy;
1062         parseJid(fullJid, shortJid, dummy);
1063         rosterShow(shortJid, show); //users in roster do not have resource
1065         XmppEvent event(XmppEvent::EVENT_PRESENCE);
1066         event.setFrom(fullJid);
1067         event.setPresence(presence);
1068         event.setShow(show);
1069         event.setStatus(status);
1070         dispatchXmppEvent(event);
1071         }
1073     return true;
1078 bool XmppClient::processIq(Element *root)
1080     DOMString from  = root->getTagAttribute("iq", "from");
1081     DOMString id    = root->getTagAttribute("iq", "id");
1082     DOMString type  = root->getTagAttribute("iq", "type");
1083     DOMString xmlns = root->getTagAttribute("query", "xmlns");
1085     if (id.size()<1)
1086         return true;
1088     //Group chat
1089     if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
1090         {
1091         printf("results of MUC query\n");
1092         }
1093     //printf("###IQ xmlns:%s\n", xmlns.c_str());
1095     //### FILE TRANSFERS
1096     if (processFileMessage(root))
1097         return true;
1099     //### STREAMS
1100     if (processInBandByteStreamMessage(root))
1101         return true;
1102         
1104     //###Normal Roster stuff
1105     if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
1106         {
1107         roster.clear();
1108         std::vector<Element *>elems = root->findElements("item");
1109         for (unsigned int i=0 ; i<elems.size() ; i++)
1110             {
1111             Element *item = elems[i];
1112             DOMString userJid      = item->getAttribute("jid");
1113             DOMString name         = item->getAttribute("name");
1114             DOMString subscription = item->getAttribute("subscription");
1115             DOMString group        = item->getTagValue("group");
1116             //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
1117             //        subscription.c_str(), group.c_str());
1118             XmppUser user(userJid, name, subscription, group);
1119             roster.push_back(user);
1120             }
1121         XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
1122         dispatchXmppEvent(event);
1123         }
1125     else if (id.find("regnew") != id.npos)
1126         {
1128         }
1130     else if (id.find("regpass") != id.npos)
1131         {
1132         std::vector<Element *> list = root->findElements("error");
1133         if (list.size()==0)
1134             {
1135             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
1136             evt.setTo(username);
1137             evt.setFrom(host);
1138             dispatchXmppEvent(evt);
1139             return true;
1140             }
1142         Element *errElem = list[0];
1143         DOMString errMsg = "Password change error: ";
1144         if (errElem->findElements("bad-request").size()>0)
1145             {
1146             errMsg.append("password change does not contain complete information");
1147             }
1148         else if (errElem->findElements("not-authorized").size()>0)
1149             {
1150             errMsg.append("server does not consider the channel safe "
1151                           "enough to enable a password change");
1152             }
1153         else if (errElem->findElements("not-allowed").size()>0)
1154             {
1155             errMsg.append("server does not allow password changes");
1156             }
1157         else if (errElem->findElements("unexpected-request").size()>0)
1158             {
1159             errMsg.append(
1160              "IQ set does not contain a 'from' address because "
1161              "the entity is not registered with the server");
1162             }
1163         error("%s",(char *)errMsg.c_str());
1164         }
1166     else if (id.find("regcancel") != id.npos)
1167         {
1168         std::vector<Element *> list = root->findElements("error");
1169         if (list.size()==0)
1170             {
1171             XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
1172             evt.setTo(username);
1173             evt.setFrom(host);
1174             dispatchXmppEvent(evt);
1175             return true;
1176             }
1178         Element *errElem = list[0];
1179         DOMString errMsg = "Registration cancel error: ";
1180         if (errElem->findElements("bad-request").size()>0)
1181             {
1182             errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
1183             }
1184         else if (errElem->findElements("forbidden").size()>0)
1185             {
1186             errMsg.append("sender does not have sufficient permissions to cancel the registration");
1187             }
1188         else if (errElem->findElements("not-allowed").size()>0)
1189             {
1190             errMsg.append("not allowed to cancel registrations in-band");
1191             }
1192         else if (errElem->findElements("registration-required").size()>0)
1193             {
1194             errMsg.append("not previously registered");
1195             }
1196         else if (errElem->findElements("unexpected-request").size()>0)
1197             {
1198             errMsg.append(
1199                  "IQ set does not contain a 'from' address because "
1200                  "the entity is not registered with the server");
1201             }
1202         error("%s",(char *)errMsg.c_str());
1203         }
1205     return true;
1212 bool XmppClient::receiveAndProcess()
1214     if (!keepGoing)
1215         return false;
1217     Parser parser;
1219     DOMString recvBuf = readStanza();
1220     recvBuf = trim(recvBuf);
1221     if (recvBuf.size() < 1)
1222         return true;
1224     //Ugly hack.  Apparently the first char can be dropped on timeouts
1225     //if (recvBuf[0] != '<')
1226     //    recvBuf.insert(0, "<");
1228     status("RECV: %s", recvBuf.c_str());
1229     Element *root = parser.parse(recvBuf);
1230     if (!root)
1231         {
1232         printf("Bad elem\n");
1233         return true;
1234         }
1236     //#### MESSAGE
1237     std::vector<Element *>elems = root->findElements("message");
1238     if (elems.size()>0)
1239         {
1240         if (!processMessage(root))
1241             return false;
1242         }
1244     //#### PRESENCE
1245     elems = root->findElements("presence");
1246     if (elems.size()>0)
1247         {
1248         if (!processPresence(root))
1249             return false;
1250         }
1252     //#### INFO
1253     elems = root->findElements("iq");
1254     if (elems.size()>0)
1255         {
1256         if (!processIq(root))
1257             return false;
1258         }
1260     delete root;
1262     return true;
1266 bool XmppClient::receiveAndProcessLoop()
1268     keepGoing = true;
1269     while (true)
1270         {
1271         if (!keepGoing)
1272             {
1273             status("Abort requested");
1274             break;
1275             }
1276         if (!receiveAndProcess())
1277             return false;
1278         }
1279     return true;
1285 //########################################################################
1286 //# SENDING
1287 //########################################################################
1290 bool XmppClient::write(char *fmt, ...)
1292     bool rc = true;
1293     va_list args;
1294     va_start(args,fmt);
1295     gchar * buffer = g_strdup_vprintf(fmt,args);
1296     va_end(args) ;
1297     status("SEND: %s", buffer);
1298     if (!sock->write(buffer))
1299         {
1300         error("Cannot write to socket");
1301         rc = false;
1302         }
1303     g_free(buffer);
1304     return rc;
1312 //########################################################################
1313 //# R E G I S T R A T I O N
1314 //########################################################################
1316 /**
1317  * Perform JEP-077 In-Band Registration.  Performed synchronously after SSL,
1318  * before authentication
1319  */
1320 bool XmppClient::inBandRegistrationNew()
1322     Parser parser;
1324     char *fmt =
1325      "<iq type='get' id='regnew%d'>"
1326          "<query xmlns='jabber:iq:register'/>"
1327          "</iq>\n\n";
1328     if (!write(fmt, msgId++))
1329         return false;
1331     DOMString recbuf = readStanza();
1332     status("RECV reg: %s", recbuf.c_str());
1333     Element *elem = parser.parse(recbuf);
1334     //elem->print();
1336     //# does the entity send the newer "instructions" tag?
1337     std::vector<Element *> fields = elem->findElements("field");
1338     std::vector<DOMString> fnames;
1339     for (unsigned int i=0; i<fields.size() ; i++)
1340         {
1341         DOMString fname = fields[i]->getAttribute("var");
1342         if (fname == "FORM_TYPE")
1343             continue;
1344         fnames.push_back(fname);
1345         status("field name:%s", fname.c_str());
1346         }
1348     //Do we have any fields?
1349     if (fnames.size() == 0)
1350         {
1351         //If no fields, maybe the older method was offered
1352         if (elem->findElements("username").size() == 0 ||
1353             elem->findElements("password").size() == 0)
1354             {
1355             error("server did not offer registration");
1356             delete elem;
1357             return false;
1358             }
1359         }
1361     delete elem;
1363     fmt =
1364      "<iq type='set' id='regnew%d'>"
1365          "<query xmlns='jabber:iq:register'>"
1366          "<username>%s</username>"
1367          "<password>%s</password>"
1368          "<email/><name/>"
1369          "</query>"
1370          "</iq>\n\n";
1371     if (!write(fmt, msgId++, toXml(username).c_str(),
1372                     toXml(password).c_str() ))
1373         return false;
1376     recbuf = readStanza();
1377     status("RECV reg: %s", recbuf.c_str());
1378     elem = parser.parse(recbuf);
1379     //elem->print();
1381     std::vector<Element *> list = elem->findElements("error");
1382     if (list.size()>0)
1383         {
1384         Element *errElem = list[0];
1385         DOMString code = errElem->getAttribute("code");
1386         DOMString errMsg = "Registration error: ";
1387         if (code == "409")
1388             {
1389             errMsg.append("conflict with existing user name");
1390             }
1391         else if (code == "406")
1392             {
1393             errMsg.append("some registration information was not provided");
1394             }
1395         error("%s",(char *)errMsg.c_str());
1396         delete elem;
1397         return false;
1398         }
1400     delete elem;
1402     XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1403     evt.setTo(username);
1404     evt.setFrom(host);
1405     dispatchXmppEvent(evt);
1407     return true;
1411 /**
1412  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1413  * See processIq() for response handling.
1414  */
1415 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1417     Parser parser;
1419     //# Let's try it form-style to allow the common old/new password thing
1420     char *fmt =
1421       "<iq type='set' id='regpass%d' to='%s'>"
1422       "  <query xmlns='jabber:iq:register'>"
1423       "    <x xmlns='jabber:x:data' type='form'>"
1424       "      <field type='hidden' var='FORM_TYPE'>"
1425       "        <value>jabber:iq:register:changepassword</value>"
1426       "      </field>"
1427       "      <field type='text-single' var='username'>"
1428       "        <value>%s</value>"
1429       "      </field>"
1430       "      <field type='text-private' var='old_password'>"
1431       "        <value>%s</value>"
1432       "      </field>"
1433       "      <field type='text-private' var='password'>"
1434       "        <value>%s</value>"
1435       "      </field>"
1436       "    </x>"
1437       "  </query>"
1438       "</iq>\n\n";
1440     if (!write(fmt, msgId++, host.c_str(),
1441              username.c_str(), password.c_str(), newpassword.c_str()))
1442         return false;
1444     return true;
1449 /**
1450  * Perform JEP-077 In-Band Registration.  Performed asynchronously, after login.
1451  * See processIq() for response handling.
1452  */
1453 bool XmppClient::inBandRegistrationCancel()
1455     Parser parser;
1457     char *fmt =
1458      "<iq type='set' id='regcancel%d'>"
1459           "<query xmlns='jabber:iq:register'><remove/></query>"
1460           "</iq>\n\n";  
1461     if (!write(fmt, msgId++))
1462         return false;
1464     return true;
1471 //########################################################################
1472 //# A U T H E N T I C A T E
1473 //########################################################################
1475 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1477     Parser parser;
1479     char *fmt =
1480     "<iq type='get' to='%s' id='auth%d'>"
1481     "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1482     "</iq>\n";
1483     if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1484         return false;
1486     DOMString recbuf = readStanza();
1487     //printf("iq received: '%s'\n", recbuf.c_str());
1488     Element *elem = parser.parse(recbuf);
1489     //elem->print();
1490     DOMString iqType = elem->getTagAttribute("iq", "type");
1491     //printf("##iqType:%s\n", iqType.c_str());
1492     delete elem;
1494     if (iqType != "result")
1495         {
1496         error("error:server does not allow login");
1497         return false;
1498         }
1500     bool digest = true;
1501     if (digest)
1502         {
1503         //## Digest authentication
1504         DOMString digest = streamId;
1505         digest.append(password);
1506         digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
1507         //printf("digest:%s\n", digest.c_str());
1508         fmt =
1509         "<iq type='set' id='auth%d'>"
1510         "<query xmlns='jabber:iq:auth'>"
1511         "<username>%s</username>"
1512         "<digest>%s</digest>"
1513         "<resource>%s</resource>"
1514         "</query>"
1515         "</iq>\n";
1516         if (!write(fmt, msgId++, username.c_str(),
1517                     digest.c_str(), resource.c_str()))
1518             return false;
1519         }
1520     else
1521         {
1523         //## Plaintext authentication
1524         fmt =
1525         "<iq type='set' id='auth%d'>"
1526         "<query xmlns='jabber:iq:auth'>"
1527         "<username>%s</username>"
1528         "<password>%s</password>"
1529         "<resource>%s</resource>"
1530         "</query>"
1531         "</iq>\n";
1532         if (!write(fmt, msgId++, username.c_str(),
1533                    password.c_str(), resource.c_str()))
1534             return false;
1535         }
1537     recbuf = readStanza();
1538     //printf("iq received: '%s'\n", recbuf.c_str());
1539     elem = parser.parse(recbuf);
1540     //elem->print();
1541     iqType = elem->getTagAttribute("iq", "type");
1542     //printf("##iqType:%s\n", iqType.c_str());
1543     delete elem;
1545     if (iqType != "result")
1546         {
1547         error("server does not allow login");
1548         return false;
1549         }
1551     return true;
1555 /**
1556  * Parse a sasl challenge to retrieve all of its key=value pairs
1557  */
1558 static bool saslParse(const DOMString &s, 
1559                       std::map<DOMString, DOMString> &vals)
1562     vals.clear();
1564     int p  = 0;
1565     int siz = s.size();
1567     while (p < siz)
1568         {
1569         DOMString key;
1570         DOMString value;
1571         char ch = '\0';
1573         //# Parse key
1574         while (p<siz)
1575             {
1576             ch = s[p++];
1577             if (ch == '=')
1578                 break;
1579             key.push_back(ch);
1580             }
1582         //No value?
1583         if (ch != '=')
1584             break;
1586         //# Parse value
1587         bool quoted = false;
1588         while (p<siz)
1589             {
1590             ch = s[p++];
1591             if (ch == '"')
1592                 quoted = !quoted;
1593             else if (ch == ',' && !quoted)
1594                 break;
1595             else
1596                 value.push_back(ch);
1597             }
1599         //printf("# Key: '%s'  Value: '%s'\n", key.c_str(), value.c_str());
1600         vals[key] = value;
1601         if (ch != ',')
1602             break;
1603         }
1605     return true;
1610 /**
1611  * Attempt suthentication using the MD5 SASL mechanism
1612  */
1613 bool XmppClient::saslMd5Authenticate()
1615     Parser parser;
1616     char *fmt =
1617     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1618         "mechanism='DIGEST-MD5'/>\n";
1619     if (!write("%s",fmt))
1620         return false;
1622     DOMString recbuf = readStanza();
1623     status("challenge received: '%s'", recbuf.c_str());
1624     Element *elem = parser.parse(recbuf);
1625     //elem->print();
1626     DOMString b64challenge = elem->getTagValue("challenge");
1627     delete elem;
1629     if (b64challenge.size() < 1)
1630         {
1631         error("login: no SASL challenge offered by server");
1632         return false;
1633         }
1634     DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1635     status("md5 challenge:'%s'", challenge.c_str());
1637     std::map<DOMString, DOMString> attrs;
1638     if (!saslParse(challenge, attrs))
1639         {
1640         error("login: error parsing SASL challenge");
1641         return false;
1642         }
1644     DOMString nonce = attrs["nonce"];
1645     if (nonce.size()==0)
1646         {
1647         error("login: no SASL nonce sent by server");
1648         return false;
1649         }
1651     DOMString realm = attrs["realm"];
1652     if (realm.size()==0)
1653         {
1654         //Apparently this is not a problem
1655         //error("login: no SASL realm sent by server");
1656         //return false;
1657         }
1659     status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1661     char idBuf[10];
1662     snprintf(idBuf, 9, "%dsasl", msgId++);
1663     DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
1664     DOMString authzid = username; authzid.append("@"); authzid.append(host);
1665     DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1667     //## Make A1
1668     Md5 md5;
1669     md5.append(username);
1670     md5.append(":");
1671     md5.append(realm);
1672     md5.append(":");
1673     md5.append(password);
1674     unsigned char a1tmp[16];
1675     md5.finish(a1tmp);
1676     md5.init();
1677     md5.append(a1tmp, 16);
1678     md5.append(":");
1679     md5.append(nonce);
1680     md5.append(":");
1681     md5.append(cnonce);
1682     //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1683     //md5.append(":");
1684     //md5.append(authzid);
1685     md5.append("");
1686     DOMString a1 = md5.finishHex();
1687     status("##a1:'%s'", a1.c_str());
1689     //# Make A2
1690     md5.init();
1691     md5.append("AUTHENTICATE:");
1692     md5.append(digest_uri);
1693     DOMString a2 = md5.finishHex();
1694     status("##a2:'%s'", a2.c_str());
1696     //# Now make the response
1697     md5.init();
1698     md5.append(a1);
1699     md5.append(":");
1700     md5.append(nonce);
1701     md5.append(":");
1702     md5.append("00000001");//nc
1703     md5.append(":");
1704     md5.append(cnonce);
1705     md5.append(":");
1706     md5.append("auth");//qop
1707     md5.append(":");
1708     md5.append(a2);
1709     DOMString response = md5.finishHex();
1711     DOMString resp;
1712     resp.append("username=\""); resp.append(username); resp.append("\",");
1713     resp.append("realm=\"");    resp.append(realm);    resp.append("\",");
1714     resp.append("nonce=\"");    resp.append(nonce);    resp.append("\",");
1715     resp.append("cnonce=\"");   resp.append(cnonce);   resp.append("\",");
1716     resp.append("nc=00000001,qop=auth,");
1717     resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1718     //resp.append("authzid=\"");  resp.append(authzid);  resp.append("\",");
1719     resp.append("response=");   resp.append(response); resp.append(",");
1720     resp.append("charset=utf-8");
1721     status("sending response:'%s'", resp.c_str());
1722     resp = Base64Encoder::encode(resp);
1723     status("base64 response:'%s'", resp.c_str());
1724     fmt =
1725     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1726     if (!write(fmt, resp.c_str()))
1727         return false;
1729     recbuf = readStanza();
1730     status("server says:: '%s'", recbuf.c_str());
1731     elem = parser.parse(recbuf);
1732     //elem->print();
1733     //# Early success?
1734     if (elem->findElements("success").size() > 0)
1735         {
1736         delete elem;
1737         return true;
1738         }
1739     //# Continue for one more SASL cycle
1740     b64challenge = elem->getTagValue("challenge");
1741     delete elem;
1743     if (b64challenge.size() < 1)
1744         {
1745         error("login: no second SASL challenge offered by server");
1746         return false;
1747         }
1749     challenge = Base64Decoder::decodeToString(b64challenge);
1750     status("md5 challenge: '%s'", challenge.c_str());
1752     if (!saslParse(challenge, attrs))
1753         {
1754         error("login: error parsing SASL challenge");
1755         return false;
1756         }
1758     DOMString rspauth = attrs["rspauth"];
1759     if (rspauth.size()==0)
1760         {
1761         error("login: no SASL respauth sent by server\n");
1762         return false;
1763         }
1765     fmt =
1766     "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1767     if (!write("%s",fmt))
1768         return false;
1770     recbuf = readStanza();
1771     status("SASL recv: '%s", recbuf.c_str());
1772     elem = parser.parse(recbuf);
1773     //elem->print();
1774     b64challenge = elem->getTagValue("challenge");
1775     bool success = (elem->findElements("success").size() > 0);
1776     delete elem;
1778     return success;
1783 /**
1784  *  Attempt to authentication using the SASL PLAIN mechanism.  This
1785  *  is used most commonly my Google Talk.
1786  */
1787 bool XmppClient::saslPlainAuthenticate()
1789     Parser parser;
1791     DOMString id = username;
1792     //id.append("@");
1793     //id.append(host);
1794     Base64Encoder encoder;
1795     encoder.append('\0');
1796     encoder.append(id);
1797     encoder.append('\0');
1798     encoder.append(password);
1799     DOMString base64Auth = encoder.finish();
1800     //printf("authbuf:%s\n", base64Auth.c_str());
1802     char *fmt =
1803     "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1804     "mechanism='PLAIN'>%s</auth>\n";
1805     if (!write(fmt, base64Auth.c_str()))
1806         return false;
1807     DOMString recbuf = readStanza();
1808     status("challenge received: '%s'", recbuf.c_str());
1809     Element *elem = parser.parse(recbuf);
1811     bool success = (elem->findElements("success").size() > 0);
1812     delete elem;
1814     return success;
1819 /**
1820  * Handshake with SASL, and use one of its offered mechanisms to
1821  * authenticate.
1822  */
1823 bool XmppClient::saslAuthenticate()
1825     Parser parser;
1827     DOMString recbuf = readStanza();
1828     status("RECV: '%s'\n", recbuf.c_str());
1829     Element *elem = parser.parse(recbuf);
1830     //elem->print();
1832     //Check for starttls
1833     bool wantStartTls = false;
1834     if (elem->findElements("starttls").size() > 0)
1835         {
1836         wantStartTls = true;
1837         if (elem->findElements("required").size() > 0)
1838             status("login: STARTTLS required");
1839         else
1840             status("login: STARTTLS available");
1841         }
1843     //# do we want TLS, are we not already running SSL, and can
1844     //# the client actually do an ssl connection?
1845     if (wantStartTls && !sock->getEnableSSL() && sock->getHaveSSL())
1846         {
1847         delete elem;
1848         char *fmt =
1849         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
1850         if (!write("%s",fmt))
1851             return false;
1852         recbuf = readStanza();
1853         status("RECV: '%s'\n", recbuf.c_str());
1854         elem = parser.parse(recbuf);
1855         if (elem->getTagAttribute("proceed", "xmlns").size()<1)
1856             {
1857             error("Server rejected TLS negotiation");
1858             disconnect();
1859             return false;
1860             }
1861         delete elem;
1862         if (!sock->startTls())
1863             {
1864             error("Could not start TLS");
1865             disconnect();
1866             return false;
1867             }
1869         fmt =
1870          "<stream:stream xmlns='jabber:client' "
1871          "xmlns:stream='http://etherx.jabber.org/streams' "
1872          "to='%s' version='1.0'>\n\n";
1873         if (!write(fmt, realm.c_str()))
1874             return false;
1876         recbuf = readStanza();
1877         status("RECVx: '%s'", recbuf.c_str());
1878         recbuf.append("</stream:stream>");
1879         elem = parser.parse(recbuf);
1880         bool success =
1881         (elem->getTagAttribute("stream:stream", "id").size()>0);
1882         if (!success)
1883             {
1884             error("STARTTLS negotiation failed");
1885             disconnect();
1886             return false;
1887             }
1888         delete elem;
1889         recbuf = readStanza();
1890         status("RECV: '%s'\n", recbuf.c_str());
1891         elem = parser.parse(recbuf);
1893         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
1894         dispatchXmppEvent(event);
1895         }
1897     //register, if user requests
1898     if (doRegister)
1899         {
1900         if (!inBandRegistrationNew())
1901             return false;
1902         }
1904     //check for sasl authentication mechanisms
1905     std::vector<Element *> elems =
1906                elem->findElements("mechanism");
1907     if (elems.size() < 1)
1908         {
1909         error("login: no SASL mechanism offered by server");
1910         return false;
1911         }
1912     bool md5Found = false;
1913     bool plainFound = false;
1914     for (unsigned int i=0 ; i<elems.size() ; i++)
1915         {
1916         DOMString mech = elems[i]->getValue();
1917         if (mech == "DIGEST-MD5")
1918             {
1919             status("MD5 authentication offered");
1920             md5Found = true;
1921             }
1922         else if (mech == "PLAIN")
1923             {
1924             status("PLAIN authentication offered");
1925             plainFound = true;
1926             }
1927         }
1928     delete elem;
1930     bool success = false;
1931     if (md5Found)
1932         {
1933         success = saslMd5Authenticate();
1934         }
1935     else if (plainFound)
1936         {
1937         success = saslPlainAuthenticate();
1938         }
1939     else
1940         {
1941         error("not able to handle sasl authentication mechanisms");
1942         return false;
1943         }
1945     if (success)
1946         status("###### SASL authentication success\n");
1947     else
1948         error("###### SASL authentication failure\n");
1950     return success;
1958 //########################################################################
1959 //# CONNECT
1960 //########################################################################
1963 /**
1964  * Check if we are connected, and fail with an error if we are not
1965  */
1966 bool XmppClient::checkConnect()
1968     if (!connected)
1969         {
1970         XmppEvent evt(XmppEvent::EVENT_ERROR);
1971         evt.setData("Attempted operation while disconnected");
1972         dispatchXmppEvent(evt);
1973         return false;
1974         }
1975     return true;
1980 /**
1981  * Create an XMPP session with a server.  This
1982  * is basically the transport layer of XMPP.
1983  */
1984 bool XmppClient::createSession()
1987     Parser parser;
1988     if (port==443 || port==5223)
1989         sock->enableSSL(true);
1990     if (!sock->connect(host, port))
1991         {
1992         return false;
1993         }
1995     if (sock->getEnableSSL())
1996         {
1997         XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
1998         dispatchXmppEvent(event);
1999         }
2001     char *fmt =
2002      "<stream:stream "
2003           "to='%s' "
2004           "xmlns='jabber:client' "
2005           "xmlns:stream='http://etherx.jabber.org/streams' "
2006           "version='1.0'>\n\n";
2007     if (!write(fmt, realm.c_str()))
2008         return false;
2010     DOMString recbuf = readStanza();
2011     //printf("received: '%s'\n", recbuf.c_str());
2012     recbuf.append("</stream:stream>");
2013     Element *elem = parser.parse(recbuf);
2014     //elem->print();
2015     bool useSasl = false;
2016     DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2017     //printf("### StreamID: %s\n", streamId.c_str());
2018     DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2019     if (streamVersion == "1.0")
2020         useSasl = true;
2022     if (useSasl)
2023         {
2024         if (!saslAuthenticate())
2025             return false;
2026         fmt =
2027           "<stream:stream "
2028           "to='%s' "
2029           "xmlns='jabber:client' "
2030           "xmlns:stream='http://etherx.jabber.org/streams' "
2031           "version='1.0'>\n\n";
2033         if (!write(fmt, realm.c_str()))
2034             return false;
2035         recbuf = readStanza();
2036         recbuf.append("</stream:stream>\n");
2037         //printf("now server says:: '%s'\n", recbuf.c_str());
2038         elem = parser.parse(recbuf);
2039         //elem->print();
2040         delete elem;
2042         recbuf = readStanza();
2043         //printf("now server says:: '%s'\n", recbuf.c_str());
2044         elem = parser.parse(recbuf);
2045         bool hasBind = (elem->findElements("bind").size() > 0);
2046         //elem->print();
2047         delete elem;
2049         if (!hasBind)
2050             {
2051             error("no binding provided by server");
2052             return false;
2053             }
2056         }
2057     else // not SASL
2058         {
2059         if (!iqAuthenticate(streamId))
2060             return false;
2061         }
2064     //### Resource binding
2065     fmt =
2066     "<iq type='set' id='bind%d'>"
2067     "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2068     "<resource>%s</resource>"
2069     "</bind></iq>\n";
2070     if (!write(fmt, msgId++, resource.c_str()))
2071         return false;
2073     recbuf = readStanza();
2074     status("bind result: '%s'", recbuf.c_str());
2075     elem = parser.parse(recbuf);
2076     //elem->print();
2077     DOMString bindType = elem->getTagAttribute("iq", "type");
2078     //printf("##bindType:%s\n", bindType.c_str());
2079     DOMString givenFullJid = elem->getTagValue("jid");
2080     delete elem;
2082     if (bindType != "result")
2083         {
2084         error("no binding with server failed");
2085         return false;
2086         }
2087         
2088     //The server sent us a JID.  We need to listen.
2089     if (givenFullJid.size()>0)
2090         {
2091         DOMString givenJid, givenResource;
2092         parseJid(givenFullJid, givenJid, givenResource);
2093         status("given user: %s realm: %s, rsrc: %s",
2094            givenJid.c_str(), realm.c_str(), givenResource.c_str());
2095         setResource(givenResource);
2096         }
2097         
2099     fmt =
2100     "<iq type='set' id='sess%d'>"
2101     "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2102     "</iq>\n";
2103     if (!write(fmt, msgId++))
2104         return false;
2106     recbuf = readStanza();
2107     status("session received: '%s'", recbuf.c_str());
2108     elem = parser.parse(recbuf);
2109     //elem->print();
2110     DOMString sessionType = elem->getTagAttribute("iq", "type");
2111     //printf("##sessionType:%s\n", sessionType.c_str());
2112     delete elem;
2114     if (sessionType != "result")
2115         {
2116         error("no session provided by server");
2117         return false;
2118         }
2120     //printf("########## COOL #########\n");
2121     //Now that we are bound, we have a valid JID
2122     jid = username;
2123     jid.append("@");
2124     jid.append(realm);
2125     jid.append("/");
2126     jid.append(resource);
2128     //We are now done with the synchronous handshaking.  Let's go into
2129     //async mode
2131     fmt =
2132      "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2133     if (!write(fmt, msgId++))
2134         return false;
2136     fmt =
2137      "<iq type='get' id='discoItems%d' to='%s'>"
2138      "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2139     if (!write(fmt, msgId++, realm.c_str()))
2140         return false;
2142     fmt =
2143     "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2144     "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2145     if (!write(fmt, msgId++, realm.c_str()))
2146         return false;
2148     fmt =
2149      "<presence/>\n";
2150     if (!write("%s",fmt))
2151         return false;
2153     /*
2154     recbuf = readStanza();
2155     status("stream received: '%s'", recbuf.c_str());
2156     elem = parser.parse(recbuf);
2157     //elem->print();
2158     delete elem;
2159     */
2161     //We are now logged in
2162     status("Connected");
2163     connected = true;
2164     XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2165     evt.setData(host);
2166     dispatchXmppEvent(evt);
2167     //Thread::sleep(1000000);
2169     sock->setReceiveTimeout(1000);
2170     ReceiverThread runner(*this);
2171     Thread thread(runner);
2172     thread.start();
2174     return true;
2179 /**
2180  * Public call to connect
2181  */
2182 bool XmppClient::connect()
2184     if (!createSession())
2185         {
2186         disconnect();
2187         return false;
2188         }
2189     return true;
2193 /**
2194  * Public call to connect
2195  */
2196 bool XmppClient::connect(DOMString hostArg, int portArg,
2197                          DOMString usernameArg,
2198                          DOMString passwordArg,
2199                          DOMString resourceArg)
2201     host     = hostArg;
2202     port     = portArg;
2203     password = passwordArg;
2204     resource = resourceArg;
2206     //parse this one
2207     setUsername(usernameArg);
2209     bool ret = connect();
2210     return ret;
2215 /**
2216  * Public call to disconnect
2217  */
2218 bool XmppClient::disconnect()
2220     if (connected)
2221         {
2222         char *fmt =
2223         "<presence type='unavailable'/>\n";
2224         write("%s",fmt);
2225         }
2226     keepGoing = false;
2227     connected = false;
2228     Thread::sleep(2000); //allow receiving thread to quit
2229     sock->disconnect();
2230     roster.clear();
2231     groupChatsClear();
2232     XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2233     event.setData(host);
2234     dispatchXmppEvent(event);
2235     return true;
2242 //########################################################################
2243 //# ROSTER
2244 //########################################################################
2246 /**
2247  *  Add an XMPP id to your roster
2248  */
2249 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2250                            const DOMString &otherJid,
2251                            const DOMString &name)
2253     if (!checkConnect())
2254         return false;
2255     char *fmt =
2256     "<iq type='set' id='roster_%d'>"
2257     "<query xmlns='jabber:iq:roster'>"
2258     "<item jid='%s' name='%s'><group>%s</group></item>"
2259     "</query></iq>\n";
2260     if (!write(fmt, msgId++, otherJid.c_str(),
2261          name.c_str(), rosterGroup.c_str()))
2262         {
2263         return false;
2264         }
2265     return true;
2270 /**
2271  *  Delete an XMPP id from your roster.
2272  */
2273 bool XmppClient::rosterDelete(const DOMString &otherJid)
2275     if (!checkConnect())
2276         return false;
2277     char *fmt =
2278     "<iq type='set' id='roster_%d'>"
2279     "<query xmlns='jabber:iq:roster'>"
2280     "<item jid='%s' subscription='remove'><group>%s</group></item>"
2281     "</query></iq>\n";
2282     if (!write(fmt, msgId++, otherJid.c_str()))
2283         {
2284         return false;
2285         }
2286     return true;
2290 /**
2291  *  Comparison method for sort() call below
2292  */
2293 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2295     DOMString s1 = p1.group;
2296     DOMString s2 = p2.group;
2297     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2298         {
2299         int comp = tolower(s1[len]) - tolower(s2[len]);
2300         if (comp)
2301             return (comp<0);
2302         }
2304     s1 = p1.jid;
2305     s2 = p2.jid;
2306     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2307         {
2308         int comp = tolower(s1[len]) - tolower(s2[len]);
2309         if (comp)
2310             return (comp<0);
2311         }
2312     return false;
2317 /**
2318  *  Sort and return the roster that has just been reported by
2319  *  an XmppEvent::EVENT_ROSTER event.
2320  */
2321 std::vector<XmppUser> XmppClient::getRoster()
2323     std::vector<XmppUser> ros = roster;
2324     std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2325     return ros;
2329 /**
2330  *
2331  */
2332 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2334     DOMString theShow = show;
2335     if (theShow == "")
2336         theShow = "available";
2338     std::vector<XmppUser>::iterator iter;
2339     for (iter=roster.begin() ; iter != roster.end() ; iter++)
2340         {
2341         if (iter->jid == jid)
2342             iter->show = theShow;
2343         }
2351 //########################################################################
2352 //# CHAT (individual)
2353 //########################################################################
2355 /**
2356  * Send a message to an xmpp jid
2357  */
2358 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2359                          const DOMString &msg)
2361     if (!checkConnect())
2362         return false;
2364     DOMString xmlSubj = toXml(subj);
2365     DOMString xmlMsg  = toXml(msg);
2367     if (xmlSubj.size() > 0)
2368         {
2369         char *fmt =
2370         "<message to='%s' from='%s' type='chat'>"
2371         "<subject>%s</subject><body>%s</body></message>\n";
2372         if (!write(fmt, user.c_str(), jid.c_str(),
2373                 xmlSubj.c_str(), xmlMsg.c_str()))
2374             return false;
2375         }
2376     else
2377         {
2378         char *fmt =
2379         "<message to='%s' from='%s'>"
2380         "<body>%s</body></message>\n";
2381         if (!write(fmt, user.c_str(), jid.c_str(), xmlMsg.c_str()))
2382             return false;
2383         }
2384     return true;
2389 /**
2390  *
2391  */
2392 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2394     return message(user, "", msg);
2399 /**
2400  *
2401  */
2402 bool XmppClient::presence(const DOMString &presence)
2404     if (!checkConnect())
2405         return false;
2407     DOMString xmlPres = toXml(presence);
2409     char *fmt =
2410     "<presence><show>%s</show></presence>\n";
2411     if (!write(fmt, xmlPres.c_str()))
2412         return false;
2413     return true;
2421 //########################################################################
2422 //# GROUP  CHAT
2423 //########################################################################
2425 /**
2426  *
2427  */
2428 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2430     std::vector<XmppGroupChat *>::iterator iter;
2431     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2432         {
2433         if ((*iter)->getGroupJid() == groupJid)
2434             {
2435             //error("Group chat '%s' already exists", groupJid.c_str());
2436             return false;
2437             }
2438         }
2439     XmppGroupChat *chat = new XmppGroupChat(groupJid);
2440     groupChats.push_back(chat);
2441     return true;
2446 /**
2447  *
2448  */
2449 void XmppClient::groupChatDelete(const DOMString &groupJid)
2451     std::vector<XmppGroupChat *>::iterator iter;
2452     for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2453         {
2454         XmppGroupChat *chat = *iter;
2455         if (chat->getGroupJid() == groupJid)
2456             {
2457             iter = groupChats.erase(iter);
2458             delete chat;
2459             }
2460         else
2461             iter++;
2462         }
2467 /**
2468  *
2469  */
2470 bool XmppClient::groupChatExists(const DOMString &groupJid)
2472     std::vector<XmppGroupChat *>::iterator iter;
2473     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2474         if ((*iter)->getGroupJid() == groupJid)
2475             return true;
2476     return false;
2481 /**
2482  *
2483  */
2484 void XmppClient::groupChatsClear()
2486     std::vector<XmppGroupChat *>::iterator iter;
2487     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2488         delete (*iter);
2489     groupChats.clear();
2495 /**
2496  *
2497  */
2498 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2499                                   const DOMString &nick,
2500                                   const DOMString &jid)
2502     std::vector<XmppGroupChat *>::iterator iter;
2503     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2504         {
2505         if ((*iter)->getGroupJid() == groupJid)
2506             {
2507             (*iter)->userAdd(nick, jid);
2508             }
2509         }
2514 /**
2515  *
2516  */
2517 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2518                                    const DOMString &nick,
2519                                    const DOMString &show)
2521     std::vector<XmppGroupChat *>::iterator iter;
2522     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2523         {
2524         if ((*iter)->getGroupJid() == groupJid)
2525             {
2526             (*iter)->userShow(nick, show);
2527             }
2528         }
2534 /**
2535  *
2536  */
2537 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2538                                      const DOMString &nick)
2540     std::vector<XmppGroupChat *>::iterator iter;
2541     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2542         {
2543         if ((*iter)->getGroupJid() == groupJid)
2544             {
2545             (*iter)->userDelete(nick);
2546             }
2547         }
2552 /**
2553  *  Comparison method for the sort() below
2554  */
2555 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2557     DOMString s1 = p1.nick;
2558     DOMString s2 = p2.nick;
2559     int comp = 0;
2560     for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2561         {
2562         comp = tolower(s1[len]) - tolower(s2[len]);
2563         if (comp)
2564             break;
2565         }
2566     return (comp<0);
2571 /**
2572  *  Return the user list for the named group
2573  */
2574 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2575                               const DOMString &groupJid)
2577     if (!checkConnect())
2578         {
2579         std::vector<XmppUser> dummy;
2580         return dummy;
2581         }
2583     std::vector<XmppGroupChat *>::iterator iter;
2584     for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2585         {
2586         if ((*iter)->getGroupJid() == groupJid )
2587             {
2588             std::vector<XmppUser> uList = (*iter)->getUserList();
2589             std::sort(uList.begin(), uList.end(), xmppUserCompare);
2590             return uList;
2591             }
2592         }
2593     std::vector<XmppUser> dummy;
2594     return dummy;
2600 /**
2601  *  Try to join a group
2602  */
2603 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2604                                const DOMString &nick,
2605                                const DOMString &pass)
2607     if (!checkConnect())
2608         return false;
2610     DOMString user = nick;
2611     if (user.size()<1)
2612         user = username;
2614     char *fmt =
2615     "<presence to='%s/%s'>"
2616     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2617     if (!write(fmt, groupJid.c_str(), user.c_str()))
2618         return false;
2619     return true;
2625 /**
2626  * Leave a group
2627  */
2628 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2629                                 const DOMString &nick)
2631     if (!checkConnect())
2632         return false;
2634     DOMString user = nick;
2635     if (user.size()<1)
2636         user = username;
2638     char *fmt =
2639     "<presence to='%s/%s' type='unavailable'>"
2640     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2641     if (!write(fmt, groupJid.c_str(), user.c_str()))
2642         return false;
2643     return true;
2649 /**
2650  *  Send a message to a group
2651  */
2652 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2653                                   const DOMString &msg)
2655     if (!checkConnect())
2656         {
2657         return false;
2658         }
2660     DOMString xmlMsg = toXml(msg);
2662     char *fmt =
2663     "<message from='%s' to='%s' type='groupchat'>"
2664     "<body>%s</body></message>\n";
2665     if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2666         return false;
2667     /*
2668     char *fmt =
2669     "<message to='%s' type='groupchat'>"
2670     "<body>%s</body></message>\n";
2671     if (!write(fmt, groupJid.c_str(), xmlMsg.c_str()))
2672         return false;
2673     */
2674     return true;
2680 /**
2681  *  Send a message to an individual in a group
2682  */
2683 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2684                                          const DOMString &toNick,
2685                                          const DOMString &msg)
2687     if (!checkConnect())
2688         return false;
2690     DOMString xmlMsg = toXml(msg);
2692     /*
2693     char *fmt =
2694     "<message from='%s' to='%s/%s' type='chat'>"
2695     "<body>%s</body></message>\n";
2696     if (!write(fmt, jid.c_str(), groupJid.c_str(),
2697                toNick.c_str(), xmlMsg.c_str()))
2698         return false;
2699     */
2700     char *fmt =
2701     "<message to='%s/%s' type='chat'>"
2702     "<body>%s</body></message>\n";
2703     if (!write(fmt, groupJid.c_str(),
2704                toNick.c_str(), xmlMsg.c_str()))
2705         return false;
2706     return true;
2712 /**
2713  *  Change your presence within a group
2714  */
2715 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2716                                    const DOMString &myNick,
2717                                    const DOMString &presence)
2719     if (!checkConnect())
2720         return false;
2722     DOMString user = myNick;
2723     if (user.size()<1)
2724         user = username;
2726     DOMString xmlPresence = toXml(presence);
2728     char *fmt =
2729     "<presence to='%s/%s' type='%s'>"
2730     "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2731     if (!write(fmt, groupJid.c_str(),
2732                user.c_str(), xmlPresence.c_str()))
2733         return true;
2734     return true;
2741 //########################################################################
2742 //# S T R E A M S
2743 //########################################################################
2746 bool XmppClient::processInBandByteStreamMessage(Element *root)
2748     DOMString from  = root->getAttribute("from");
2749     DOMString id    = root->getAttribute("id");
2750     DOMString type  = root->getAttribute("type");
2752     //### Incoming stream requests
2753     //Input streams are id's by stream id
2754     DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
2756     if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
2757         {
2758         DOMString streamId = root->getTagAttribute("open", "sid");
2759         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
2760         dispatchXmppEvent(event);
2761         std::map<DOMString, XmppStream *>::iterator iter =
2762                      inputStreams.find(streamId);
2763         if (iter != inputStreams.end())
2764             {
2765             XmppStream *ins = iter->second;
2766             ins->setState(STREAM_OPENING);
2767             ins->setMessageId(id);
2768             return true;
2769             }
2770         return true;
2771         }
2773     else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
2774         {
2775         XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
2776         dispatchXmppEvent(event);
2777         DOMString streamId = root->getTagAttribute("close", "sid");
2778         std::map<DOMString, XmppStream *>::iterator iter =
2779                      inputStreams.find(streamId);
2780         if (iter != inputStreams.end())
2781             {
2782             XmppStream *ins = iter->second;
2783             if (from == ins->getPeerId())
2784                 {
2785                 ins->setState(STREAM_CLOSING);
2786                 ins->setMessageId(id);
2787                 return true;
2788                 }
2789             }
2790         return true;
2791         }
2793     else if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
2794         {
2795         DOMString streamId = root->getTagAttribute("data", "sid");
2796         std::map<DOMString, XmppStream *>::iterator iter =
2797                      inputStreams.find(streamId);
2798         if (iter != inputStreams.end())
2799             {
2800             XmppStream *ins = iter->second;
2801             if (ins->getState() != STREAM_OPEN)
2802                 {
2803                 XmppEvent event(XmppEvent::EVENT_ERROR);
2804                 event.setFrom(from);
2805                 event.setData("received unrequested stream data");
2806                 dispatchXmppEvent(event);
2807                 return true;
2808                 }
2809             DOMString data = root->getTagValue("data");
2810             std::vector<unsigned char>binData =
2811                        Base64Decoder::decode(data);
2812             ins->receiveData(binData);
2813             }
2814         }
2816     //### Responses to outgoing requests
2817     //Output streams are id's by message id
2818     std::map<DOMString, XmppStream *>::iterator iter =
2819                      outputStreams.find(id);
2820     if (iter != outputStreams.end())
2821         {
2822         XmppStream *outs = iter->second;
2823         if (type == "error")
2824             {
2825             outs->setState(STREAM_ERROR);
2826             return true;
2827             }
2828         else if (type == "result")
2829             {
2830             if (outs->getState() == STREAM_OPENING)
2831                 {
2832                 outs->setState(STREAM_OPEN);
2833                 }
2834             else if (outs->getState() == STREAM_CLOSING)
2835                 {
2836                 outs->setState(STREAM_CLOSED);
2837                 }
2838             return true;
2839             }
2840         }
2842     return false;
2846 /**
2847  *
2848  */
2849 bool XmppClient::outputStreamOpen(const DOMString &destId,
2850                                  const DOMString &streamIdArg)
2852     char buf[32];
2853     snprintf(buf, 31, "inband%d", getMsgId());
2854     DOMString messageId = buf;
2856     //Output streams are id's by message id
2857     XmppStream *outs = new XmppStream();
2858     outputStreams[messageId] = outs;
2860     outs->setState(STREAM_OPENING);
2862     DOMString streamId = streamIdArg;
2863     if (streamId.size()<1)
2864         {
2865         snprintf(buf, 31, "stream%d", getMsgId());
2866         DOMString streamId = buf;
2867         }
2868     outs->setMessageId(messageId);
2869     outs->setStreamId(streamId);
2870     outs->setPeerId(destId);
2873     char *fmt =
2874     "<%s type='set' to='%s' id='%s'>"
2875     "<open sid='%s' block-size='4096'"
2876     " xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
2877     if (!write(fmt,
2878               streamPacket.c_str(),
2879               destId.c_str(), messageId.c_str(),
2880               streamId.c_str(),
2881               streamPacket.c_str()))
2882         {
2883         outs->reset();
2884         return -1;
2885         }
2887     int state = outs->getState();
2888     for (int tim=0 ; tim<20 ; tim++)
2889         {
2890         if (state == STREAM_OPEN)
2891             break;
2892         else if (state == STREAM_ERROR)
2893             {
2894             printf("ERROR\n");
2895             outs->reset();
2896             return false;
2897             }
2898         Thread::sleep(1000);
2899         state = outs->getState();
2900         }
2901     if (state != STREAM_OPEN)
2902         {
2903         printf("TIMEOUT ERROR\n");
2904         outs->reset();
2905         return -1;
2906         }
2908     return true;
2911 /**
2912  *
2913  */
2914 bool XmppClient::outputStreamWrite(const DOMString &streamId,
2915                const std::vector<unsigned char> &buf)
2917     std::map<DOMString, XmppStream *>::iterator iter =
2918         outputStreams.find(streamId);
2919     if (iter == outputStreams.end())
2920         return false;
2921     XmppStream *outs = iter->second;
2923     unsigned int len = buf.size();
2924     unsigned int pos = 0;
2926     while (pos < len)
2927         {
2928         unsigned int pos2 = pos + 1024;
2929         if (pos2>len)
2930             pos2 = len;
2932         Base64Encoder encoder;
2933         for (unsigned int i=pos ; i<pos2 ; i++)
2934             encoder.append(buf[i]);
2935         DOMString b64data = encoder.finish();
2938         char *fmt =
2939         "<message to='%s' id='msg%d'>"
2940         "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
2941         "%s"
2942         "</data>"
2943         "<amp xmlns='http://jabber.org/protocol/amp'>"
2944         "<rule condition='deliver-at' value='stored' action='error'/>"
2945         "<rule condition='match-resource' value='exact' action='error'/>"
2946         "</amp>"
2947         "</message>\n";
2948         if (!write(fmt,
2949               outs->getPeerId().c_str(),
2950               getMsgId(),
2951               outs->getStreamId().c_str(),
2952               outs->getSeqNr(),
2953               b64data.c_str()))
2954             {
2955             outs->reset();
2956             return false;
2957             }
2958         pause(5000);
2959         
2960         pos = pos2;
2961         }
2962         
2963     return true;
2966 /**
2967  *
2968  */
2969 bool XmppClient::outputStreamClose(const DOMString &streamId)
2971     std::map<DOMString, XmppStream *>::iterator iter =
2972         outputStreams.find(streamId);
2973     if (iter == outputStreams.end())
2974         return false;
2975     XmppStream *outs = iter->second;
2977     char buf[32];
2978     snprintf(buf, 31, "inband%d", getMsgId());
2979     DOMString messageId = buf;
2980     outs->setMessageId(messageId);
2982     outs->setState(STREAM_CLOSING);
2983     char *fmt =
2984     "<%s type='set' to='%s' id='%s'>"
2985     "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></%s>\n";
2986     if (!write(fmt,
2987             streamPacket.c_str(),
2988             outs->getPeerId().c_str(),
2989             messageId.c_str(),
2990             outs->getStreamId().c_str(),
2991             streamPacket.c_str()
2992             ))
2993         return false;
2995     int state = outs->getState();
2996     for (int tim=0 ; tim<20 ; tim++)
2997         {
2998         if (state == STREAM_CLOSED)
2999             break;
3000         else if (state == STREAM_ERROR)
3001             {
3002             printf("ERROR\n");
3003             outs->reset();
3004             return false;
3005             }
3006         Thread::sleep(1000);
3007         state = outs->getState();
3008         }
3009     if (state != STREAM_CLOSED)
3010         {
3011         printf("TIMEOUT ERROR\n");
3012         outs->reset();
3013         return false;
3014         }
3016     delete outs;
3017     outputStreams.erase(streamId);
3019     return true;
3023 /**
3024  *
3025  */
3026 bool XmppClient::inputStreamOpen(const DOMString &fromJid,
3027                                 const DOMString &streamId,
3028                                 const DOMString &iqId)
3030     XmppStream *ins = new XmppStream();
3032     inputStreams[streamId] = ins;
3033     ins->reset();
3034     ins->setPeerId(fromJid);
3035     ins->setState(STREAM_CLOSED);
3036     ins->setStreamId(streamId);
3038     int state = ins->getState();
3039     for (int tim=0 ; tim<20 ; tim++)
3040         {
3041         if (state == STREAM_OPENING)
3042             break;
3043         else if (state == STREAM_ERROR)
3044             {
3045             printf("ERROR\n");
3046             ins->reset();
3047             return false;
3048             }
3049         Thread::sleep(1000);
3050         state = ins->getState();
3051         }
3052     if (state != STREAM_OPENING)
3053         {
3054         printf("TIMEOUT ERROR\n");
3055         ins->reset();
3056         return false;
3057         }
3058     char *fmt =
3059     "<%s type='result' to='%s' id='%s'/>\n";
3060     if (!write(fmt, streamPacket.c_str(),
3061            fromJid.c_str(), ins->getMessageId().c_str()))
3062         {
3063         return false;
3064         }
3066     ins->setState(STREAM_OPEN);
3067     return true;
3072 /**
3073  *
3074  */
3075 bool XmppClient::inputStreamClose(const DOMString &streamId)
3077     std::map<DOMString, XmppStream *>::iterator iter =
3078         inputStreams.find(streamId);
3079     if (iter == inputStreams.end())
3080         return false;
3081     XmppStream *ins = iter->second;
3083     if (ins->getState() == STREAM_CLOSING)
3084         {
3085         char *fmt =
3086         "<iq type='result' to='%s' id='%s'/>\n";
3087         if (!write(fmt, ins->getPeerId().c_str(),
3088                     ins->getMessageId().c_str()))
3089             {
3090             return false;
3091             }
3092         }
3093     inputStreams.erase(streamId);
3094     delete ins;
3096     return true;
3104 //########################################################################
3105 //# FILE   TRANSFERS
3106 //########################################################################
3109 bool XmppClient::processFileMessage(Element *root)
3111     DOMString siNamespace = "http://jabber.org/protocol/si";
3112     if (root->getTagAttribute("si", "xmlns") != siNamespace)
3113         return false;
3115     
3116     Element *mainElement = root->getFirstChild();
3117     if (!mainElement)
3118         return false;
3120     DOMString from  = mainElement->getAttribute("from");
3121     DOMString id    = mainElement->getAttribute("id");
3122     DOMString type  = mainElement->getAttribute("type");
3124     status("received file message from %s", from.c_str());
3126     if (type == "set")
3127         {
3128         DOMString streamId = root->getTagAttribute("si", "id");
3129         DOMString fname    = root->getTagAttribute("file", "name");
3130         DOMString sizeStr  = root->getTagAttribute("file", "size");
3131         DOMString hash     = root->getTagAttribute("file", "hash");
3132         XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
3133         event.setFrom(from);
3134         event.setIqId(id);
3135         event.setStreamId(streamId);
3136         event.setFileName(fname);
3137         event.setFileHash(hash);
3138         event.setFileSize(atol(sizeStr.c_str()));
3139         dispatchXmppEvent(event);
3140         return true;
3141         }
3143     //##expecting result or error
3144     //file sends id'd by message id's
3145     std::map<DOMString, XmppStream *>::iterator iter =
3146         fileSends.find(id);
3147     if (iter != fileSends.end())
3148         {
3149         XmppStream *outf = iter->second;
3150         if (from != outf->getPeerId())
3151             return true;
3152         if (type == "error")
3153             {
3154             outf->setState(STREAM_ERROR);
3155             error("user '%s' rejected file", from.c_str());
3156             return true;
3157             }
3158         else if (type == "result")
3159             {
3160             if (outf->getState() == STREAM_OPENING)
3161                 {
3162                 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
3163                 event.setFrom(from);
3164                 dispatchXmppEvent(event);
3165                 outf->setState(STREAM_OPEN);
3166                 }
3167             else if (outf->getState() == STREAM_CLOSING)
3168                 {
3169                 outf->setState(STREAM_CLOSED);
3170                 }
3171             return true;
3172             }
3173         }
3175     return true;
3183 /**
3184  *
3185  */
3186 bool XmppClient::fileSend(const DOMString &destJidArg,
3187                           const DOMString &offeredNameArg,
3188                           const DOMString &fileNameArg,
3189                           const DOMString &descriptionArg)
3191     DOMString destJid     = destJidArg;
3192     DOMString offeredName = offeredNameArg;
3193     DOMString fileName    = fileNameArg;
3194     DOMString description = descriptionArg;
3196     struct stat finfo;
3197     if (stat(fileName.c_str(), &finfo)<0)
3198         {
3199         error("Cannot stat file '%s' for sending", fileName.c_str());
3200         return false;
3201         }
3202     long fileLen = finfo.st_size;
3203     if (!fileLen > 1000000)
3204         {
3205         error("'%s' too large", fileName.c_str());
3206         return false;
3207         }
3208     if (!S_ISREG(finfo.st_mode))
3209         {
3210         error("'%s' is not a regular file", fileName.c_str());
3211         return false;
3212         }
3213     FILE *f = fopen(fileName.c_str(), "rb");
3214     if (!f)
3215         {
3216         error("cannot open '%s' for sending", fileName.c_str());
3217         return false;
3218         }
3219     std::vector<unsigned char> sendBuf;
3220     Md5 md5hash;
3221     for (long i=0 ; i<fileLen && !feof(f); i++)
3222         {
3223         int ch = fgetc(f);
3224         if (ch<0)
3225             break;
3226         md5hash.append((unsigned char)ch);
3227         sendBuf.push_back((unsigned char)ch);
3228         }
3229     fclose(f);
3230     DOMString hash = md5hash.finishHex();
3231     printf("Hash:%s\n", hash.c_str());
3233    
3234     //## get the last path segment from the whole path
3235     if (offeredName.size()<1)
3236         {
3237         int slashPos = -1;
3238         for (unsigned int i=0 ; i<fileName.size() ; i++)
3239             {
3240             int ch = fileName[i];
3241             if (ch == '/' || ch == '\\')
3242                 slashPos = i;
3243             }
3244         if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3245             {
3246             offeredName = fileName.substr(slashPos+1,
3247                                           fileName.size()-slashPos-1);
3248             printf("offeredName:%s\n", offeredName.c_str());
3249             }
3250         }
3252     char buf[32];
3253     snprintf(buf, 31, "file%d", getMsgId());
3254     DOMString messageId = buf;
3256     XmppStream *outf = new XmppStream();
3258     outf->setState(STREAM_OPENING);
3259     outf->setMessageId(messageId);
3260     fileSends[messageId] = outf;
3262     snprintf(buf, 31, "stream%d", getMsgId());
3263     DOMString streamId = buf;
3264     //outf->setStreamId(streamId);
3266     outf->setPeerId(destJid);
3268     char dtgBuf[81];
3269     struct tm *timeVal = gmtime(&(finfo.st_mtime));
3270     strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3272     char *fmt =
3273     "<%s type='set' id='%s' to='%s'>"
3274     "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3275       " mime-type='text/plain'"
3276       " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3277     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3278           " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3279     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3280     "<x xmlns='jabber:x:data' type='form'>"
3281     "<field var='stream-method' type='list-single'>"
3282     //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3283     "<option><value>http://jabber.org/protocol/ibb</value></option>"
3284     "</field></x></feature></si></%s>\n";
3285     if (!write(fmt, streamPacket.c_str(),
3286          messageId.c_str(), destJid.c_str(),
3287          streamId.c_str(), offeredName.c_str(), fileLen,
3288          hash.c_str(), dtgBuf, description.c_str(),
3289          streamPacket.c_str()))
3290         {
3291         return false;
3292         }
3294     int ret = true;
3295     int state = outf->getState();
3296     for (int tim=0 ; tim<20 ; tim++)
3297         {
3298         printf("##### waiting for open\n");
3299         if (state == STREAM_OPEN)
3300             {
3301             outf->reset();
3302             break;
3303             }
3304         else if (state == STREAM_ERROR)
3305             {
3306             printf("ERROR\n");
3307             outf->reset();
3308             ret = false;
3309             }
3310         Thread::sleep(1000);
3311         state = outf->getState();
3312         }
3313     if (state != STREAM_OPEN)
3314         {
3315         printf("TIMEOUT ERROR\n");
3316         ret = false;
3317         }
3319     //free up this resource
3320     fileSends.erase(messageId);
3321     delete outf;
3323     if (!outputStreamOpen(destJid, streamId))
3324         {
3325         error("cannot open output stream %s", streamId.c_str());
3326         return false;
3327         }
3329     if (!outputStreamWrite(streamId, sendBuf))
3330         {
3331         }
3333     if (!outputStreamClose(streamId))
3334         {
3335         }
3337     return true;
3341 class FileSendThread : public Thread
3343 public:
3345     FileSendThread(XmppClient &par,
3346                    const DOMString &destJidArg,
3347                    const DOMString &offeredNameArg,
3348                    const DOMString &fileNameArg,
3349                    const DOMString &descriptionArg) : client(par)
3350         {
3351         destJid     = destJidArg;
3352         offeredName = offeredNameArg;
3353         fileName    = fileNameArg;
3354         description = descriptionArg;
3355         }
3357     virtual ~FileSendThread() {}
3359     void run()
3360       {
3361       client.fileSend(destJid, offeredName,
3362                       fileName, description);
3363       }
3365 private:
3367     XmppClient &client;
3368     DOMString destJid;
3369     DOMString offeredName;
3370     DOMString fileName;
3371     DOMString description;
3372 };
3374 /**
3375  *
3376  */
3377 bool XmppClient::fileSendBackground(const DOMString &destJid,
3378                                     const DOMString &offeredName,
3379                                     const DOMString &fileName,
3380                                     const DOMString &description)
3382     FileSendThread thread(*this, destJid, offeredName,
3383                            fileName, description);
3384     thread.start();
3385     return true;
3389 /**
3390  *
3391  */
3392 bool XmppClient::fileReceive(const DOMString &fromJid,
3393                              const DOMString &iqId,
3394                              const DOMString &streamId,
3395                              const DOMString &fileName,
3396                              long  fileSize,
3397                              const DOMString &fileHash)
3399     char *fmt =
3400     "<%s type='result' to='%s' id='%s'>"
3401     "<si xmlns='http://jabber.org/protocol/si'>"
3402     "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3403     "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3404     "<x xmlns='jabber:x:data' type='submit'>"
3405     "<field var='stream-method'>"
3406     "<value>http://jabber.org/protocol/ibb</value>"
3407     "</field></x></feature></si></%s>\n";
3408     if (!write(fmt, streamPacket.c_str(),
3409                     fromJid.c_str(), iqId.c_str(),
3410                     streamPacket.c_str()))
3411         {
3412         return false;
3413         }
3415     if (!inputStreamOpen(fromJid, streamId, iqId))
3416         {
3417         return false;
3418         }
3420     XmppStream *ins = inputStreams[streamId];
3422     Md5 md5;
3423     FILE *f = fopen(fileName.c_str(), "wb");
3424     if (!f)
3425         {
3426         return false;
3427         }
3429     while (true)
3430         {
3431         if (ins->available()<1)
3432             {
3433             if (ins->getState() == STREAM_CLOSING)
3434                 break;
3435             pause(100);
3436             continue;
3437             }
3438         std::vector<unsigned char> ret = ins->read();
3439         std::vector<unsigned char>::iterator iter;
3440         for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3441             {
3442             unsigned char ch = *iter;
3443             md5.append(&ch, 1);
3444             fwrite(&ch, 1, 1, f);
3445             }
3446         }
3448     inputStreamClose(streamId);
3449     fclose(f);
3451     DOMString hash = md5.finishHex();
3452     printf("received file hash:%s\n", hash.c_str());
3454     return true;
3459 class FileReceiveThread : public Thread
3461 public:
3463     FileReceiveThread(XmppClient &par,
3464                       const DOMString &fromJidArg,
3465                       const DOMString &iqIdArg,
3466                       const DOMString &streamIdArg,
3467                       const DOMString &fileNameArg,
3468                       long  fileSizeArg,
3469                       const DOMString &fileHashArg) : client(par)
3470         {
3471         fromJid     = fromJidArg;
3472         iqId        = iqIdArg;
3473         streamId    = streamIdArg;
3474         fileName    = fileNameArg;
3475         fileSize    = fileSizeArg;
3476         fileHash    = fileHashArg;
3477         }
3479     virtual ~FileReceiveThread() {}
3481     void run()
3482       {
3483       client.fileReceive(fromJid, iqId, streamId,
3484                         fileName, fileSize, fileHash);
3485       }
3487 private:
3489     XmppClient &client;
3490     DOMString fromJid;
3491     DOMString iqId;
3492     DOMString streamId;
3493     DOMString fileName;
3494     long      fileSize;
3495     DOMString fileHash;
3496 };
3498 /**
3499  *
3500  */
3501 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3502                                        const DOMString &iqId,
3503                                        const DOMString &streamId,
3504                                        const DOMString &fileName,
3505                                        long  fileSize,
3506                                        const DOMString &fileHash)
3508     FileReceiveThread thread(*this, fromJid, iqId, streamId,
3509                   fileName, fileSize, fileHash);
3510     thread.start();
3511     return true;
3516 //########################################################################
3517 //# X M P P    G R O U P    C H A T
3518 //########################################################################
3520 /**
3521  *
3522  */
3523 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3525     groupJid = groupJidArg;
3528 /**
3529  *
3530  */
3531 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3533     groupJid = other.groupJid;
3534     userList = other.userList;
3537 /**
3538  *
3539  */
3540 XmppGroupChat::~XmppGroupChat()
3545 /**
3546  *
3547  */
3548 DOMString XmppGroupChat::getGroupJid()
3550     return groupJid;
3554 void XmppGroupChat::userAdd(const DOMString &nick,
3555                             const DOMString &jid)
3557     std::vector<XmppUser>::iterator iter;
3558     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3559         {
3560         if (iter->nick == nick)
3561             return;
3562         }
3563     XmppUser user(jid, nick);
3564     userList.push_back(user);
3567 void XmppGroupChat::userShow(const DOMString &nick,
3568                              const DOMString &show)
3570     DOMString theShow = show;
3571     if (theShow == "")
3572         theShow = "available"; // a join message will now have a show
3573     std::vector<XmppUser>::iterator iter;
3574     for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3575         {
3576         if (iter->nick == nick)
3577             iter->show = theShow;
3578         }
3581 void XmppGroupChat::userDelete(const DOMString &nick)
3583     std::vector<XmppUser>::iterator iter;
3584     for (iter= userList.begin() ; iter!=userList.end() ; )
3585         {
3586         if (iter->nick == nick)
3587             iter = userList.erase(iter);
3588         else
3589             iter++;
3590         }
3593 std::vector<XmppUser> XmppGroupChat::getUserList() const
3595     return userList;
3606 } //namespace Pedro
3607 //########################################################################
3608 //# E N D    O F     F I L E
3609 //########################################################################