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();
414 /**
415 *
416 */
417 virtual ~XmppStream();
419 /**
420 *
421 */
422 virtual void reset();
424 /**
425 *
426 */
427 virtual int getState();
429 /**
430 *
431 */
432 virtual void setState(int val);
434 /**
435 *
436 */
437 virtual DOMString getStreamId();
439 /**
440 *
441 */
442 void setStreamId(const DOMString &val);
444 /**
445 *
446 */
447 virtual DOMString getIqId();
449 /**
450 *
451 */
452 void setIqId(const DOMString &val);
454 /**
455 *
456 */
457 virtual int getSeqNr();
459 /**
460 *
461 */
462 virtual DOMString getPeerId();
464 /**
465 *
466 */
467 virtual void setPeerId(const DOMString &val);
469 /**
470 *
471 */
472 int available();
474 /**
475 *
476 */
477 void receiveData(std::vector<unsigned char> &newData);
479 /**
480 *
481 */
482 std::vector<unsigned char> read();
484 private:
487 DOMString streamId;
489 DOMString iqId;
491 DOMString sourceId;
493 int state;
495 long seqNr;
497 std::vector<unsigned char> data;
498 };
501 /**
502 *
503 */
504 XmppStream::XmppStream()
505 {
506 reset();
507 }
509 /**
510 *
511 */
512 XmppStream::~XmppStream()
513 {
514 reset();
515 }
517 /**
518 *
519 */
520 void XmppStream::reset()
521 {
522 state = XmppClient::STREAM_AVAILABLE;
523 seqNr = 0;
524 data.clear();
525 }
527 /**
528 *
529 */
530 int XmppStream::getState()
531 {
532 return state;
533 }
535 /**
536 *
537 */
538 void XmppStream::setState(int val)
539 {
540 state = val;
541 }
543 /**
544 *
545 */
546 DOMString XmppStream::getStreamId()
547 {
548 return streamId;
549 }
551 /**
552 *
553 */
554 void XmppStream::setStreamId(const DOMString &val)
555 {
556 streamId = val;
557 }
559 /**
560 *
561 */
562 DOMString XmppStream::getIqId()
563 {
564 return iqId;
565 }
568 /**
569 *
570 */
571 void XmppStream::setIqId(const DOMString &val)
572 {
573 iqId = val;
574 }
576 /**
577 * Source or destination JID
578 */
579 void XmppStream::setPeerId(const DOMString &val)
580 {
581 sourceId = val;
582 }
584 /**
585 * Source or destination JID
586 */
587 DOMString XmppStream::getPeerId()
588 {
589 return sourceId;
590 }
592 /**
593 * Stream packet sequence number
594 */
595 int XmppStream::getSeqNr()
596 {
597 seqNr++;
598 if (seqNr >= 65535)
599 seqNr = 0;
600 return seqNr;
601 }
603 /**
604 *
605 */
606 int XmppStream::available()
607 {
608 return data.size();
609 }
611 /**
612 *
613 */
614 void XmppStream::receiveData(std::vector<unsigned char> &newData)
615 {
616 std::vector<unsigned char>::iterator iter;
617 for (iter=newData.begin() ; iter!=newData.end() ; iter++)
618 data.push_back(*iter);
619 }
621 /**
622 *
623 */
624 std::vector<unsigned char> XmppStream::read()
625 {
626 if (state != XmppClient::STREAM_OPEN)
627 {
628 std::vector<unsigned char>dummy;
629 return dummy;
630 }
631 std::vector<unsigned char> ret = data;
632 data.clear();
633 return ret;
634 }
641 //########################################################################
642 //########################################################################
643 //# X M P P C L I E N T
644 //########################################################################
645 //########################################################################
647 class ReceiverThread : public Runnable
648 {
649 public:
651 ReceiverThread(XmppClient &par) : client(par) {}
653 virtual ~ReceiverThread() {}
655 void run()
656 { client.receiveAndProcessLoop(); }
658 private:
660 XmppClient &client;
661 };
667 //########################################################################
668 //# CONSTRUCTORS
669 //########################################################################
671 XmppClient::XmppClient()
672 {
673 init();
674 }
677 XmppClient::XmppClient(const XmppClient &other) : XmppEventTarget(other)
678 {
679 init();
680 assign(other);
681 }
683 void XmppClient::assign(const XmppClient &other)
684 {
685 msgId = other.msgId;
686 host = other.host;
687 realm = other.realm;
688 port = other.port;
689 username = other.username;
690 password = other.password;
691 resource = other.resource;
692 connected = other.connected;
693 doRegister = other.doRegister;
694 groupChats = other.groupChats;
695 }
698 void XmppClient::init()
699 {
700 sock = new TcpSocket();
701 msgId = 0;
702 connected = false;
703 doRegister = false;
705 for (int i=0 ; i<outputStreamCount ; i++)
706 {
707 outputStreams[i] = new XmppStream();
708 }
709 for (int i=0 ; i<inputStreamCount ; i++)
710 {
711 inputStreams[i] = new XmppStream();
712 }
713 for (int i=0 ; i<fileSendCount ; i++)
714 {
715 fileSends[i] = new XmppStream();
716 }
717 }
719 XmppClient::~XmppClient()
720 {
721 disconnect();
722 delete sock;
723 for (int i=0 ; i<outputStreamCount ; i++)
724 {
725 delete outputStreams[i];
726 }
727 for (int i=0 ; i<inputStreamCount ; i++)
728 {
729 delete inputStreams[i];
730 }
731 for (int i=0 ; i<fileSendCount ; i++)
732 {
733 delete fileSends[i];
734 }
735 groupChatsClear();
736 }
743 //########################################################################
744 //# UTILILY
745 //########################################################################
747 /**
748 *
749 */
750 bool XmppClient::pause(unsigned long millis)
751 {
752 Thread::sleep(millis);
753 return true;
754 }
757 static int strIndex(const DOMString &str, char *key)
758 {
759 unsigned int p = str.find(key);
760 if (p == str.npos)
761 return -1;
762 return p;
763 }
767 DOMString XmppClient::toXml(const DOMString &str)
768 {
769 return Parser::encode(str);
770 }
774 static DOMString trim(const DOMString &str)
775 {
776 unsigned int i;
777 for (i=0 ; i<str.size() ; i++)
778 if (!isspace(str[i]))
779 break;
780 int start = i;
781 for (i=str.size() ; i>0 ; i--)
782 if (!isspace(str[i-1]))
783 break;
784 int end = i;
785 if (start>=end)
786 return "";
787 return str.substr(start, end);
788 }
794 //########################################################################
795 //# VARIABLES (ones that need special handling)
796 //########################################################################
798 /**
799 *
800 */
801 DOMString XmppClient::getUsername()
802 {
803 return username;
804 }
806 /**
807 *
808 */
809 void XmppClient::setUsername(const DOMString &val)
810 {
811 int p = strIndex(val, "@");
812 if (p > 0)
813 {
814 username = val.substr(0, p);
815 realm = val.substr(p+1, jid.size()-p-1);
816 }
817 else
818 {
819 realm = host;
820 username = val;
821 }
822 }
831 //########################################################################
832 //# RECEIVING
833 //########################################################################
836 DOMString XmppClient::readStanza()
837 {
839 int openCount = 0;
840 bool inTag = false;
841 bool slashSeen = false;
842 bool trivialTag = false;
843 bool querySeen = false;
844 bool inQuote = false;
845 bool textSeen = false;
846 DOMString buf;
849 time_t timeout = time((time_t *)0) + 180;
851 while (true)
852 {
853 int ch = sock->read();
854 //printf("%c", ch); fflush(stdout);
855 if (ch<0)
856 {
857 if (ch == -2) //a simple timeout, not an error
858 {
859 //Since we are timed out, let's assume that we
860 //are between chunks of text. Let's reset all states.
861 //printf("-----#### Timeout\n");
862 time_t currentTime = time((time_t *)0);
863 if (currentTime > timeout)
864 {
865 timeout = currentTime + 180;
866 if (!write("\n"))
867 {
868 error("ping send error");
869 disconnect();
870 return "";
871 }
872 }
873 continue;
874 }
875 else
876 {
877 keepGoing = false;
878 if (!sock->isConnected())
879 {
880 disconnect();
881 return "";
882 }
883 else
884 {
885 error("socket read error");
886 disconnect();
887 return "";
888 }
889 }
890 }
891 buf.push_back(ch);
892 if (ch == '<')
893 {
894 inTag = true;
895 slashSeen = false;
896 querySeen = false;
897 inQuote = false;
898 textSeen = false;
899 trivialTag = false;
900 }
901 else if (ch == '>')
902 {
903 if (!inTag) //unescaped '>' in pcdata? horror
904 continue;
905 inTag = false;
906 if (!trivialTag && !querySeen)
907 {
908 if (slashSeen)
909 openCount--;
910 else
911 openCount++;
912 }
913 //printf("# openCount:%d t:%d q:%d\n",
914 // openCount, trivialTag, querySeen);
915 //check if we are 'balanced', but not a <?version?> tag
916 if (openCount <= 0 && !querySeen)
917 {
918 break;
919 }
920 //we know that this one will be open-ended
921 if (strIndex(buf, "<stream:stream") >= 0)
922 {
923 buf.append("</stream:stream>");
924 break;
925 }
926 }
927 else if (ch == '/')
928 {
929 if (inTag && !inQuote)
930 {
931 slashSeen = true;
932 if (textSeen) // <tagName/> <--looks like this
933 trivialTag = true;
934 }
935 }
936 else if (ch == '?')
937 {
938 if (inTag && !inQuote)
939 querySeen = true;
940 }
941 else if (ch == '"' || ch == '\'')
942 {
943 if (inTag)
944 inQuote = !inQuote;
945 }
946 else
947 {
948 if (inTag && !inQuote && !isspace(ch))
949 textSeen = true;
950 }
951 }
952 return buf;
953 }
957 static bool isGroupChat(Element *root)
958 {
959 if (!root)
960 return false;
961 std::vector<Element *>elems = root->findElements("x");
962 for (unsigned int i=0 ; i<elems.size() ; i++)
963 {
964 DOMString xmlns = elems[i]->getAttribute("xmlns");
965 //printf("### XMLNS ### %s\n", xmlns.c_str());
966 if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
967 return true;
968 }
969 return false;
970 }
975 static bool parseJid(const DOMString &fullJid,
976 DOMString &jid, DOMString &resource)
977 {
978 int p = strIndex(fullJid, "/");
979 if (p < 0)
980 {
981 jid = fullJid;
982 resource = "";
983 return true;
984 }
985 jid = fullJid.substr(0, p);
986 resource = fullJid.substr(p+1, fullJid.size()-p-1);
987 return true;
988 }
993 bool XmppClient::processMessage(Element *root)
994 {
995 DOMString from = root->getTagAttribute("message", "from");
996 DOMString to = root->getTagAttribute("message", "to");
997 DOMString type = root->getTagAttribute("message", "type");
999 //####Check for embedded namespaces here
1000 //# IN BAND BYTESTREAMS
1001 DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
1002 if (root->getTagAttribute("data", "xmlns") == ibbNamespace)
1003 {
1004 DOMString streamId = root->getTagAttribute("data", "sid");
1005 if (streamId.size() > 0)
1006 {
1007 for (int i=0 ; i<inputStreamCount ; i++)
1008 {
1009 XmppStream *ins = inputStreams[i];
1010 //printf("##ins:%s streamid:%s\n",
1011 // ins->getStreamId().c_str(), streamId.c_str());
1012 if (ins->getStreamId() == streamId)
1013 {
1014 //# We have a winner
1015 if (ins->getState() != STREAM_OPEN)
1016 {
1017 XmppEvent event(XmppEvent::EVENT_ERROR);
1018 event.setFrom(from);
1019 event.setData("received unrequested stream data");
1020 dispatchXmppEvent(event);
1021 return true;
1022 }
1023 DOMString data = root->getTagValue("data");
1024 std::vector<unsigned char>binData =
1025 Base64Decoder::decode(data);
1026 ins->receiveData(binData);
1027 }
1028 }
1029 }
1030 }
1033 //#### NORMAL MESSAGES
1034 DOMString subject = root->getTagValue("subject");
1035 DOMString body = root->getTagValue("body");
1036 DOMString thread = root->getTagValue("thread");
1037 //##rfc 3921, para 2.4. ignore if no recognizable info
1038 //if (subject.size() < 1 && thread.size()<1)
1039 // return true;
1041 if (type == "groupchat")
1042 {
1043 DOMString fromGid;
1044 DOMString fromNick;
1045 parseJid(from, fromGid, fromNick);
1046 //printf("fromGid:%s fromNick:%s\n",
1047 // fromGid.c_str(), fromNick.c_str());
1048 DOMString toGid;
1049 DOMString toNick;
1050 parseJid(to, toGid, toNick);
1051 //printf("toGid:%s toNick:%s\n",
1052 // toGid.c_str(), toNick.c_str());
1054 if (fromNick.size() > 0)//normal group message
1055 {
1056 XmppEvent event(XmppEvent::EVENT_MUC_MESSAGE);
1057 event.setGroup(fromGid);
1058 event.setFrom(fromNick);
1059 event.setData(body);
1060 event.setDOM(root);
1061 dispatchXmppEvent(event);
1062 }
1063 else // from the server itself
1064 {
1065 //note the space before, so it doesnt match 'unlocked'
1066 if (strIndex(body, " locked") >= 0)
1067 {
1068 printf("LOCKED!! ;)\n");
1069 char *fmt =
1070 "<iq from='%s' id='create%d' to='%s' type='set'>"
1071 "<query xmlns='http://jabber.org/protocol/muc#owner'>"
1072 "<x xmlns='jabber:x:data' type='submit'/>"
1073 "</query></iq>\n";
1074 if (!write(fmt, jid.c_str(), msgId++, fromGid.c_str()))
1075 return false;
1076 }
1077 }
1078 }
1079 else
1080 {
1081 XmppEvent event(XmppEvent::EVENT_MESSAGE);
1082 event.setFrom(from);
1083 event.setData(body);
1084 event.setDOM(root);
1085 dispatchXmppEvent(event);
1086 }
1088 return true;
1089 }
1094 bool XmppClient::processPresence(Element *root)
1095 {
1097 DOMString fullJid = root->getTagAttribute("presence", "from");
1098 DOMString to = root->getTagAttribute("presence", "to");
1099 DOMString presenceStr = root->getTagAttribute("presence", "type");
1100 bool presence = true;
1101 if (presenceStr == "unavailable")
1102 presence = false;
1103 DOMString status = root->getTagValue("status");
1104 DOMString show = root->getTagValue("show");
1106 if (isGroupChat(root))
1107 {
1108 DOMString fromGid;
1109 DOMString fromNick;
1110 parseJid(fullJid, fromGid, fromNick);
1111 //printf("fromGid:%s fromNick:%s\n",
1112 // fromGid.c_str(), fromNick.c_str());
1113 DOMString item_jid = root->getTagAttribute("item", "jid");
1114 if (item_jid == jid) //Me
1115 {
1116 if (presence)
1117 {
1118 groupChatCreate(fromGid);
1119 groupChatUserAdd(fromGid, fromNick, "");
1120 groupChatUserShow(fromGid, fromNick, "available");
1122 XmppEvent event(XmppEvent::EVENT_MUC_JOIN);
1123 event.setGroup(fromGid);
1124 event.setFrom(fromNick);
1125 event.setPresence(presence);
1126 event.setShow(show);
1127 event.setStatus(status);
1128 dispatchXmppEvent(event);
1129 }
1130 else
1131 {
1132 groupChatDelete(fromGid);
1133 groupChatUserDelete(fromGid, fromNick);
1135 XmppEvent event(XmppEvent::EVENT_MUC_LEAVE);
1136 event.setGroup(fromGid);
1137 event.setFrom(fromNick);
1138 event.setPresence(presence);
1139 event.setShow(show);
1140 event.setStatus(status);
1141 dispatchXmppEvent(event);
1142 }
1143 }
1144 else // someone else
1145 {
1146 if (presence)
1147 {
1148 groupChatUserAdd(fromGid, fromNick, "");
1149 }
1150 else
1151 groupChatUserDelete(fromGid, fromNick);
1152 groupChatUserShow(fromGid, fromNick, show);
1153 XmppEvent event(XmppEvent::EVENT_MUC_PRESENCE);
1154 event.setGroup(fromGid);
1155 event.setFrom(fromNick);
1156 event.setPresence(presence);
1157 event.setShow(show);
1158 event.setStatus(status);
1159 dispatchXmppEvent(event);
1160 }
1161 }
1162 else
1163 {
1164 DOMString shortJid;
1165 DOMString dummy;
1166 parseJid(fullJid, shortJid, dummy);
1167 rosterShow(shortJid, show); //users in roster do not have resource
1169 XmppEvent event(XmppEvent::EVENT_PRESENCE);
1170 event.setFrom(fullJid);
1171 event.setPresence(presence);
1172 event.setShow(show);
1173 event.setStatus(status);
1174 dispatchXmppEvent(event);
1175 }
1177 return true;
1178 }
1182 bool XmppClient::processIq(Element *root)
1183 {
1184 DOMString from = root->getTagAttribute("iq", "from");
1185 DOMString id = root->getTagAttribute("iq", "id");
1186 DOMString type = root->getTagAttribute("iq", "type");
1187 DOMString xmlns = root->getTagAttribute("query", "xmlns");
1189 if (id.size()<1)
1190 return true;
1192 //Group chat
1193 if (strIndex(xmlns, "http://jabber.org/protocol/muc") >=0 )
1194 {
1195 printf("results of MUC query\n");
1196 }
1197 //printf("###IQ xmlns:%s\n", xmlns.c_str());
1199 //### FILE TRANSFERS
1200 DOMString siNamespace = "http://jabber.org/protocol/si";
1201 if (root->getTagAttribute("si", "xmlns") == siNamespace)
1202 {
1203 if (type == "set")
1204 {
1205 DOMString streamId = root->getTagAttribute("si", "id");
1206 DOMString fname = root->getTagAttribute("file", "name");
1207 DOMString sizeStr = root->getTagAttribute("file", "size");
1208 DOMString hash = root->getTagAttribute("file", "hash");
1209 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_RECEIVE);
1210 event.setFrom(from);
1211 event.setIqId(id);
1212 event.setStreamId(streamId);
1213 event.setFileName(fname);
1214 event.setFileHash(hash);
1215 event.setFileSize(atol(sizeStr.c_str()));
1216 dispatchXmppEvent(event);
1217 }
1218 else //result
1219 {
1220 //printf("Got result\n");
1221 for (int i=0 ; i<fileSendCount ; i++)
1222 {
1223 XmppStream *outf = fileSends[i];
1224 if (outf->getIqId() == id &&
1225 from == outf->getPeerId())
1226 {
1227 if (type == "error")
1228 {
1229 outf->setState(STREAM_ERROR);
1230 error("user '%s' rejected file", from.c_str());
1231 return true;
1232 }
1233 else if (type == "result")
1234 {
1235 if (outf->getState() == STREAM_OPENING)
1236 {
1237 XmppEvent event(XmppEvent::XmppEvent::EVENT_FILE_ACCEPTED);
1238 event.setFrom(from);
1239 dispatchXmppEvent(event);
1240 outf->setState(STREAM_OPEN);
1241 }
1242 else if (outf->getState() == STREAM_CLOSING)
1243 {
1244 outf->setState(STREAM_CLOSED);
1245 }
1246 return true;
1247 }
1248 }
1249 }
1250 }
1251 return true;
1252 }
1255 //### IN-BAND BYTESTREAMS
1256 //### Incoming stream requests
1257 DOMString ibbNamespace = "http://jabber.org/protocol/ibb";
1258 if (root->getTagAttribute("open", "xmlns") == ibbNamespace)
1259 {
1260 DOMString streamId = root->getTagAttribute("open", "sid");
1261 XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_INIT);
1262 dispatchXmppEvent(event);
1263 if (streamId.size()>0)
1264 {
1265 for (int i=0 ; i<inputStreamCount ; i++)
1266 {
1267 XmppStream *ins = inputStreams[i];
1268 if (ins->getStreamId() == streamId)
1269 {
1270 ins->setState(STREAM_OPENING);
1271 ins->setIqId(id);
1272 return true;
1273 }
1274 }
1275 }
1276 return true;
1277 }
1278 else if (root->getTagAttribute("close", "xmlns") == ibbNamespace)
1279 {
1280 XmppEvent event(XmppEvent::XmppEvent::EVENT_STREAM_RECEIVE_CLOSE);
1281 dispatchXmppEvent(event);
1282 DOMString streamId = root->getTagAttribute("close", "sid");
1283 if (streamId.size()>0)
1284 {
1285 for (int i=0 ; i<inputStreamCount ; i++)
1286 {
1287 XmppStream *ins = inputStreams[i];
1288 if (ins->getStreamId() == streamId &&
1289 from == ins->getPeerId())
1290 {
1291 ins->setState(STREAM_CLOSING);
1292 ins->setIqId(id);
1293 return true;
1294 }
1295 }
1296 }
1297 return true;
1298 }
1299 //### Responses to outgoing requests
1300 for (int i=0 ; i<outputStreamCount ; i++)
1301 {
1302 XmppStream *outs = outputStreams[i];
1303 if (outs->getIqId() == id)
1304 {
1305 if (type == "error")
1306 {
1307 outs->setState(STREAM_ERROR);
1308 return true;
1309 }
1310 else if (type == "result")
1311 {
1312 if (outs->getState() == STREAM_OPENING)
1313 {
1314 outs->setState(STREAM_OPEN);
1315 }
1316 else if (outs->getState() == STREAM_CLOSING)
1317 {
1318 outs->setState(STREAM_CLOSED);
1319 }
1320 return true;
1321 }
1322 }
1323 }
1325 //###Normal Roster stuff
1326 if (root->getTagAttribute("query", "xmlns") == "jabber:iq:roster")
1327 {
1328 roster.clear();
1329 std::vector<Element *>elems = root->findElements("item");
1330 for (unsigned int i=0 ; i<elems.size() ; i++)
1331 {
1332 Element *item = elems[i];
1333 DOMString userJid = item->getAttribute("jid");
1334 DOMString name = item->getAttribute("name");
1335 DOMString subscription = item->getAttribute("subscription");
1336 DOMString group = item->getTagValue("group");
1337 //printf("jid:%s name:%s sub:%s group:%s\n", userJid.c_str(), name.c_str(),
1338 // subscription.c_str(), group.c_str());
1339 XmppUser user(userJid, name, subscription, group);
1340 roster.push_back(user);
1341 }
1342 XmppEvent event(XmppEvent::XmppEvent::EVENT_ROSTER);
1343 dispatchXmppEvent(event);
1344 }
1346 else if (id.find("regnew") != id.npos)
1347 {
1349 }
1351 else if (id.find("regpass") != id.npos)
1352 {
1353 std::vector<Element *> list = root->findElements("error");
1354 if (list.size()==0)
1355 {
1356 XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CHANGE_PASS);
1357 evt.setTo(username);
1358 evt.setFrom(host);
1359 dispatchXmppEvent(evt);
1360 return true;
1361 }
1363 Element *errElem = list[0];
1364 DOMString errMsg = "Password change error: ";
1365 if (errElem->findElements("bad-request").size()>0)
1366 {
1367 errMsg.append("password change does not contain complete information");
1368 }
1369 else if (errElem->findElements("not-authorized").size()>0)
1370 {
1371 errMsg.append("server does not consider the channel safe "
1372 "enough to enable a password change");
1373 }
1374 else if (errElem->findElements("not-allowed").size()>0)
1375 {
1376 errMsg.append("server does not allow password changes");
1377 }
1378 else if (errElem->findElements("unexpected-request").size()>0)
1379 {
1380 errMsg.append(
1381 "IQ set does not contain a 'from' address because "
1382 "the entity is not registered with the server");
1383 }
1384 error((char *)errMsg.c_str());
1385 }
1387 else if (id.find("regcancel") != id.npos)
1388 {
1389 std::vector<Element *> list = root->findElements("error");
1390 if (list.size()==0)
1391 {
1392 XmppEvent evt(XmppEvent::EVENT_REGISTRATION_CANCEL);
1393 evt.setTo(username);
1394 evt.setFrom(host);
1395 dispatchXmppEvent(evt);
1396 return true;
1397 }
1399 Element *errElem = list[0];
1400 DOMString errMsg = "Registration cancel error: ";
1401 if (errElem->findElements("bad-request").size()>0)
1402 {
1403 errMsg.append("The <remove/> element was not the only child element of the <query/> element.");
1404 }
1405 else if (errElem->findElements("forbidden").size()>0)
1406 {
1407 errMsg.append("sender does not have sufficient permissions to cancel the registration");
1408 }
1409 else if (errElem->findElements("not-allowed").size()>0)
1410 {
1411 errMsg.append("not allowed to cancel registrations in-band");
1412 }
1413 else if (errElem->findElements("registration-required").size()>0)
1414 {
1415 errMsg.append("not previously registered");
1416 }
1417 else if (errElem->findElements("unexpected-request").size()>0)
1418 {
1419 errMsg.append(
1420 "IQ set does not contain a 'from' address because "
1421 "the entity is not registered with the server");
1422 }
1423 error((char *)errMsg.c_str());
1424 }
1426 return true;
1427 }
1431 bool XmppClient::receiveAndProcess()
1432 {
1433 if (!keepGoing)
1434 return false;
1436 Parser parser;
1438 DOMString recvBuf = readStanza();
1439 recvBuf = trim(recvBuf);
1440 if (recvBuf.size() < 1)
1441 return true;
1443 //Ugly hack. Apparently the first char can be dropped on timeouts
1444 //if (recvBuf[0] != '<')
1445 // recvBuf.insert(0, "<");
1447 status("RECV: %s", recvBuf.c_str());
1448 Element *root = parser.parse(recvBuf);
1449 if (!root)
1450 {
1451 printf("Bad elem\n");
1452 return true;
1453 }
1455 //#### MESSAGE
1456 std::vector<Element *>elems = root->findElements("message");
1457 if (elems.size()>0)
1458 {
1459 if (!processMessage(root))
1460 return false;
1461 }
1463 //#### PRESENCE
1464 elems = root->findElements("presence");
1465 if (elems.size()>0)
1466 {
1467 if (!processPresence(root))
1468 return false;
1469 }
1471 //#### INFO
1472 elems = root->findElements("iq");
1473 if (elems.size()>0)
1474 {
1475 if (!processIq(root))
1476 return false;
1477 }
1479 delete root;
1481 return true;
1482 }
1485 bool XmppClient::receiveAndProcessLoop()
1486 {
1487 keepGoing = true;
1488 while (true)
1489 {
1490 if (!keepGoing)
1491 {
1492 status("Abort requested");
1493 break;
1494 }
1495 if (!receiveAndProcess())
1496 return false;
1497 }
1498 return true;
1499 }
1504 //########################################################################
1505 //# SENDING
1506 //########################################################################
1509 bool XmppClient::write(char *fmt, ...)
1510 {
1511 va_list args;
1512 va_start(args,fmt);
1513 vsnprintf((char *)writeBuf, writeBufLen, fmt,args);
1514 va_end(args) ;
1515 status("SEND: %s", writeBuf);
1516 if (!sock->write((char *)writeBuf))
1517 {
1518 error("Cannot write to socket");
1519 return false;
1520 }
1521 return true;
1522 }
1529 //########################################################################
1530 //# R E G I S T R A T I O N
1531 //########################################################################
1533 /**
1534 * Perform JEP-077 In-Band Registration. Performed synchronously after SSL,
1535 * before authentication
1536 */
1537 bool XmppClient::inBandRegistrationNew()
1538 {
1539 Parser parser;
1541 char *fmt =
1542 "<iq type='get' id='regnew%d'>"
1543 "<query xmlns='jabber:iq:register'/>"
1544 "</iq>\n\n";
1545 if (!write(fmt, msgId++))
1546 return false;
1548 DOMString recbuf = readStanza();
1549 status("RECV reg: %s", recbuf.c_str());
1550 Element *elem = parser.parse(recbuf);
1551 //elem->print();
1553 //# does the entity send the newer "instructions" tag?
1554 std::vector<Element *> fields = elem->findElements("field");
1555 std::vector<DOMString> fnames;
1556 for (unsigned int i=0; i<fields.size() ; i++)
1557 {
1558 DOMString fname = fields[i]->getAttribute("var");
1559 if (fname == "FORM_TYPE")
1560 continue;
1561 fnames.push_back(fname);
1562 status("field name:%s", fname.c_str());
1563 }
1565 //Do we have any fields?
1566 if (fnames.size() == 0)
1567 {
1568 //If no fields, maybe the older method was offered
1569 if (elem->findElements("username").size() == 0 ||
1570 elem->findElements("password").size() == 0)
1571 {
1572 error("server did not offer registration");
1573 delete elem;
1574 return false;
1575 }
1576 }
1578 delete elem;
1580 fmt =
1581 "<iq type='set' id='regnew%d'>"
1582 "<query xmlns='jabber:iq:register'>"
1583 "<username>%s</username>"
1584 "<password>%s</password>"
1585 "<email/><name/>"
1586 "</query>"
1587 "</iq>\n\n";
1588 if (!write(fmt, msgId++, toXml(username).c_str(),
1589 toXml(password).c_str() ))
1590 return false;
1593 recbuf = readStanza();
1594 status("RECV reg: %s", recbuf.c_str());
1595 elem = parser.parse(recbuf);
1596 //elem->print();
1598 std::vector<Element *> list = elem->findElements("error");
1599 if (list.size()>0)
1600 {
1601 Element *errElem = list[0];
1602 DOMString code = errElem->getAttribute("code");
1603 DOMString errMsg = "Registration error: ";
1604 if (code == "409")
1605 {
1606 errMsg.append("conflict with existing user name");
1607 }
1608 else if (code == "406")
1609 {
1610 errMsg.append("some registration information was not provided");
1611 }
1612 error((char *)errMsg.c_str());
1613 delete elem;
1614 return false;
1615 }
1617 delete elem;
1619 XmppEvent evt(XmppEvent::EVENT_REGISTRATION_NEW);
1620 evt.setTo(username);
1621 evt.setFrom(host);
1622 dispatchXmppEvent(evt);
1624 return true;
1625 }
1628 /**
1629 * Perform JEP-077 In-Band Registration. Performed asynchronously, after login.
1630 * See processIq() for response handling.
1631 */
1632 bool XmppClient::inBandRegistrationChangePassword(const DOMString &newpassword)
1633 {
1634 Parser parser;
1636 //# Let's try it form-style to allow the common old/new password thing
1637 char *fmt =
1638 "<iq type='set' id='regpass%d' from='%s' to='%s'>"
1639 " <query xmlns='jabber:iq:register'>"
1640 " <x xmlns='jabber:x:data' type='form'>"
1641 " <field type='hidden' var='FORM_TYPE'>"
1642 " <value>jabber:iq:register:changepassword</value>"
1643 " </field>"
1644 " <field type='text-single' var='username'>"
1645 " <value>%s</value>"
1646 " </field>"
1647 " <field type='text-private' var='old_password'>"
1648 " <value>%s</value>"
1649 " </field>"
1650 " <field type='text-private' var='password'>"
1651 " <value>%s</value>"
1652 " </field>"
1653 " </x>"
1654 " </query>"
1655 "</iq>\n\n";
1657 if (!write(fmt, msgId++, jid.c_str(), host.c_str(),
1658 username.c_str(), password.c_str(), newpassword.c_str()))
1659 return false;
1661 return true;
1663 }
1666 /**
1667 * Perform JEP-077 In-Band Registration. Performed asynchronously, after login.
1668 * See processIq() for response handling.
1669 */
1670 bool XmppClient::inBandRegistrationCancel()
1671 {
1672 Parser parser;
1674 char *fmt =
1675 "<iq type='set' id='regcancel%d' from='%s'>"
1676 "<query xmlns='jabber:iq:register'><remove/></query>"
1677 "</iq>\n\n";
1678 if (!write(fmt, msgId++, jid.c_str()))
1679 return false;
1681 return true;
1682 }
1688 //########################################################################
1689 //# A U T H E N T I C A T E
1690 //########################################################################
1692 bool XmppClient::iqAuthenticate(const DOMString &streamId)
1693 {
1694 Parser parser;
1696 char *fmt =
1697 "<iq type='get' to='%s' id='auth%d'>"
1698 "<query xmlns='jabber:iq:auth'><username>%s</username></query>"
1699 "</iq>\n";
1700 if (!write(fmt, realm.c_str(), msgId++, username.c_str()))
1701 return false;
1703 DOMString recbuf = readStanza();
1704 //printf("iq received: '%s'\n", recbuf.c_str());
1705 Element *elem = parser.parse(recbuf);
1706 //elem->print();
1707 DOMString iqType = elem->getTagAttribute("iq", "type");
1708 //printf("##iqType:%s\n", iqType.c_str());
1709 delete elem;
1711 if (iqType != "result")
1712 {
1713 error("error:server does not allow login");
1714 return false;
1715 }
1717 bool digest = true;
1718 if (digest)
1719 {
1720 //## Digest authentication
1721 DOMString digest = streamId;
1722 digest.append(password);
1723 digest = Sha1::hashHex((unsigned char *)digest.c_str(), digest.size());
1724 //printf("digest:%s\n", digest.c_str());
1725 fmt =
1726 "<iq type='set' id='auth%d'>"
1727 "<query xmlns='jabber:iq:auth'>"
1728 "<username>%s</username>"
1729 "<digest>%s</digest>"
1730 "<resource>%s</resource>"
1731 "</query>"
1732 "</iq>\n";
1733 if (!write(fmt, msgId++, username.c_str(),
1734 digest.c_str(), resource.c_str()))
1735 return false;
1736 }
1737 else
1738 {
1740 //## Plaintext authentication
1741 fmt =
1742 "<iq type='set' id='auth%d'>"
1743 "<query xmlns='jabber:iq:auth'>"
1744 "<username>%s</username>"
1745 "<password>%s</password>"
1746 "<resource>%s</resource>"
1747 "</query>"
1748 "</iq>\n";
1749 if (!write(fmt, msgId++, username.c_str(),
1750 password.c_str(), resource.c_str()))
1751 return false;
1752 }
1754 recbuf = readStanza();
1755 //printf("iq received: '%s'\n", recbuf.c_str());
1756 elem = parser.parse(recbuf);
1757 //elem->print();
1758 iqType = elem->getTagAttribute("iq", "type");
1759 //printf("##iqType:%s\n", iqType.c_str());
1760 delete elem;
1762 if (iqType != "result")
1763 {
1764 error("server does not allow login");
1765 return false;
1766 }
1768 return true;
1769 }
1772 /**
1773 * Parse a sasl challenge to retrieve all of its key=value pairs
1774 */
1775 static bool saslParse(const DOMString &s,
1776 std::map<DOMString, DOMString> &vals)
1777 {
1779 vals.clear();
1781 int p = 0;
1782 int siz = s.size();
1784 while (p < siz)
1785 {
1786 DOMString key;
1787 DOMString value;
1788 char ch = '\0';
1790 //# Parse key
1791 while (p<siz)
1792 {
1793 ch = s[p++];
1794 if (ch == '=')
1795 break;
1796 key.push_back(ch);
1797 }
1799 //No value?
1800 if (ch != '=')
1801 break;
1803 //# Parse value
1804 bool quoted = false;
1805 while (p<siz)
1806 {
1807 ch = s[p++];
1808 if (ch == '"')
1809 quoted = !quoted;
1810 else if (ch == ',' && !quoted)
1811 break;
1812 else
1813 value.push_back(ch);
1814 }
1816 //printf("# Key: '%s' Value: '%s'\n", key.c_str(), value.c_str());
1817 vals[key] = value;
1818 if (ch != ',')
1819 break;
1820 }
1822 return true;
1823 }
1827 /**
1828 * Attempt suthentication using the MD5 SASL mechanism
1829 */
1830 bool XmppClient::saslMd5Authenticate()
1831 {
1832 Parser parser;
1833 char *fmt =
1834 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
1835 "mechanism='DIGEST-MD5'/>\n";
1836 if (!write(fmt))
1837 return false;
1839 DOMString recbuf = readStanza();
1840 status("challenge received: '%s'", recbuf.c_str());
1841 Element *elem = parser.parse(recbuf);
1842 //elem->print();
1843 DOMString b64challenge = elem->getTagValue("challenge");
1844 delete elem;
1846 if (b64challenge.size() < 1)
1847 {
1848 error("login: no SASL challenge offered by server");
1849 return false;
1850 }
1851 DOMString challenge = Base64Decoder::decodeToString(b64challenge);
1852 status("md5 challenge:'%s'", challenge.c_str());
1854 std::map<DOMString, DOMString> attrs;
1855 if (!saslParse(challenge, attrs))
1856 {
1857 error("login: error parsing SASL challenge");
1858 return false;
1859 }
1861 DOMString nonce = attrs["nonce"];
1862 if (nonce.size()==0)
1863 {
1864 error("login: no SASL nonce sent by server");
1865 return false;
1866 }
1868 DOMString realm = attrs["realm"];
1869 if (realm.size()==0)
1870 {
1871 //Apparently this is not a problem
1872 //error("login: no SASL realm sent by server");
1873 //return false;
1874 }
1876 status("SASL recv nonce: '%s' realm:'%s'\n", nonce.c_str(), realm.c_str());
1878 char idBuf[10];
1879 snprintf(idBuf, 9, "%dsasl", msgId++);
1880 DOMString cnonce = Sha1::hashHex((unsigned char *)idBuf, 7);
1881 DOMString authzid = username; authzid.append("@"); authzid.append(host);
1882 DOMString digest_uri = "xmpp/"; digest_uri.append(host);
1884 //## Make A1
1885 Md5 md5;
1886 md5.append(username);
1887 md5.append(":");
1888 md5.append(realm);
1889 md5.append(":");
1890 md5.append(password);
1891 unsigned char a1tmp[16];
1892 md5.finish(a1tmp);
1893 md5.init();
1894 md5.append(a1tmp, 16);
1895 md5.append(":");
1896 md5.append(nonce);
1897 md5.append(":");
1898 md5.append(cnonce);
1899 //RFC2831 says authzid is optional. Wildfire has trouble with authzid's
1900 //md5.append(":");
1901 //md5.append(authzid);
1902 md5.append("");
1903 DOMString a1 = md5.finishHex();
1904 status("##a1:'%s'", a1.c_str());
1906 //# Make A2
1907 md5.init();
1908 md5.append("AUTHENTICATE:");
1909 md5.append(digest_uri);
1910 DOMString a2 = md5.finishHex();
1911 status("##a2:'%s'", a2.c_str());
1913 //# Now make the response
1914 md5.init();
1915 md5.append(a1);
1916 md5.append(":");
1917 md5.append(nonce);
1918 md5.append(":");
1919 md5.append("00000001");//nc
1920 md5.append(":");
1921 md5.append(cnonce);
1922 md5.append(":");
1923 md5.append("auth");//qop
1924 md5.append(":");
1925 md5.append(a2);
1926 DOMString response = md5.finishHex();
1928 DOMString resp;
1929 resp.append("username=\""); resp.append(username); resp.append("\",");
1930 resp.append("realm=\""); resp.append(realm); resp.append("\",");
1931 resp.append("nonce=\""); resp.append(nonce); resp.append("\",");
1932 resp.append("cnonce=\""); resp.append(cnonce); resp.append("\",");
1933 resp.append("nc=00000001,qop=auth,");
1934 resp.append("digest-uri=\""); resp.append(digest_uri); resp.append("\"," );
1935 //resp.append("authzid=\""); resp.append(authzid); resp.append("\",");
1936 resp.append("response="); resp.append(response); resp.append(",");
1937 resp.append("charset=utf-8");
1938 status("sending response:'%s'", resp.c_str());
1939 resp = Base64Encoder::encode(resp);
1940 status("base64 response:'%s'", resp.c_str());
1941 fmt =
1942 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>\n";
1943 if (!write(fmt, resp.c_str()))
1944 return false;
1946 recbuf = readStanza();
1947 status("server says:: '%s'", recbuf.c_str());
1948 elem = parser.parse(recbuf);
1949 //elem->print();
1950 b64challenge = elem->getTagValue("challenge");
1951 delete elem;
1953 if (b64challenge.size() < 1)
1954 {
1955 error("login: no second SASL challenge offered by server");
1956 return false;
1957 }
1959 challenge = Base64Decoder::decodeToString(b64challenge);
1960 status("md5 challenge: '%s'", challenge.c_str());
1962 if (!saslParse(challenge, attrs))
1963 {
1964 error("login: error parsing SASL challenge");
1965 return false;
1966 }
1968 DOMString rspauth = attrs["rspauth"];
1969 if (rspauth.size()==0)
1970 {
1971 error("login: no SASL respauth sent by server\n");
1972 return false;
1973 }
1975 fmt =
1976 "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>\n";
1977 if (!write(fmt))
1978 return false;
1980 recbuf = readStanza();
1981 status("SASL recv: '%s", recbuf.c_str());
1982 elem = parser.parse(recbuf);
1983 //elem->print();
1984 b64challenge = elem->getTagValue("challenge");
1985 bool success = (elem->findElements("success").size() > 0);
1986 delete elem;
1988 return success;
1989 }
1993 /**
1994 * Attempt to authentication using the SASL PLAIN mechanism. This
1995 * is used most commonly my Google Talk.
1996 */
1997 bool XmppClient::saslPlainAuthenticate()
1998 {
1999 Parser parser;
2001 DOMString id = username;
2002 //id.append("@");
2003 //id.append(host);
2004 Base64Encoder encoder;
2005 encoder.append('\0');
2006 encoder.append(id);
2007 encoder.append('\0');
2008 encoder.append(password);
2009 DOMString base64Auth = encoder.finish();
2010 //printf("authbuf:%s\n", base64Auth.c_str());
2012 char *fmt =
2013 "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' "
2014 "mechanism='PLAIN'>%s</auth>\n";
2015 if (!write(fmt, base64Auth.c_str()))
2016 return false;
2017 DOMString recbuf = readStanza();
2018 status("challenge received: '%s'", recbuf.c_str());
2019 Element *elem = parser.parse(recbuf);
2021 bool success = (elem->findElements("success").size() > 0);
2022 delete elem;
2024 return success;
2025 }
2029 /**
2030 * Handshake with SASL, and use one of its offered mechanisms to
2031 * authenticate.
2032 */
2033 bool XmppClient::saslAuthenticate()
2034 {
2035 Parser parser;
2037 DOMString recbuf = readStanza();
2038 status("RECV: '%s'\n", recbuf.c_str());
2039 Element *elem = parser.parse(recbuf);
2040 //elem->print();
2042 //Check for starttls
2043 bool wantStartTls = false;
2044 if (elem->findElements("starttls").size() > 0)
2045 {
2046 wantStartTls = true;
2047 if (elem->findElements("required").size() > 0)
2048 status("login: STARTTLS required");
2049 else
2050 status("login: STARTTLS available");
2051 }
2053 if (wantStartTls && !sock->getEnableSSL())
2054 {
2055 delete elem;
2056 char *fmt =
2057 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n";
2058 if (!write(fmt))
2059 return false;
2060 recbuf = readStanza();
2061 status("RECV: '%s'\n", recbuf.c_str());
2062 elem = parser.parse(recbuf);
2063 if (elem->getTagAttribute("proceed", "xmlns").size()<1)
2064 {
2065 error("Server rejected TLS negotiation");
2066 disconnect();
2067 return false;
2068 }
2069 delete elem;
2070 if (!sock->startTls())
2071 {
2072 error("Could not start TLS");
2073 disconnect();
2074 return false;
2075 }
2077 fmt =
2078 "<stream:stream xmlns='jabber:client' "
2079 "xmlns:stream='http://etherx.jabber.org/streams' "
2080 "to='%s' version='1.0'>\n\n";
2081 if (!write(fmt, realm.c_str()))
2082 return false;
2084 recbuf = readStanza();
2085 status("RECVx: '%s'", recbuf.c_str());
2086 recbuf.append("</stream:stream>");
2087 elem = parser.parse(recbuf);
2088 bool success =
2089 (elem->getTagAttribute("stream:stream", "id").size()>0);
2090 if (!success)
2091 {
2092 error("STARTTLS negotiation failed");
2093 disconnect();
2094 return false;
2095 }
2096 delete elem;
2097 recbuf = readStanza();
2098 status("RECV: '%s'\n", recbuf.c_str());
2099 elem = parser.parse(recbuf);
2101 XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2102 dispatchXmppEvent(event);
2103 }
2105 //register, if user requests
2106 if (doRegister)
2107 {
2108 if (!inBandRegistrationNew())
2109 return false;
2110 }
2112 //check for sasl authentication mechanisms
2113 std::vector<Element *> elems =
2114 elem->findElements("mechanism");
2115 if (elems.size() < 1)
2116 {
2117 error("login: no SASL mechanism offered by server");
2118 return false;
2119 }
2120 bool md5Found = false;
2121 bool plainFound = false;
2122 for (unsigned int i=0 ; i<elems.size() ; i++)
2123 {
2124 DOMString mech = elems[i]->getValue();
2125 if (mech == "DIGEST-MD5")
2126 {
2127 status("MD5 authentication offered");
2128 md5Found = true;
2129 }
2130 else if (mech == "PLAIN")
2131 {
2132 status("PLAIN authentication offered");
2133 plainFound = true;
2134 }
2135 }
2136 delete elem;
2138 bool success = false;
2139 if (md5Found)
2140 {
2141 success = saslMd5Authenticate();
2142 }
2143 else if (plainFound)
2144 {
2145 success = saslPlainAuthenticate();
2146 }
2147 else
2148 {
2149 error("not able to handle sasl authentication mechanisms");
2150 return false;
2151 }
2153 if (success)
2154 status("###### SASL authentication success\n");
2155 else
2156 error("###### SASL authentication failure\n");
2158 return success;
2159 }
2166 //########################################################################
2167 //# CONNECT
2168 //########################################################################
2171 /**
2172 * Check if we are connected, and fail with an error if we are not
2173 */
2174 bool XmppClient::checkConnect()
2175 {
2176 if (!connected)
2177 {
2178 XmppEvent evt(XmppEvent::EVENT_ERROR);
2179 evt.setData("Attempted operation while disconnected");
2180 dispatchXmppEvent(evt);
2181 return false;
2182 }
2183 return true;
2184 }
2188 /**
2189 * Create an XMPP session with a server. This
2190 * is basically the transport layer of XMPP.
2191 */
2192 bool XmppClient::createSession()
2193 {
2195 Parser parser;
2196 if (port==443 || port==5223)
2197 sock->enableSSL(true);
2198 if (!sock->connect(host, port))
2199 {
2200 return false;
2201 }
2203 if (sock->getEnableSSL())
2204 {
2205 XmppEvent event(XmppEvent::EVENT_SSL_STARTED);
2206 dispatchXmppEvent(event);
2207 }
2209 char *fmt =
2210 "<stream:stream "
2211 "to='%s' "
2212 "xmlns='jabber:client' "
2213 "xmlns:stream='http://etherx.jabber.org/streams' "
2214 "version='1.0'>\n\n";
2215 if (!write(fmt, realm.c_str()))
2216 return false;
2218 DOMString recbuf = readStanza();
2219 //printf("received: '%s'\n", recbuf.c_str());
2220 recbuf.append("</stream:stream>");
2221 Element *elem = parser.parse(recbuf);
2222 //elem->print();
2223 bool useSasl = false;
2224 DOMString streamId = elem->getTagAttribute("stream:stream", "id");
2225 //printf("### StreamID: %s\n", streamId.c_str());
2226 DOMString streamVersion = elem->getTagAttribute("stream:stream", "version");
2227 if (streamVersion == "1.0")
2228 useSasl = true;
2230 if (useSasl)
2231 {
2232 if (!saslAuthenticate())
2233 return false;
2234 fmt =
2235 "<stream:stream "
2236 "to='%s' "
2237 "xmlns='jabber:client' "
2238 "xmlns:stream='http://etherx.jabber.org/streams' "
2239 "version='1.0'>\n\n";
2241 if (!write(fmt, realm.c_str()))
2242 return false;
2243 recbuf = readStanza();
2244 recbuf.append("</stream:stream>\n");
2245 //printf("now server says:: '%s'\n", recbuf.c_str());
2246 elem = parser.parse(recbuf);
2247 //elem->print();
2248 delete elem;
2250 recbuf = readStanza();
2251 //printf("now server says:: '%s'\n", recbuf.c_str());
2252 elem = parser.parse(recbuf);
2253 bool hasBind = (elem->findElements("bind").size() > 0);
2254 //elem->print();
2255 delete elem;
2257 if (!hasBind)
2258 {
2259 error("no binding provided by server");
2260 return false;
2261 }
2264 }
2265 else // not SASL
2266 {
2267 if (!iqAuthenticate(streamId))
2268 return false;
2269 }
2272 //### Resource binding
2273 fmt =
2274 "<iq type='set' id='bind%d'>"
2275 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
2276 "<resource>%s</resource>"
2277 "</bind></iq>\n";
2278 if (!write(fmt, msgId++, resource.c_str()))
2279 return false;
2281 recbuf = readStanza();
2282 status("bind result: '%s'", recbuf.c_str());
2283 elem = parser.parse(recbuf);
2284 //elem->print();
2285 DOMString bindType = elem->getTagAttribute("iq", "type");
2286 //printf("##bindType:%s\n", bindType.c_str());
2287 delete elem;
2289 if (bindType != "result")
2290 {
2291 error("no binding with server failed");
2292 return false;
2293 }
2295 fmt =
2296 "<iq type='set' id='sess%d'>"
2297 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
2298 "</iq>\n";
2299 if (!write(fmt, msgId++))
2300 return false;
2302 recbuf = readStanza();
2303 status("session received: '%s'", recbuf.c_str());
2304 elem = parser.parse(recbuf);
2305 //elem->print();
2306 DOMString sessionType = elem->getTagAttribute("iq", "type");
2307 //printf("##sessionType:%s\n", sessionType.c_str());
2308 delete elem;
2310 if (sessionType != "result")
2311 {
2312 error("no session provided by server");
2313 return false;
2314 }
2316 //printf("########## COOL #########\n");
2317 //Now that we are bound, we have a valid JID
2318 jid = username;
2319 jid.append("@");
2320 jid.append(realm);
2321 jid.append("/");
2322 jid.append(resource);
2324 //We are now done with the synchronous handshaking. Let's go into
2325 //async mode
2327 fmt =
2328 "<iq type='get' id='roster%d'><query xmlns='jabber:iq:roster'/></iq>\n";
2329 if (!write(fmt, msgId++))
2330 return false;
2332 fmt =
2333 "<iq type='get' id='discoItems%d' to='%s'>"
2334 "<query xmlns='http://jabber.org/protocol/disco#items'/></iq>\n";
2335 if (!write(fmt, msgId++, realm.c_str()))
2336 return false;
2338 fmt =
2339 "<iq type='get' id='discoInfo%d' to='conference.%s'>"
2340 "<query xmlns='http://jabber.org/protocol/disco#info'/></iq>\n";
2341 if (!write(fmt, msgId++, realm.c_str()))
2342 return false;
2344 fmt =
2345 "<presence/>\n";
2346 if (!write(fmt))
2347 return false;
2349 /*
2350 recbuf = readStanza();
2351 status("stream received: '%s'", recbuf.c_str());
2352 elem = parser.parse(recbuf);
2353 //elem->print();
2354 delete elem;
2355 */
2357 //We are now logged in
2358 status("Connected");
2359 connected = true;
2360 XmppEvent evt(XmppEvent::EVENT_CONNECTED);
2361 evt.setData(host);
2362 dispatchXmppEvent(evt);
2363 //Thread::sleep(1000000);
2365 sock->setReceiveTimeout(1000);
2366 ReceiverThread runner(*this);
2367 Thread thread(runner);
2368 thread.start();
2370 return true;
2371 }
2375 /**
2376 * Public call to connect
2377 */
2378 bool XmppClient::connect()
2379 {
2380 if (!createSession())
2381 {
2382 disconnect();
2383 return false;
2384 }
2385 return true;
2386 }
2389 /**
2390 * Public call to connect
2391 */
2392 bool XmppClient::connect(DOMString hostArg, int portArg,
2393 DOMString usernameArg,
2394 DOMString passwordArg,
2395 DOMString resourceArg)
2396 {
2397 host = hostArg;
2398 port = portArg;
2399 password = passwordArg;
2400 resource = resourceArg;
2402 //parse this one
2403 setUsername(usernameArg);
2405 bool ret = connect();
2406 return ret;
2407 }
2411 /**
2412 * Public call to disconnect
2413 */
2414 bool XmppClient::disconnect()
2415 {
2416 if (connected)
2417 {
2418 char *fmt =
2419 "<presence from='%s' type='unavailable'/>\n";
2420 write(fmt, jid.c_str());
2421 }
2422 keepGoing = false;
2423 connected = false;
2424 Thread::sleep(2000); //allow receiving thread to quit
2425 sock->disconnect();
2426 roster.clear();
2427 groupChatsClear();
2428 XmppEvent event(XmppEvent::EVENT_DISCONNECTED);
2429 event.setData(host);
2430 dispatchXmppEvent(event);
2431 return true;
2432 }
2438 //########################################################################
2439 //# ROSTER
2440 //########################################################################
2442 /**
2443 * Add an XMPP id to your roster
2444 */
2445 bool XmppClient::rosterAdd(const DOMString &rosterGroup,
2446 const DOMString &otherJid,
2447 const DOMString &name)
2448 {
2449 if (!checkConnect())
2450 return false;
2451 char *fmt =
2452 "<iq from='%s' type='set' id='roster_%d'>"
2453 "<query xmlns='jabber:iq:roster'>"
2454 "<item jid='%s' name='%s'><group>%s</group></item>"
2455 "</query></iq>\n";
2456 if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str(),
2457 name.c_str(), rosterGroup.c_str()))
2458 {
2459 return false;
2460 }
2461 return true;
2462 }
2466 /**
2467 * Delete an XMPP id from your roster.
2468 */
2469 bool XmppClient::rosterDelete(const DOMString &otherJid)
2470 {
2471 if (!checkConnect())
2472 return false;
2473 char *fmt =
2474 "<iq from='%s' type='set' id='roster_%d'>"
2475 "<query xmlns='jabber:iq:roster'>"
2476 "<item jid='%s' subscription='remove'><group>%s</group></item>"
2477 "</query></iq>\n";
2478 if (!write(fmt, jid.c_str(), msgId++, otherJid.c_str()))
2479 {
2480 return false;
2481 }
2482 return true;
2483 }
2486 /**
2487 * Comparison method for sort() call below
2488 */
2489 static bool xmppRosterCompare(const XmppUser& p1, const XmppUser& p2)
2490 {
2491 DOMString s1 = p1.group;
2492 DOMString s2 = p2.group;
2493 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2494 {
2495 int comp = tolower(s1[len]) - tolower(s2[len]);
2496 if (comp)
2497 return (comp<0);
2498 }
2500 s1 = p1.jid;
2501 s2 = p2.jid;
2502 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2503 {
2504 int comp = tolower(s1[len]) - tolower(s2[len]);
2505 if (comp)
2506 return (comp<0);
2507 }
2508 return false;
2509 }
2513 /**
2514 * Sort and return the roster that has just been reported by
2515 * an XmppEvent::EVENT_ROSTER event.
2516 */
2517 std::vector<XmppUser> XmppClient::getRoster()
2518 {
2519 std::vector<XmppUser> ros = roster;
2520 std::sort(ros.begin(), ros.end(), xmppRosterCompare);
2521 return ros;
2522 }
2525 /**
2526 *
2527 */
2528 void XmppClient::rosterShow(const DOMString &jid, const DOMString &show)
2529 {
2530 DOMString theShow = show;
2531 if (theShow == "")
2532 theShow = "available";
2534 std::vector<XmppUser>::iterator iter;
2535 for (iter=roster.begin() ; iter != roster.end() ; iter++)
2536 {
2537 if (iter->jid == jid)
2538 iter->show = theShow;
2539 }
2540 }
2547 //########################################################################
2548 //# CHAT (individual)
2549 //########################################################################
2551 /**
2552 * Send a message to an xmpp jid
2553 */
2554 bool XmppClient::message(const DOMString &user, const DOMString &subj,
2555 const DOMString &msg)
2556 {
2557 if (!checkConnect())
2558 return false;
2560 DOMString xmlSubj = toXml(subj);
2561 DOMString xmlMsg = toXml(msg);
2563 if (xmlSubj.size() > 0)
2564 {
2565 char *fmt =
2566 "<message from='%s' to='%s' type='chat'>"
2567 "<subject>%s</subject><body>%s</body></message>\n";
2568 if (!write(fmt, jid.c_str(), user.c_str(),
2569 xmlSubj.c_str(), xmlMsg.c_str()))
2570 return false;
2571 }
2572 else
2573 {
2574 char *fmt =
2575 "<message from='%s' to='%s'>"
2576 "<body>%s</body></message>\n";
2577 if (!write(fmt, jid.c_str(), user.c_str(), xmlMsg.c_str()))
2578 return false;
2579 }
2580 return true;
2581 }
2585 /**
2586 *
2587 */
2588 bool XmppClient::message(const DOMString &user, const DOMString &msg)
2589 {
2590 return message(user, "", msg);
2591 }
2595 /**
2596 *
2597 */
2598 bool XmppClient::presence(const DOMString &presence)
2599 {
2600 if (!checkConnect())
2601 return false;
2603 DOMString xmlPres = toXml(presence);
2605 char *fmt =
2606 "<presence from='%s'><show>%s</show></presence>\n";
2607 if (!write(fmt, jid.c_str(), xmlPres.c_str()))
2608 return false;
2609 return true;
2610 }
2617 //########################################################################
2618 //# GROUP CHAT
2619 //########################################################################
2621 /**
2622 *
2623 */
2624 bool XmppClient::groupChatCreate(const DOMString &groupJid)
2625 {
2626 std::vector<XmppGroupChat *>::iterator iter;
2627 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2628 {
2629 if ((*iter)->getGroupJid() == groupJid)
2630 {
2631 error("Group chat '%s' already exists", groupJid.c_str());
2632 return false;
2633 }
2634 }
2635 XmppGroupChat *chat = new XmppGroupChat(groupJid);
2636 groupChats.push_back(chat);
2637 return true;
2638 }
2642 /**
2643 *
2644 */
2645 void XmppClient::groupChatDelete(const DOMString &groupJid)
2646 {
2647 std::vector<XmppGroupChat *>::iterator iter;
2648 for (iter=groupChats.begin() ; iter!=groupChats.end() ; )
2649 {
2650 XmppGroupChat *chat = *iter;
2651 if (chat->getGroupJid() == groupJid)
2652 {
2653 iter = groupChats.erase(iter);
2654 delete chat;
2655 }
2656 else
2657 iter++;
2658 }
2659 }
2663 /**
2664 *
2665 */
2666 bool XmppClient::groupChatExists(const DOMString &groupJid)
2667 {
2668 std::vector<XmppGroupChat *>::iterator iter;
2669 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2670 if ((*iter)->getGroupJid() == groupJid)
2671 return true;
2672 return false;
2673 }
2677 /**
2678 *
2679 */
2680 void XmppClient::groupChatsClear()
2681 {
2682 std::vector<XmppGroupChat *>::iterator iter;
2683 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2684 delete (*iter);
2685 groupChats.clear();
2686 }
2691 /**
2692 *
2693 */
2694 void XmppClient::groupChatUserAdd(const DOMString &groupJid,
2695 const DOMString &nick,
2696 const DOMString &jid)
2697 {
2698 std::vector<XmppGroupChat *>::iterator iter;
2699 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2700 {
2701 if ((*iter)->getGroupJid() == groupJid)
2702 {
2703 (*iter)->userAdd(nick, jid);
2704 }
2705 }
2706 }
2710 /**
2711 *
2712 */
2713 void XmppClient::groupChatUserShow(const DOMString &groupJid,
2714 const DOMString &nick,
2715 const DOMString &show)
2716 {
2717 std::vector<XmppGroupChat *>::iterator iter;
2718 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2719 {
2720 if ((*iter)->getGroupJid() == groupJid)
2721 {
2722 (*iter)->userShow(nick, show);
2723 }
2724 }
2725 }
2730 /**
2731 *
2732 */
2733 void XmppClient::groupChatUserDelete(const DOMString &groupJid,
2734 const DOMString &nick)
2735 {
2736 std::vector<XmppGroupChat *>::iterator iter;
2737 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2738 {
2739 if ((*iter)->getGroupJid() == groupJid)
2740 {
2741 (*iter)->userDelete(nick);
2742 }
2743 }
2744 }
2748 /**
2749 * Comparison method for the sort() below
2750 */
2751 static bool xmppUserCompare(const XmppUser& p1, const XmppUser& p2)
2752 {
2753 DOMString s1 = p1.nick;
2754 DOMString s2 = p2.nick;
2755 int comp = 0;
2756 for (unsigned int len=0 ; len<s1.size() && len<s2.size() ; len++)
2757 {
2758 comp = tolower(s1[len]) - tolower(s2[len]);
2759 if (comp)
2760 break;
2761 }
2762 return (comp<0);
2763 }
2767 /**
2768 * Return the user list for the named group
2769 */
2770 std::vector<XmppUser> XmppClient::groupChatGetUserList(
2771 const DOMString &groupJid)
2772 {
2773 if (!checkConnect())
2774 {
2775 std::vector<XmppUser> dummy;
2776 return dummy;
2777 }
2779 std::vector<XmppGroupChat *>::iterator iter;
2780 for (iter=groupChats.begin() ; iter!=groupChats.end() ; iter++)
2781 {
2782 if ((*iter)->getGroupJid() == groupJid )
2783 {
2784 std::vector<XmppUser> uList = (*iter)->getUserList();
2785 std::sort(uList.begin(), uList.end(), xmppUserCompare);
2786 return uList;
2787 }
2788 }
2789 std::vector<XmppUser> dummy;
2790 return dummy;
2791 }
2796 /**
2797 * Try to join a group
2798 */
2799 bool XmppClient::groupChatJoin(const DOMString &groupJid,
2800 const DOMString &nick,
2801 const DOMString &pass)
2802 {
2803 if (!checkConnect())
2804 return false;
2806 DOMString user = nick;
2807 if (user.size()<1)
2808 user = username;
2810 char *fmt =
2811 "<presence to='%s/%s'>"
2812 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2813 if (!write(fmt, groupJid.c_str(), user.c_str()))
2814 return false;
2815 return true;
2816 }
2821 /**
2822 * Leave a group
2823 */
2824 bool XmppClient::groupChatLeave(const DOMString &groupJid,
2825 const DOMString &nick)
2826 {
2827 if (!checkConnect())
2828 return false;
2830 DOMString user = nick;
2831 if (user.size()<1)
2832 user = username;
2834 char *fmt =
2835 "<presence to='%s/%s' type='unavailable'>"
2836 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2837 if (!write(fmt, groupJid.c_str(), user.c_str()))
2838 return false;
2839 return true;
2840 }
2845 /**
2846 * Send a message to a group
2847 */
2848 bool XmppClient::groupChatMessage(const DOMString &groupJid,
2849 const DOMString &msg)
2850 {
2851 if (!checkConnect())
2852 {
2853 return false;
2854 }
2856 DOMString xmlMsg = toXml(msg);
2858 char *fmt =
2859 "<message from='%s' to='%s' type='groupchat'>"
2860 "<body>%s</body></message>\n";
2861 if (!write(fmt, jid.c_str(), groupJid.c_str(), xmlMsg.c_str()))
2862 return false;
2863 return true;
2864 }
2869 /**
2870 * Send a message to an individual in a group
2871 */
2872 bool XmppClient::groupChatPrivateMessage(const DOMString &groupJid,
2873 const DOMString &toNick,
2874 const DOMString &msg)
2875 {
2876 if (!checkConnect())
2877 return false;
2879 DOMString xmlMsg = toXml(msg);
2881 char *fmt =
2882 "<message from='%s' to='%s/%s' type='chat'>"
2883 "<body>%s</body></message>\n";
2884 if (!write(fmt, jid.c_str(), groupJid.c_str(),
2885 toNick.c_str(), xmlMsg.c_str()))
2886 return false;
2887 return true;
2888 }
2893 /**
2894 * Change your presence within a group
2895 */
2896 bool XmppClient::groupChatPresence(const DOMString &groupJid,
2897 const DOMString &myNick,
2898 const DOMString &presence)
2899 {
2900 if (!checkConnect())
2901 return false;
2903 DOMString user = myNick;
2904 if (user.size()<1)
2905 user = username;
2907 DOMString xmlPresence = toXml(presence);
2909 char *fmt =
2910 "<presence from='%s' to='%s/%s' type='unavailable'>"
2911 "<x xmlns='http://jabber.org/protocol/muc'/></presence>\n";
2912 if (!write(fmt, jid.c_str(), groupJid.c_str(),
2913 user.c_str(), xmlPresence.c_str()))
2914 return true;
2915 return true;
2916 }
2922 //########################################################################
2923 //# S T R E A M S
2924 //########################################################################
2927 /**
2928 *
2929 */
2930 int XmppClient::outputStreamOpen(const DOMString &destId,
2931 const DOMString &streamIdArg)
2932 {
2933 int i;
2934 for (i=0; i<outputStreamCount ; i++)
2935 if (outputStreams[i]->getState() == STREAM_AVAILABLE)
2936 break;
2937 if (i>=outputStreamCount)
2938 {
2939 error("No available output streams");
2940 return -1;
2941 }
2942 int streamNr = i;
2943 XmppStream *outs = outputStreams[streamNr];
2945 outs->setState(STREAM_OPENING);
2947 char buf[32];
2948 snprintf(buf, 31, "inband%d", getMsgId());
2949 DOMString iqId = buf;
2951 DOMString streamId = streamIdArg;
2952 if (streamId.size()<1)
2953 {
2954 snprintf(buf, 31, "stream%d", getMsgId());
2955 DOMString streamId = buf;
2956 }
2957 outs->setIqId(iqId);
2958 outs->setStreamId(streamId);
2959 outs->setPeerId(destId);
2961 char *fmt =
2962 "<iq type='set' from='%s' to='%s' id='%s'>"
2963 "<open sid='%s' block-size='4096'"
2964 " xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
2965 if (!write(fmt, jid.c_str(),
2966 destId.c_str(), iqId.c_str(),
2967 streamId.c_str()))
2968 {
2969 outs->reset();
2970 return -1;
2971 }
2973 int state = outs->getState();
2974 for (int tim=0 ; tim<20 ; tim++)
2975 {
2976 if (state == STREAM_OPEN)
2977 break;
2978 else if (state == STREAM_ERROR)
2979 {
2980 printf("ERROR\n");
2981 outs->reset();
2982 return -1;
2983 }
2984 Thread::sleep(1000);
2985 state = outs->getState();
2986 }
2987 if (state != STREAM_OPEN)
2988 {
2989 printf("TIMEOUT ERROR\n");
2990 outs->reset();
2991 return -1;
2992 }
2994 return streamNr;
2995 }
2997 /**
2998 *
2999 */
3000 int XmppClient::outputStreamWrite(int streamNr,
3001 const unsigned char *buf, unsigned long len)
3002 {
3003 XmppStream *outs = outputStreams[streamNr];
3005 unsigned long outLen = 0;
3006 unsigned char *p = (unsigned char *)buf;
3008 while (outLen < len)
3009 {
3010 unsigned long chunksize = 1024;
3011 if (chunksize + outLen > len)
3012 chunksize = len - outLen;
3014 Base64Encoder encoder;
3015 encoder.append(p, chunksize);
3016 DOMString b64data = encoder.finish();
3017 p += chunksize;
3018 outLen += chunksize;
3020 char *fmt =
3021 "<message from='%s' to='%s' id='msg%d'>"
3022 "<data xmlns='http://jabber.org/protocol/ibb' sid='%s' seq='%d'>"
3023 "%s"
3024 "</data>"
3025 "<amp xmlns='http://jabber.org/protocol/amp'>"
3026 "<rule condition='deliver-at' value='stored' action='error'/>"
3027 "<rule condition='match-resource' value='exact' action='error'/>"
3028 "</amp>"
3029 "</message>\n";
3030 if (!write(fmt, jid.c_str(),
3031 outs->getPeerId().c_str(),
3032 getMsgId(),
3033 outs->getStreamId().c_str(),
3034 outs->getSeqNr(),
3035 b64data.c_str()))
3036 {
3037 outs->reset();
3038 return -1;
3039 }
3040 pause(5000);
3041 }
3042 return outLen;
3043 }
3045 /**
3046 *
3047 */
3048 int XmppClient::outputStreamClose(int streamNr)
3049 {
3050 XmppStream *outs = outputStreams[streamNr];
3052 char buf[32];
3053 snprintf(buf, 31, "inband%d", getMsgId());
3054 DOMString iqId = buf;
3055 outs->setIqId(iqId);
3057 outs->setState(STREAM_CLOSING);
3058 char *fmt =
3059 "<iq type='set' from='%s' to='%s' id='%s'>"
3060 "<close sid='%s' xmlns='http://jabber.org/protocol/ibb'/></iq>\n";
3061 if (!write(fmt, jid.c_str(),
3062 outs->getPeerId().c_str(),
3063 iqId.c_str(),
3064 outs->getStreamId().c_str()))
3065 return false;
3067 int state = outs->getState();
3068 for (int tim=0 ; tim<20 ; tim++)
3069 {
3070 if (state == STREAM_CLOSED)
3071 break;
3072 else if (state == STREAM_ERROR)
3073 {
3074 printf("ERROR\n");
3075 outs->reset();
3076 return -1;
3077 }
3078 Thread::sleep(1000);
3079 state = outs->getState();
3080 }
3081 if (state != STREAM_CLOSED)
3082 {
3083 printf("TIMEOUT ERROR\n");
3084 outs->reset();
3085 return -1;
3086 }
3088 outs->reset();
3089 return 1;
3090 }
3093 /**
3094 *
3095 */
3096 int XmppClient::inputStreamOpen(const DOMString &fromJid, const DOMString &streamId,
3097 const DOMString &iqId)
3098 {
3099 int i;
3100 for (i=0 ; i<inputStreamCount ; i++)
3101 {
3102 if (inputStreams[i]->getState() == STREAM_AVAILABLE)
3103 break;
3104 }
3105 if (i>=inputStreamCount)
3106 {
3107 error("No available input streams");
3108 return -1;
3109 }
3110 int streamNr = i;
3111 XmppStream *ins = inputStreams[streamNr];
3112 ins->reset();
3113 ins->setPeerId(fromJid);
3114 ins->setState(STREAM_CLOSED);
3115 ins->setStreamId(streamId);
3117 int state = ins->getState();
3118 for (int tim=0 ; tim<20 ; tim++)
3119 {
3120 if (state == STREAM_OPENING)
3121 break;
3122 else if (state == STREAM_ERROR)
3123 {
3124 printf("ERROR\n");
3125 ins->reset();
3126 return -1;
3127 }
3128 Thread::sleep(1000);
3129 state = ins->getState();
3130 }
3131 if (state != STREAM_OPENING)
3132 {
3133 printf("TIMEOUT ERROR\n");
3134 ins->reset();
3135 return -1;
3136 }
3137 char *fmt =
3138 "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3139 if (!write(fmt, jid.c_str(), fromJid.c_str(), ins->getIqId().c_str()))
3140 {
3141 return -1;
3142 }
3144 ins->setState(STREAM_OPEN);
3145 return streamNr;
3146 }
3150 /**
3151 *
3152 */
3153 int XmppClient::inputStreamAvailable(int streamNr)
3154 {
3155 XmppStream *ins = inputStreams[streamNr];
3156 return ins->available();
3157 }
3159 /**
3160 *
3161 */
3162 std::vector<unsigned char> XmppClient::inputStreamRead(int streamNr)
3163 {
3164 XmppStream *ins = inputStreams[streamNr];
3165 return ins->read();
3166 }
3168 /**
3169 *
3170 */
3171 bool XmppClient::inputStreamClosing(int streamNr)
3172 {
3173 XmppStream *ins = inputStreams[streamNr];
3174 if (ins->getState() == STREAM_CLOSING)
3175 return true;
3176 return false;
3177 }
3180 /**
3181 *
3182 */
3183 int XmppClient::inputStreamClose(int streamNr)
3184 {
3185 int ret=1;
3186 XmppStream *ins = inputStreams[streamNr];
3187 if (ins->getState() == STREAM_CLOSING)
3188 {
3189 char *fmt =
3190 "<iq type='result' from='%s' to='%s' id='%s'/>\n";
3191 if (!write(fmt, jid.c_str(), ins->getPeerId().c_str(),
3192 ins->getIqId().c_str()))
3193 {
3194 ret = -1;
3195 }
3196 }
3197 ins->reset();
3198 return ret;
3199 }
3206 //########################################################################
3207 //# FILE TRANSFERS
3208 //########################################################################
3211 /**
3212 *
3213 */
3214 bool XmppClient::fileSend(const DOMString &destJidArg,
3215 const DOMString &offeredNameArg,
3216 const DOMString &fileNameArg,
3217 const DOMString &descriptionArg)
3218 {
3219 DOMString destJid = destJidArg;
3220 DOMString offeredName = offeredNameArg;
3221 DOMString fileName = fileNameArg;
3222 DOMString description = descriptionArg;
3224 int i;
3225 for (i=0; i<fileSendCount ; i++)
3226 if (fileSends[i]->getState() == STREAM_AVAILABLE)
3227 break;
3228 if (i>=fileSendCount)
3229 {
3230 error("No available file send streams");
3231 return false;
3232 }
3233 int fileSendNr = i;
3234 XmppStream *outf = fileSends[fileSendNr];
3236 outf->setState(STREAM_OPENING);
3238 struct stat finfo;
3239 if (stat(fileName.c_str(), &finfo)<0)
3240 {
3241 error("Cannot stat file '%s' for sending", fileName.c_str());
3242 return false;
3243 }
3244 long fileLen = finfo.st_size;
3245 if (!fileLen > 1000000)
3246 {
3247 error("'%s' too large", fileName.c_str());
3248 return false;
3249 }
3250 if (!S_ISREG(finfo.st_mode))
3251 {
3252 error("'%s' is not a regular file", fileName.c_str());
3253 return false;
3254 }
3255 FILE *f = fopen(fileName.c_str(), "rb");
3256 if (!f)
3257 {
3258 error("cannot open '%s' for sending", fileName.c_str());
3259 return false;
3260 }
3261 unsigned char *sendBuf = (unsigned char *)malloc(fileLen+1);
3262 if (!sendBuf)
3263 {
3264 error("cannot cannot allocate send buffer for %s", fileName.c_str());
3265 return false;
3266 }
3267 for (long i=0 ; i<fileLen && !feof(f); i++)
3268 {
3269 sendBuf[i] = fgetc(f);
3270 }
3271 fclose(f);
3273 //## get the last path segment from the whole path
3274 if (offeredName.size()<1)
3275 {
3276 int slashPos = -1;
3277 for (unsigned int i=0 ; i<fileName.size() ; i++)
3278 {
3279 int ch = fileName[i];
3280 if (ch == '/' || ch == '\\')
3281 slashPos = i;
3282 }
3283 if (slashPos>=0 && slashPos<=(int)(fileName.size()-1))
3284 {
3285 offeredName = fileName.substr(slashPos+1,
3286 fileName.size()-slashPos-1);
3287 printf("offeredName:%s\n", offeredName.c_str());
3288 }
3289 }
3291 char buf[32];
3292 snprintf(buf, 31, "file%d", getMsgId());
3293 DOMString iqId = buf;
3294 outf->setIqId(iqId);
3296 snprintf(buf, 31, "stream%d", getMsgId());
3297 DOMString streamId = buf;
3298 //outf->setStreamId(streamId);
3300 DOMString hash = Md5::hashHex(sendBuf, fileLen);
3301 printf("Hash:%s\n", hash.c_str());
3303 outf->setPeerId(destJid);
3305 char dtgBuf[81];
3306 struct tm *timeVal = gmtime(&(finfo.st_mtime));
3307 strftime(dtgBuf, 80, "%Y-%m-%dT%H:%M:%Sz", timeVal);
3309 char *fmt =
3310 "<iq type='set' id='%s' to='%s'>"
3311 "<si xmlns='http://jabber.org/protocol/si' id='%s'"
3312 " mime-type='text/plain'"
3313 " profile='http://jabber.org/protocol/si/profile/file-transfer'>"
3314 "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'"
3315 " name='%s' size='%d' hash='%s' date='%s'><desc>%s</desc></file>"
3316 "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3317 "<x xmlns='jabber:x:data' type='form'>"
3318 "<field var='stream-method' type='list-single'>"
3319 //"<option><value>http://jabber.org/protocol/bytestreams</value></option>"
3320 "<option><value>http://jabber.org/protocol/ibb</value></option>"
3321 "</field></x></feature></si></iq>\n";
3322 if (!write(fmt, iqId.c_str(), destJid.c_str(),
3323 streamId.c_str(), offeredName.c_str(), fileLen,
3324 hash.c_str(), dtgBuf, description.c_str()))
3325 {
3326 free(sendBuf);
3327 return false;
3328 }
3330 int state = outf->getState();
3331 for (int tim=0 ; tim<20 ; tim++)
3332 {
3333 printf("##### waiting for open\n");
3334 if (state == STREAM_OPEN)
3335 {
3336 outf->reset();
3337 break;
3338 }
3339 else if (state == STREAM_ERROR)
3340 {
3341 printf("ERROR\n");
3342 outf->reset();
3343 return false;
3344 }
3345 Thread::sleep(1000);
3346 state = outf->getState();
3347 }
3348 if (state != STREAM_OPEN)
3349 {
3350 printf("TIMEOUT ERROR\n");
3351 outf->reset();
3352 return false;
3353 }
3355 //free up this reqource
3356 outf->reset();
3358 int streamNr = outputStreamOpen(destJid, streamId);
3359 if (streamNr<0)
3360 {
3361 error("cannot open output stream %s", streamId.c_str());
3362 outf->reset();
3363 return false;
3364 }
3366 int ret = outputStreamWrite(streamNr, sendBuf, fileLen);
3368 if (ret<0)
3369 {
3370 }
3372 outputStreamClose(streamNr);
3374 free(sendBuf);
3375 return true;
3376 }
3379 class FileSendThread : public Thread
3380 {
3381 public:
3383 FileSendThread(XmppClient &par,
3384 const DOMString &destJidArg,
3385 const DOMString &offeredNameArg,
3386 const DOMString &fileNameArg,
3387 const DOMString &descriptionArg) : client(par)
3388 {
3389 destJid = destJidArg;
3390 offeredName = offeredNameArg;
3391 fileName = fileNameArg;
3392 description = descriptionArg;
3393 }
3395 virtual ~FileSendThread() {}
3397 void run()
3398 {
3399 client.fileSend(destJid, offeredName,
3400 fileName, description);
3401 }
3403 private:
3405 XmppClient &client;
3406 DOMString destJid;
3407 DOMString offeredName;
3408 DOMString fileName;
3409 DOMString description;
3410 };
3412 /**
3413 *
3414 */
3415 bool XmppClient::fileSendBackground(const DOMString &destJid,
3416 const DOMString &offeredName,
3417 const DOMString &fileName,
3418 const DOMString &description)
3419 {
3420 FileSendThread thread(*this, destJid, offeredName,
3421 fileName, description);
3422 thread.start();
3423 return true;
3424 }
3427 /**
3428 *
3429 */
3430 bool XmppClient::fileReceive(const DOMString &fromJid,
3431 const DOMString &iqId,
3432 const DOMString &streamId,
3433 const DOMString &fileName,
3434 long fileSize,
3435 const DOMString &fileHash)
3436 {
3437 char *fmt =
3438 "<iq type='result' to='%s' id='%s'>"
3439 "<si xmlns='http://jabber.org/protocol/si'>"
3440 "<file xmlns='http://jabber.org/protocol/si/profile/file-transfer'/>"
3441 "<feature xmlns='http://jabber.org/protocol/feature-neg'>"
3442 "<x xmlns='jabber:x:data' type='submit'>"
3443 "<field var='stream-method'>"
3444 "<value>http://jabber.org/protocol/ibb</value>"
3445 "</field></x></feature></si></iq>\n";
3446 if (!write(fmt, fromJid.c_str(), iqId.c_str()))
3447 {
3448 return false;
3449 }
3451 int streamNr = inputStreamOpen(fromJid, streamId, iqId);
3452 if (streamNr < 0)
3453 {
3454 return false;
3455 }
3458 Md5 md5;
3459 FILE *f = fopen(fileName.c_str(), "wb");
3460 if (!f)
3461 {
3462 return false;
3463 }
3465 while (true)
3466 {
3467 if (inputStreamAvailable(streamNr)<1)
3468 {
3469 if (inputStreamClosing(streamNr))
3470 break;
3471 pause(100);
3472 continue;
3473 }
3474 std::vector<unsigned char> ret = inputStreamRead(streamNr);
3475 std::vector<unsigned char>::iterator iter;
3476 for (iter=ret.begin() ; iter!=ret.end() ; iter++)
3477 {
3478 unsigned char ch = *iter;
3479 md5.append(&ch, 1);
3480 fwrite(&ch, 1, 1, f);
3481 }
3482 }
3484 inputStreamClose(streamNr);
3485 fclose(f);
3487 DOMString hash = md5.finishHex();
3488 printf("received file hash:%s\n", hash.c_str());
3490 return true;
3491 }
3495 class FileReceiveThread : public Thread
3496 {
3497 public:
3499 FileReceiveThread(XmppClient &par,
3500 const DOMString &fromJidArg,
3501 const DOMString &iqIdArg,
3502 const DOMString &streamIdArg,
3503 const DOMString &fileNameArg,
3504 long fileSizeArg,
3505 const DOMString &fileHashArg) : client(par)
3506 {
3507 fromJid = fromJidArg;
3508 iqId = iqIdArg;
3509 streamId = streamIdArg;
3510 fileName = fileNameArg;
3511 fileSize = fileSizeArg;
3512 fileHash = fileHashArg;
3513 }
3515 virtual ~FileReceiveThread() {}
3517 void run()
3518 {
3519 client.fileReceive(fromJid, iqId, streamId,
3520 fileName, fileSize, fileHash);
3521 }
3523 private:
3525 XmppClient &client;
3526 DOMString fromJid;
3527 DOMString iqId;
3528 DOMString streamId;
3529 DOMString fileName;
3530 long fileSize;
3531 DOMString fileHash;
3532 };
3534 /**
3535 *
3536 */
3537 bool XmppClient::fileReceiveBackground(const DOMString &fromJid,
3538 const DOMString &iqId,
3539 const DOMString &streamId,
3540 const DOMString &fileName,
3541 long fileSize,
3542 const DOMString &fileHash)
3543 {
3544 FileReceiveThread thread(*this, fromJid, iqId, streamId,
3545 fileName, fileSize, fileHash);
3546 thread.start();
3547 return true;
3548 }
3552 //########################################################################
3553 //# X M P P G R O U P C H A T
3554 //########################################################################
3556 /**
3557 *
3558 */
3559 XmppGroupChat::XmppGroupChat(const DOMString &groupJidArg)
3560 {
3561 groupJid = groupJidArg;
3562 }
3564 /**
3565 *
3566 */
3567 XmppGroupChat::XmppGroupChat(const XmppGroupChat &other)
3568 {
3569 groupJid = other.groupJid;
3570 userList = other.userList;
3571 }
3573 /**
3574 *
3575 */
3576 XmppGroupChat::~XmppGroupChat()
3577 {
3578 }
3581 /**
3582 *
3583 */
3584 DOMString XmppGroupChat::getGroupJid()
3585 {
3586 return groupJid;
3587 }
3590 void XmppGroupChat::userAdd(const DOMString &nick,
3591 const DOMString &jid)
3592 {
3593 std::vector<XmppUser>::iterator iter;
3594 for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3595 {
3596 if (iter->nick == nick)
3597 return;
3598 }
3599 XmppUser user(jid, nick);
3600 userList.push_back(user);
3601 }
3603 void XmppGroupChat::userShow(const DOMString &nick,
3604 const DOMString &show)
3605 {
3606 DOMString theShow = show;
3607 if (theShow == "")
3608 theShow = "available"; // a join message will now have a show
3609 std::vector<XmppUser>::iterator iter;
3610 for (iter= userList.begin() ; iter!=userList.end() ; iter++)
3611 {
3612 if (iter->nick == nick)
3613 iter->show = theShow;
3614 }
3615 }
3617 void XmppGroupChat::userDelete(const DOMString &nick)
3618 {
3619 std::vector<XmppUser>::iterator iter;
3620 for (iter= userList.begin() ; iter!=userList.end() ; )
3621 {
3622 if (iter->nick == nick)
3623 iter = userList.erase(iter);
3624 else
3625 iter++;
3626 }
3627 }
3629 std::vector<XmppUser> XmppGroupChat::getUserList() const
3630 {
3631 return userList;
3632 }
3642 } //namespace Pedro
3643 //########################################################################
3644 //# E N D O F F I L E
3645 //########################################################################