1/* 2 * libjingle 3 * Copyright 2004--2005, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#include "talk/examples/call/presencepushtask.h" 29 30#include "talk/examples/call/muc.h" 31#include "talk/xmpp/constants.h" 32#include "webrtc/base/stringencode.h" 33 34 35 36namespace buzz { 37 38// string helper functions ----------------------------------------------------- 39 40static bool 41IsXmlSpace(int ch) { 42 return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t'; 43} 44 45static bool ListContainsToken(const std::string & list, 46 const std::string & token) { 47 size_t i = list.find(token); 48 if (i == std::string::npos || token.empty()) 49 return false; 50 bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1])); 51 bool boundary_after = (i == list.length() - token.length() || 52 IsXmlSpace(list[i + token.length()])); 53 return boundary_before && boundary_after; 54} 55 56 57bool PresencePushTask::HandleStanza(const XmlElement * stanza) { 58 if (stanza->Name() != QN_PRESENCE) 59 return false; 60 QueueStanza(stanza); 61 return true; 62} 63 64static bool IsUtf8FirstByte(int c) { 65 return (((c)&0x80)==0) || // is single byte 66 ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte 67} 68 69int PresencePushTask::ProcessStart() { 70 const XmlElement * stanza = NextStanza(); 71 if (stanza == NULL) 72 return STATE_BLOCKED; 73 74 Jid from(stanza->Attr(QN_FROM)); 75 std::map<Jid, buzz::Muc*>::const_iterator elem = 76 client_->mucs().find(from.BareJid()); 77 if (elem == client_->mucs().end()) { 78 HandlePresence(from, stanza); 79 } else { 80 HandleMucPresence(elem->second, from, stanza); 81 } 82 83 return STATE_START; 84} 85 86void PresencePushTask::HandlePresence(const Jid& from, 87 const XmlElement* stanza) { 88 if (stanza->Attr(QN_TYPE) == STR_ERROR) 89 return; 90 91 PresenceStatus s; 92 FillStatus(from, stanza, &s); 93 SignalStatusUpdate(s); 94} 95 96void PresencePushTask::HandleMucPresence(buzz::Muc* muc, 97 const Jid& from, 98 const XmlElement* stanza) { 99 if (from == muc->local_jid()) { 100 if (!stanza->HasAttr(QN_TYPE)) { 101 // We joined the MUC. 102 const XmlElement* elem = stanza->FirstNamed(QN_MUC_USER_X); 103 // Status code=110 or 100 is not guaranteed to be present, so we 104 // only check the item element and Muc join status. 105 if (elem) { 106 if (elem->FirstNamed(QN_MUC_USER_ITEM) && 107 muc->state() == buzz::Muc::MUC_JOINING) { 108 SignalMucJoined(muc->jid()); 109 } 110 } 111 } else { 112 // We've been kicked. Bye. 113 int error = 0; 114 if (stanza->Attr(QN_TYPE) == STR_ERROR) { 115 const XmlElement* elem = stanza->FirstNamed(QN_ERROR); 116 if (elem && elem->HasAttr(QN_CODE)) { 117 error = atoi(elem->Attr(QN_CODE).c_str()); 118 } 119 } 120 SignalMucLeft(muc->jid(), error); 121 } 122 } else { 123 MucPresenceStatus s; 124 FillMucStatus(from, stanza, &s); 125 SignalMucStatusUpdate(muc->jid(), s); 126 } 127} 128 129void PresencePushTask::FillStatus(const Jid& from, const XmlElement* stanza, 130 PresenceStatus* s) { 131 s->set_jid(from); 132 if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) { 133 s->set_available(false); 134 } else { 135 s->set_available(true); 136 const XmlElement * status = stanza->FirstNamed(QN_STATUS); 137 if (status != NULL) { 138 s->set_status(status->BodyText()); 139 140 // Truncate status messages longer than 300 bytes 141 if (s->status().length() > 300) { 142 size_t len = 300; 143 144 // Be careful not to split legal utf-8 chars in half 145 while (!IsUtf8FirstByte(s->status()[len]) && len > 0) { 146 len -= 1; 147 } 148 std::string truncated(s->status(), 0, len); 149 s->set_status(truncated); 150 } 151 } 152 153 const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY); 154 if (priority != NULL) { 155 int pri; 156 if (rtc::FromString(priority->BodyText(), &pri)) { 157 s->set_priority(pri); 158 } 159 } 160 161 const XmlElement * show = stanza->FirstNamed(QN_SHOW); 162 if (show == NULL || show->FirstChild() == NULL) { 163 s->set_show(PresenceStatus::SHOW_ONLINE); 164 } 165 else { 166 if (show->BodyText() == "away") { 167 s->set_show(PresenceStatus::SHOW_AWAY); 168 } 169 else if (show->BodyText() == "xa") { 170 s->set_show(PresenceStatus::SHOW_XA); 171 } 172 else if (show->BodyText() == "dnd") { 173 s->set_show(PresenceStatus::SHOW_DND); 174 } 175 else if (show->BodyText() == "chat") { 176 s->set_show(PresenceStatus::SHOW_CHAT); 177 } 178 else { 179 s->set_show(PresenceStatus::SHOW_ONLINE); 180 } 181 } 182 183 const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C); 184 if (caps != NULL) { 185 std::string node = caps->Attr(QN_NODE); 186 std::string ver = caps->Attr(QN_VER); 187 std::string exts = caps->Attr(QN_EXT); 188 189 s->set_know_capabilities(true); 190 s->set_caps_node(node); 191 s->set_version(ver); 192 193 if (ListContainsToken(exts, "voice-v1")) { 194 s->set_voice_capability(true); 195 } 196 if (ListContainsToken(exts, "video-v1")) { 197 s->set_video_capability(true); 198 } 199 } 200 201 const XmlElement* delay = stanza->FirstNamed(kQnDelayX); 202 if (delay != NULL) { 203 // Ideally we would parse this according to the Psuedo ISO-8601 rules 204 // that are laid out in JEP-0082: 205 // http://www.jabber.org/jeps/jep-0082.html 206 std::string stamp = delay->Attr(kQnStamp); 207 s->set_sent_time(stamp); 208 } 209 210 const XmlElement* nick = stanza->FirstNamed(QN_NICKNAME); 211 if (nick) { 212 s->set_nick(nick->BodyText()); 213 } 214 } 215} 216 217void PresencePushTask::FillMucStatus(const Jid& from, const XmlElement* stanza, 218 MucPresenceStatus* s) { 219 FillStatus(from, stanza, s); 220} 221 222} 223