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#include "talk/xmpp/xmpptask.h"
29#include "talk/xmpp/xmppclient.h"
30#include "talk/xmpp/xmppengine.h"
31#include "talk/xmpp/constants.h"
32#include "talk/xmpp/ratelimitmanager.h"
33
34namespace buzz {
35
36RateLimitManager task_rate_manager;
37
38XmppTask::XmppTask(TaskParent* parent, XmppEngine::HandlerLevel level)
39    : Task(parent), client_(NULL) {
40#ifdef _DEBUG
41  debug_force_timeout_ = false;
42#endif
43
44  XmppClient* client =
45      static_cast<XmppClient*>(parent->GetParent(XMPP_CLIENT_TASK_CODE));
46  client_ = client;
47  id_ = client->NextId();
48  client->AddXmppTask(this, level);
49  client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect);
50}
51
52XmppTask::~XmppTask() {
53  StopImpl();
54}
55
56void XmppTask::StopImpl() {
57  while (NextStanza() != NULL) {}
58  if (client_) {
59    client_->RemoveXmppTask(this);
60    client_->SignalDisconnected.disconnect(this);
61    client_ = NULL;
62  }
63}
64
65XmppReturnStatus XmppTask::SendStanza(const XmlElement* stanza) {
66  if (client_ == NULL)
67    return XMPP_RETURN_BADSTATE;
68  return client_->SendStanza(stanza);
69}
70
71XmppReturnStatus XmppTask::SendStanzaError(const XmlElement* element_original,
72                                           XmppStanzaError code,
73                                           const std::string& text) {
74  if (client_ == NULL)
75    return XMPP_RETURN_BADSTATE;
76  return client_->SendStanzaError(element_original, code, text);
77}
78
79void XmppTask::Stop() {
80  StopImpl();
81  Task::Stop();
82}
83
84void XmppTask::OnDisconnect() {
85  Error();
86}
87
88void XmppTask::QueueStanza(const XmlElement* stanza) {
89#ifdef _DEBUG
90  if (debug_force_timeout_)
91    return;
92#endif
93
94  stanza_queue_.push_back(new XmlElement(*stanza));
95  Wake();
96}
97
98const XmlElement* XmppTask::NextStanza() {
99  XmlElement* result = NULL;
100  if (!stanza_queue_.empty()) {
101    result = stanza_queue_.front();
102    stanza_queue_.pop_front();
103  }
104  next_stanza_.reset(result);
105  return result;
106}
107
108XmlElement* XmppTask::MakeIq(const std::string& type,
109                             const buzz::Jid& to,
110                             const std::string& id) {
111  XmlElement* result = new XmlElement(QN_IQ);
112  if (!type.empty())
113    result->AddAttr(QN_TYPE, type);
114  if (to != JID_EMPTY)
115    result->AddAttr(QN_TO, to.Str());
116  if (!id.empty())
117    result->AddAttr(QN_ID, id);
118  return result;
119}
120
121XmlElement* XmppTask::MakeIqResult(const XmlElement * query) {
122  XmlElement* result = new XmlElement(QN_IQ);
123  result->AddAttr(QN_TYPE, STR_RESULT);
124  if (query->HasAttr(QN_FROM)) {
125    result->AddAttr(QN_TO, query->Attr(QN_FROM));
126  }
127  result->AddAttr(QN_ID, query->Attr(QN_ID));
128  return result;
129}
130
131bool XmppTask::MatchResponseIq(const XmlElement* stanza,
132                               const Jid& to,
133                               const std::string& id) {
134  if (stanza->Name() != QN_IQ)
135    return false;
136
137  if (stanza->Attr(QN_ID) != id)
138    return false;
139
140  Jid from(stanza->Attr(QN_FROM));
141  if (from == to)
142    return true;
143
144  // We address the server as "", check if we are doing so here.
145  if (to != JID_EMPTY)
146    return false;
147
148  // It is legal for the server to identify itself with "domain" or
149  // "myself@domain"
150  Jid me = client_->jid();
151  return (from == Jid(me.domain())) || (from == me.BareJid());
152}
153
154bool XmppTask::MatchRequestIq(const XmlElement* stanza,
155                              const std::string& type,
156                              const QName& qn) {
157  if (stanza->Name() != QN_IQ)
158    return false;
159
160  if (stanza->Attr(QN_TYPE) != type)
161    return false;
162
163  if (stanza->FirstNamed(qn) == NULL)
164    return false;
165
166  return true;
167}
168
169bool XmppTask::VerifyTaskRateLimit(const std::string task_name, int max_count,
170                                   int per_x_seconds) {
171  return task_rate_manager.VerifyRateLimit(task_name, max_count,
172                                           per_x_seconds);
173}
174
175}
176