1 /*
2  * libjingle
3  * Copyright 2004--2006, 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_XMPP_XMPPTASK_H_
29 #define TALK_XMPP_XMPPTASK_H_
30 
31 #include <string>
32 #include <deque>
33 #include "talk/base/sigslot.h"
34 #include "talk/base/task.h"
35 #include "talk/base/taskparent.h"
36 #include "talk/xmpp/xmppengine.h"
37 
38 namespace buzz {
39 
40 /////////////////////////////////////////////////////////////////////
41 //
42 // XMPPTASK
43 //
44 /////////////////////////////////////////////////////////////////////
45 //
46 // See Task and XmppClient first.
47 //
48 // XmppTask is a task that is designed to go underneath XmppClient and be
49 // useful there.  It has a way of finding its XmppClient parent so you
50 // can have it nested arbitrarily deep under an XmppClient and it can
51 // still find the XMPP services.
52 //
53 // Tasks register themselves to listen to particular kinds of stanzas
54 // that are sent out by the client.  Rather than processing stanzas
55 // right away, they should decide if they own the sent stanza,
56 // and if so, queue it and Wake() the task, or if a stanza does not belong
57 // to you, return false right away so the next XmppTask can take a crack.
58 // This technique (synchronous recognize, but asynchronous processing)
59 // allows you to have arbitrary logic for recognizing stanzas yet still,
60 // for example, disconnect a client while processing a stanza -
61 // without reentrancy problems.
62 //
63 /////////////////////////////////////////////////////////////////////
64 
65 class XmppTask;
66 
67 // XmppClientInterface is an abstract interface for sending and
68 // handling stanzas.  It can be implemented for unit tests or
69 // different network environments.  It will usually be implemented by
70 // XmppClient.
71 class XmppClientInterface {
72  public:
73   XmppClientInterface();
74   virtual ~XmppClientInterface();
75 
76   virtual XmppEngine::State GetState() const = 0;
77   virtual const Jid& jid() const = 0;
78   virtual std::string NextId() = 0;
79   virtual XmppReturnStatus SendStanza(const XmlElement* stanza) = 0;
80   virtual XmppReturnStatus SendStanzaError(const XmlElement* original_stanza,
81                                            XmppStanzaError error_code,
82                                            const std::string& message) = 0;
83   virtual void AddXmppTask(XmppTask* task, XmppEngine::HandlerLevel level) = 0;
84   virtual void RemoveXmppTask(XmppTask* task) = 0;
85   sigslot::signal0<> SignalDisconnected;
86 
87   DISALLOW_EVIL_CONSTRUCTORS(XmppClientInterface);
88 };
89 
90 // XmppTaskParentInterface is the interface require for any parent of
91 // an XmppTask.  It needs, for example, a way to get an
92 // XmppClientInterface.
93 
94 // We really ought to inherit from a TaskParentInterface, but we tried
95 // that and it's way too complicated to change
96 // Task/TaskParent/TaskRunner.  For now, this works.
97 class XmppTaskParentInterface : public talk_base::Task {
98  public:
XmppTaskParentInterface(talk_base::TaskParent * parent)99   explicit XmppTaskParentInterface(talk_base::TaskParent* parent)
100       : Task(parent) {
101   }
~XmppTaskParentInterface()102   virtual ~XmppTaskParentInterface() {}
103 
104   virtual XmppClientInterface* GetClient() = 0;
105 
106   DISALLOW_EVIL_CONSTRUCTORS(XmppTaskParentInterface);
107 };
108 
109 class XmppTaskBase : public XmppTaskParentInterface {
110  public:
XmppTaskBase(XmppTaskParentInterface * parent)111   explicit XmppTaskBase(XmppTaskParentInterface* parent)
112       : XmppTaskParentInterface(parent),
113         parent_(parent) {
114   }
~XmppTaskBase()115   virtual ~XmppTaskBase() {}
116 
GetClient()117   virtual XmppClientInterface* GetClient() {
118     return parent_->GetClient();
119   }
120 
121  protected:
122   XmppTaskParentInterface* parent_;
123 
124   DISALLOW_EVIL_CONSTRUCTORS(XmppTaskBase);
125 };
126 
127 class XmppTask : public XmppTaskBase,
128                  public XmppStanzaHandler,
129                  public sigslot::has_slots<>
130 {
131  public:
132   XmppTask(XmppTaskParentInterface* parent,
133            XmppEngine::HandlerLevel level = XmppEngine::HL_NONE);
134   virtual ~XmppTask();
135 
task_id()136   std::string task_id() const { return id_; }
set_task_id(std::string id)137   void set_task_id(std::string id) { id_ = id; }
138 
139 #ifdef _DEBUG
set_debug_force_timeout(const bool f)140   void set_debug_force_timeout(const bool f) { debug_force_timeout_ = f; }
141 #endif
142 
HandleStanza(const XmlElement * stanza)143   virtual bool HandleStanza(const XmlElement* stanza) { return false; }
144 
145  protected:
146   XmppReturnStatus SendStanza(const XmlElement* stanza);
147   XmppReturnStatus SetResult(const std::string& code);
148   XmppReturnStatus SendStanzaError(const XmlElement* element_original,
149                                    XmppStanzaError code,
150                                    const std::string& text);
151 
152   virtual void Stop();
153   virtual void OnDisconnect();
154 
155   virtual void QueueStanza(const XmlElement* stanza);
156   const XmlElement* NextStanza();
157 
158   bool MatchStanzaFrom(const XmlElement* stanza, const Jid& match_jid);
159 
160   bool MatchResponseIq(const XmlElement* stanza, const Jid& to,
161                        const std::string& task_id);
162 
163   static bool MatchRequestIq(const XmlElement* stanza, const std::string& type,
164                              const QName& qn);
165   static XmlElement *MakeIqResult(const XmlElement* query);
166   static XmlElement *MakeIq(const std::string& type,
167                             const Jid& to, const std::string& task_id);
168 
169   // Returns true if the task is under the specified rate limit and updates the
170   // rate limit accordingly
171   bool VerifyTaskRateLimit(const std::string task_name, int max_count,
172                            int per_x_seconds);
173 
174 private:
175   void StopImpl();
176 
177   bool stopped_;
178   std::deque<XmlElement*> stanza_queue_;
179   talk_base::scoped_ptr<XmlElement> next_stanza_;
180   std::string id_;
181 
182 #ifdef _DEBUG
183   bool debug_force_timeout_;
184 #endif
185 };
186 
187 }  // namespace buzz
188 
189 #endif // TALK_XMPP_XMPPTASK_H_
190