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