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 #ifndef TALK_P2P_CLIENT_SESSIONSENDTASK_H_ 29 #define TALK_P2P_CLIENT_SESSIONSENDTASK_H_ 30 31 #include "talk/base/common.h" 32 #include "talk/xmpp/constants.h" 33 #include "talk/xmpp/xmppclient.h" 34 #include "talk/xmpp/xmppengine.h" 35 #include "talk/xmpp/xmpptask.h" 36 #include "talk/p2p/base/sessionmanager.h" 37 38 namespace cricket { 39 40 // The job of this task is to send an IQ stanza out (after stamping it with 41 // an ID attribute) and then wait for a response. If not response happens 42 // within 5 seconds, it will signal failure on a SessionManager. If an error 43 // happens it will also signal failure. If, however, the send succeeds this 44 // task will quietly go away. 45 46 class SessionSendTask : public buzz::XmppTask { 47 public: 48 SessionSendTask(buzz::XmppTaskParentInterface* parent, 49 SessionManager* session_manager) 50 : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE), 51 session_manager_(session_manager) { 52 set_timeout_seconds(15); 53 session_manager_->SignalDestroyed.connect( 54 this, &SessionSendTask::OnSessionManagerDestroyed); 55 } 56 57 virtual ~SessionSendTask() { 58 SignalDone(this); 59 } 60 61 void Send(const buzz::XmlElement* stanza) { 62 ASSERT(stanza_.get() == NULL); 63 64 // This should be an IQ of type set, result, or error. In the first case, 65 // we supply an ID. In the others, it should be present. 66 ASSERT(stanza->Name() == buzz::QN_IQ); 67 ASSERT(stanza->HasAttr(buzz::QN_TYPE)); 68 if (stanza->Attr(buzz::QN_TYPE) == "set") { 69 ASSERT(!stanza->HasAttr(buzz::QN_ID)); 70 } else { 71 ASSERT((stanza->Attr(buzz::QN_TYPE) == "result") || 72 (stanza->Attr(buzz::QN_TYPE) == "error")); 73 ASSERT(stanza->HasAttr(buzz::QN_ID)); 74 } 75 76 stanza_.reset(new buzz::XmlElement(*stanza)); 77 if (stanza_->HasAttr(buzz::QN_ID)) { 78 set_task_id(stanza_->Attr(buzz::QN_ID)); 79 } else { 80 stanza_->SetAttr(buzz::QN_ID, task_id()); 81 } 82 } 83 84 void OnSessionManagerDestroyed() { 85 // If the session manager doesn't exist anymore, we should still try to 86 // send the message, but avoid calling back into the SessionManager. 87 session_manager_ = NULL; 88 } 89 90 sigslot::signal1<SessionSendTask *> SignalDone; 91 92 protected: 93 virtual int OnTimeout() { 94 if (session_manager_ != NULL) { 95 session_manager_->OnFailedSend(stanza_.get(), NULL); 96 } 97 98 return XmppTask::OnTimeout(); 99 } 100 101 virtual int ProcessStart() { 102 SendStanza(stanza_.get()); 103 if (stanza_->Attr(buzz::QN_TYPE) == buzz::STR_SET) { 104 return STATE_RESPONSE; 105 } else { 106 return STATE_DONE; 107 } 108 } 109 110 virtual int ProcessResponse() { 111 const buzz::XmlElement* next = NextStanza(); 112 if (next == NULL) 113 return STATE_BLOCKED; 114 115 if (session_manager_ != NULL) { 116 if (next->Attr(buzz::QN_TYPE) == buzz::STR_RESULT) { 117 session_manager_->OnIncomingResponse(stanza_.get(), next); 118 } else { 119 session_manager_->OnFailedSend(stanza_.get(), next); 120 } 121 } 122 123 return STATE_DONE; 124 } 125 126 virtual bool HandleStanza(const buzz::XmlElement *stanza) { 127 if (!MatchResponseIq(stanza, 128 buzz::Jid(stanza_->Attr(buzz::QN_TO)), task_id())) 129 return false; 130 if (stanza->Attr(buzz::QN_TYPE) == buzz::STR_RESULT || 131 stanza->Attr(buzz::QN_TYPE) == buzz::STR_ERROR) { 132 QueueStanza(stanza); 133 return true; 134 } 135 return false; 136 } 137 138 private: 139 SessionManager *session_manager_; 140 talk_base::scoped_ptr<buzz::XmlElement> stanza_; 141 }; 142 143 } 144 145 #endif // TALK_P2P_CLIENT_SESSIONSENDTASK_H_ 146