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 "rostertask.h" 29 #include "talk/xmpp/constants.h" 30 #include "talk/base/stream.h" 31 32 #undef WIN32 33 #ifdef WIN32 34 #include "talk/app/win32/offlineroster.h" 35 #endif 36 37 namespace buzz { 38 39 class RosterTask::RosterGetTask : public XmppTask { 40 public: 41 RosterGetTask(Task * parent) : XmppTask(parent, XmppEngine::HL_SINGLE), 42 done_(false) {} 43 44 virtual int ProcessStart(); 45 virtual int ProcessResponse(); 46 47 protected: 48 virtual bool HandleStanza(const XmlElement * stanza); 49 50 bool done_; 51 }; 52 53 //============================================================================== 54 // RosterTask 55 //============================================================================== 56 void RosterTask::RefreshRosterNow() { 57 RosterGetTask* get_task = new RosterGetTask(this); 58 ResumeTimeout(); 59 get_task->Start(); 60 } 61 62 void RosterTask::TranslateItems(const XmlElement * rosterQueryResult) { 63 #if defined(FEATURE_ENABLE_PSTN) 64 #ifdef WIN32 65 // We build up a list of contacts which have had information persisted offline. 66 // we'll remove items from this list if we get a buzz::SUBSCRIBE_REMOVE 67 // subscription. After updating all the items from the server, we'll then 68 // update (and merge) any roster items left in our map of offline items 69 XmlElement *el_local = OfflineRoster::RetrieveOfflineRoster(GetClient()->jid()); 70 std::map<buzz::Jid, RosterItem> jid_to_item; 71 if (el_local) { 72 for (XmlElement *el_item = el_local->FirstNamed(QN_ROSTER_ITEM); 73 el_item != NULL; 74 el_item = el_item->NextNamed(QN_ROSTER_ITEM)) { 75 RosterItem roster_item; 76 roster_item.FromXml(el_item); 77 78 jid_to_item[roster_item.jid()] = roster_item; 79 } 80 } 81 #endif // WIN32 82 #endif // FEATURE_ENABLE_PSTN 83 84 const XmlElement * xml_item; 85 for (xml_item = rosterQueryResult->FirstNamed(QN_ROSTER_ITEM); 86 xml_item != NULL; xml_item = xml_item->NextNamed(QN_ROSTER_ITEM)) { 87 RosterItem roster_item; 88 roster_item.FromXml(xml_item); 89 90 if (roster_item.subscription() == buzz::SUBSCRIBE_REMOVE) { 91 SignalRosterItemRemoved(roster_item); 92 93 #if defined(FEATURE_ENABLE_PSTN) 94 #ifdef WIN32 95 std::map<buzz::Jid, RosterItem>::iterator it = 96 jid_to_item.find(roster_item.jid()); 97 98 if (it != jid_to_item.end()) 99 jid_to_item.erase(it); 100 #endif 101 #endif 102 } else { 103 SignalRosterItemUpdated(roster_item, false); 104 } 105 } 106 107 #if defined(FEATURE_ENABLE_PSTN) 108 #ifdef WIN32 109 for (std::map<buzz::Jid, RosterItem>::iterator it = jid_to_item.begin(); 110 it != jid_to_item.end(); ++it) { 111 SignalRosterItemUpdated(it->second, true); 112 } 113 #endif 114 #endif 115 } 116 117 int RosterTask::ProcessStart() { 118 const XmlElement * stanza = NextStanza(); 119 if (stanza == NULL) 120 return STATE_BLOCKED; 121 122 if (stanza->Name() == QN_IQ) { 123 SuspendTimeout(); 124 bool result = (stanza->Attr(QN_TYPE) == STR_RESULT); 125 if (result) 126 SignalRosterRefreshStarted(); 127 128 TranslateItems(stanza->FirstNamed(QN_ROSTER_QUERY)); 129 130 if (result) 131 SignalRosterRefreshFinished(); 132 } else if (stanza->Name() == QN_PRESENCE) { 133 Jid jid(stanza->Attr(QN_FROM)); 134 std::string type = stanza->Attr(QN_TYPE); 135 if (type == "subscribe") 136 SignalSubscribe(jid); 137 else if (type == "unsubscribe") 138 SignalUnsubscribe(jid); 139 else if (type == "subscribed") 140 SignalSubscribed(jid); 141 else if (type == "unsubscribed") 142 SignalUnsubscribed(jid); 143 } 144 145 return STATE_START; 146 } 147 148 bool RosterTask::HandleStanza(const XmlElement * stanza) { 149 if (!MatchRequestIq(stanza, STR_SET, QN_ROSTER_QUERY)) { 150 // Not a roster IQ. Look for a presence instead 151 if (stanza->Name() != QN_PRESENCE) 152 return false; 153 if (!stanza->HasAttr(QN_TYPE)) 154 return false; 155 std::string type = stanza->Attr(QN_TYPE); 156 if (type == "subscribe" || type == "unsubscribe" || 157 type == "subscribed" || type == "unsubscribed") { 158 QueueStanza(stanza); 159 return true; 160 } 161 return false; 162 } 163 164 // only respect roster push from the server 165 Jid from(stanza->Attr(QN_FROM)); 166 if (from != JID_EMPTY && 167 !from.BareEquals(GetClient()->jid()) && 168 from != Jid(GetClient()->jid().domain())) 169 return false; 170 171 XmlElement * result = MakeIqResult(stanza); 172 result->AddElement(new XmlElement(QN_ROSTER_QUERY, true)); 173 SendStanza(result); 174 175 QueueStanza(stanza); 176 return true; 177 } 178 179 180 //============================================================================== 181 // RosterTask::RosterGetTask 182 //============================================================================== 183 int RosterTask::RosterGetTask::ProcessStart() { 184 talk_base::scoped_ptr<XmlElement> get(MakeIq(STR_GET, JID_EMPTY, task_id())); 185 get->AddElement(new XmlElement(QN_ROSTER_QUERY, true)); 186 get->AddAttr(QN_XMLNS_GR, NS_GR, 1); 187 get->AddAttr(QN_GR_EXT, "2", 1); 188 get->AddAttr(QN_GR_INCLUDE, "all", 1); 189 if (SendStanza(get.get()) != XMPP_RETURN_OK) { 190 return STATE_ERROR; 191 } 192 return STATE_RESPONSE; 193 } 194 195 int RosterTask::RosterGetTask::ProcessResponse() { 196 if (done_) 197 return STATE_DONE; 198 return STATE_BLOCKED; 199 } 200 201 bool RosterTask::RosterGetTask::HandleStanza(const XmlElement * stanza) { 202 if (!MatchResponseIq(stanza, JID_EMPTY, task_id())) 203 return false; 204 205 if (stanza->Attr(QN_TYPE) != STR_RESULT) 206 return false; 207 208 // Queue the stanza with the parent so these don't get handled out of order 209 RosterTask* parent = static_cast<RosterTask*>(GetParent()); 210 parent->QueueStanza(stanza); 211 212 // Wake ourselves so we can go into the done state 213 done_ = true; 214 Wake(); 215 return true; 216 } 217 218 } 219