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