• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "xmppclient.h"
29 #include "xmpptask.h"
30 #include "talk/xmpp/constants.h"
31 #include "talk/base/sigslot.h"
32 #include "talk/xmpp/saslplainmechanism.h"
33 #include "talk/xmpp/prexmppauth.h"
34 #include "talk/base/scoped_ptr.h"
35 #include "talk/xmpp/plainsaslhandler.h"
36 
37 namespace buzz {
38 
GetParent(int code)39 talk_base::TaskParent* XmppClient::GetParent(int code) {
40   if (code == XMPP_CLIENT_TASK_CODE)
41     return this;
42   else
43     return talk_base::Task::GetParent(code);
44 }
45 
46 class XmppClient::Private :
47     public sigslot::has_slots<>,
48     public XmppSessionHandler,
49     public XmppOutputHandler {
50 public:
51 
Private(XmppClient * client)52   Private(XmppClient * client) :
53     client_(client),
54     socket_(NULL),
55     engine_(NULL),
56     proxy_port_(0),
57     pre_engine_error_(XmppEngine::ERROR_NONE),
58     pre_engine_subcode_(0),
59     signal_closed_(false),
60     allow_plain_(false) {}
61 
62   // the owner
63   XmppClient * const client_;
64 
65   // the two main objects
66   talk_base::scoped_ptr<AsyncSocket> socket_;
67   talk_base::scoped_ptr<XmppEngine> engine_;
68   talk_base::scoped_ptr<PreXmppAuth> pre_auth_;
69   talk_base::CryptString pass_;
70   std::string auth_cookie_;
71   talk_base::SocketAddress server_;
72   std::string proxy_host_;
73   int proxy_port_;
74   XmppEngine::Error pre_engine_error_;
75   int pre_engine_subcode_;
76   CaptchaChallenge captcha_challenge_;
77   bool signal_closed_;
78   bool allow_plain_;
79 
80   // implementations of interfaces
81   void OnStateChange(int state);
82   void WriteOutput(const char * bytes, size_t len);
83   void StartTls(const std::string & domainname);
84   void CloseConnection();
85 
86   // slots for socket signals
87   void OnSocketConnected();
88   void OnSocketRead();
89   void OnSocketClosed();
90 };
91 
92 XmppReturnStatus
Connect(const XmppClientSettings & settings,const std::string & lang,AsyncSocket * socket,PreXmppAuth * pre_auth)93 XmppClient::Connect(const XmppClientSettings & settings, const std::string & lang, AsyncSocket * socket, PreXmppAuth * pre_auth) {
94   if (socket == NULL)
95     return XMPP_RETURN_BADARGUMENT;
96   if (d_->socket_.get() != NULL)
97     return XMPP_RETURN_BADSTATE;
98 
99   d_->socket_.reset(socket);
100 
101   d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected);
102   d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead);
103   d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed);
104 
105   d_->engine_.reset(XmppEngine::Create());
106   d_->engine_->SetSessionHandler(d_.get());
107   d_->engine_->SetOutputHandler(d_.get());
108   if (!settings.resource().empty()) {
109     d_->engine_->SetRequestedResource(settings.resource());
110   }
111   d_->engine_->SetUseTls(settings.use_tls());
112 
113   //
114   // The talk.google.com server expects you to use "gmail.com" in the
115   // stream, and expects the domain certificate to be "gmail.com" as well.
116   // For all other servers, we leave the strings empty, which causes
117   // the jid's domain to be used.  "foo@example.com" -> stream to="example.com"
118   // tls certificate for "example.com"
119   //
120   // This is only true when using Gaia auth, so let's say if there's no preauth,
121   // we should use the actual server name
122   std::string server_name = settings.server().IPAsString();
123   if ((server_name == buzz::STR_TALK_GOOGLE_COM ||
124       server_name == buzz::STR_TALKX_L_GOOGLE_COM) &&
125       pre_auth != NULL) {
126     d_->engine_->SetTlsServer(buzz::STR_GMAIL_COM, buzz::STR_GMAIL_COM);
127   }
128 
129   // Set language
130   d_->engine_->SetLanguage(lang);
131 
132   d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY));
133 
134   d_->pass_ = settings.pass();
135   d_->auth_cookie_ = settings.auth_cookie();
136   d_->server_ = settings.server();
137   d_->proxy_host_ = settings.proxy_host();
138   d_->proxy_port_ = settings.proxy_port();
139   d_->allow_plain_ = settings.allow_plain();
140   d_->pre_auth_.reset(pre_auth);
141 
142   return XMPP_RETURN_OK;
143 }
144 
145 XmppEngine::State
GetState()146 XmppClient::GetState() {
147   if (d_->engine_.get() == NULL)
148     return XmppEngine::STATE_NONE;
149   return d_->engine_->GetState();
150 }
151 
152 XmppEngine::Error
GetError(int * subcode)153 XmppClient::GetError(int *subcode) {
154   if (subcode) {
155     *subcode = 0;
156   }
157   if (d_->engine_.get() == NULL)
158     return XmppEngine::ERROR_NONE;
159   if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) {
160     if (subcode) {
161       *subcode = d_->pre_engine_subcode_;
162     }
163     return d_->pre_engine_error_;
164   }
165   return d_->engine_->GetError(subcode);
166 }
167 
168 const XmlElement *
GetStreamError()169 XmppClient::GetStreamError() {
170   if (d_->engine_.get() == NULL) {
171     return NULL;
172   }
173   return d_->engine_->GetStreamError();
174 }
175 
GetCaptchaChallenge()176 CaptchaChallenge XmppClient::GetCaptchaChallenge() {
177   if (d_->engine_.get() == NULL)
178     return CaptchaChallenge();
179   return d_->captcha_challenge_;
180 }
181 
182 std::string
GetAuthCookie()183 XmppClient::GetAuthCookie() {
184   if (d_->engine_.get() == NULL)
185     return "";
186   return d_->auth_cookie_;
187 }
188 
189 int
ProcessStart()190 XmppClient::ProcessStart() {
191   if (d_->pre_auth_.get()) {
192     d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone);
193     d_->pre_auth_->StartPreXmppAuth(
194         d_->engine_->GetUser(), d_->server_, d_->pass_, d_->auth_cookie_);
195     d_->pass_.Clear(); // done with this;
196     return STATE_PRE_XMPP_LOGIN;
197   }
198   else {
199     d_->engine_->SetSaslHandler(new PlainSaslHandler(
200               d_->engine_->GetUser(), d_->pass_, d_->allow_plain_));
201     d_->pass_.Clear(); // done with this;
202     return STATE_START_XMPP_LOGIN;
203   }
204 }
205 
206 void
OnAuthDone()207 XmppClient::OnAuthDone() {
208   Wake();
209 }
210 
211 int
ProcessCookieLogin()212 XmppClient::ProcessCookieLogin() {
213   // Don't know how this could happen, but crash reports show it as NULL
214   if (!d_->pre_auth_.get()) {
215     d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
216     EnsureClosed();
217     return STATE_ERROR;
218   }
219 
220   // Wait until pre authentication is done is done
221   if (!d_->pre_auth_->IsAuthDone())
222     return STATE_BLOCKED;
223 
224   if (!d_->pre_auth_->IsAuthorized()) {
225     // maybe split out a case when gaia is down?
226     if (d_->pre_auth_->HadError()) {
227       d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
228       d_->pre_engine_subcode_ = d_->pre_auth_->GetError();
229     }
230     else {
231       d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED;
232       d_->pre_engine_subcode_ = 0;
233       d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge();
234     }
235     d_->pre_auth_.reset(NULL); // done with this
236     EnsureClosed();
237     return STATE_ERROR;
238   }
239 
240   // Save auth cookie as a result
241   d_->auth_cookie_ = d_->pre_auth_->GetAuthCookie();
242 
243   // transfer ownership of pre_auth_ to engine
244   d_->engine_->SetSaslHandler(d_->pre_auth_.release());
245   return STATE_START_XMPP_LOGIN;
246 }
247 
248 int
ProcessStartXmppLogin()249 XmppClient::ProcessStartXmppLogin() {
250   // Done with pre-connect tasks - connect!
251   if (!d_->socket_->Connect(d_->server_)) {
252     EnsureClosed();
253     return STATE_ERROR;
254   }
255 
256   return STATE_RESPONSE;
257 }
258 
259 int
ProcessResponse()260 XmppClient::ProcessResponse() {
261   // Hang around while we are connected.
262   if (!delivering_signal_ && (d_->engine_.get() == NULL ||
263     d_->engine_->GetState() == XmppEngine::STATE_CLOSED))
264     return STATE_DONE;
265   return STATE_BLOCKED;
266 }
267 
268 XmppReturnStatus
Disconnect()269 XmppClient::Disconnect() {
270   if (d_->socket_.get() == NULL)
271     return XMPP_RETURN_BADSTATE;
272   d_->engine_->Disconnect();
273   d_->socket_.reset(NULL);
274   return XMPP_RETURN_OK;
275 }
276 
XmppClient(TaskParent * parent)277 XmppClient::XmppClient(TaskParent * parent)
278     : Task(parent),
279       delivering_signal_(false),
280       valid_(false) {
281   d_.reset(new Private(this));
282   valid_ = true;
283 }
284 
~XmppClient()285 XmppClient::~XmppClient() {
286   valid_ = false;
287 }
288 
289 const Jid &
jid()290 XmppClient::jid() {
291   return d_->engine_->FullJid();
292 }
293 
294 
295 std::string
NextId()296 XmppClient::NextId() {
297   return d_->engine_->NextId();
298 }
299 
300 XmppReturnStatus
SendStanza(const XmlElement * stanza)301 XmppClient::SendStanza(const XmlElement * stanza) {
302   return d_->engine_->SendStanza(stanza);
303 }
304 
305 XmppReturnStatus
SendStanzaError(const XmlElement * old_stanza,XmppStanzaError xse,const std::string & message)306 XmppClient::SendStanzaError(const XmlElement * old_stanza, XmppStanzaError xse, const std::string & message) {
307   return d_->engine_->SendStanzaError(old_stanza, xse, message);
308 }
309 
310 XmppReturnStatus
SendRaw(const std::string & text)311 XmppClient::SendRaw(const std::string & text) {
312   return d_->engine_->SendRaw(text);
313 }
314 
315 XmppEngine*
engine()316 XmppClient::engine() {
317   return d_->engine_.get();
318 }
319 
320 void
OnSocketConnected()321 XmppClient::Private::OnSocketConnected() {
322   engine_->Connect();
323 }
324 
325 void
OnSocketRead()326 XmppClient::Private::OnSocketRead() {
327   char bytes[4096];
328   size_t bytes_read;
329   for (;;) {
330     if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) {
331       // TODO: deal with error information
332       return;
333     }
334 
335     if (bytes_read == 0)
336       return;
337 
338 //#ifdef _DEBUG
339     client_->SignalLogInput(bytes, bytes_read);
340 //#endif
341 
342     engine_->HandleInput(bytes, bytes_read);
343   }
344 }
345 
346 void
OnSocketClosed()347 XmppClient::Private::OnSocketClosed() {
348   int code = socket_->GetError();
349   engine_->ConnectionClosed(code);
350 }
351 
352 void
OnStateChange(int state)353 XmppClient::Private::OnStateChange(int state) {
354   if (state == XmppEngine::STATE_CLOSED) {
355     client_->EnsureClosed();
356   }
357   else {
358     client_->SignalStateChange((XmppEngine::State)state);
359   }
360   client_->Wake();
361 }
362 
363 void
WriteOutput(const char * bytes,size_t len)364 XmppClient::Private::WriteOutput(const char * bytes, size_t len) {
365 
366 //#ifdef _DEBUG
367   client_->SignalLogOutput(bytes, len);
368 //#endif
369 
370   socket_->Write(bytes, len);
371   // TODO: deal with error information
372 }
373 
374 void
StartTls(const std::string & domain)375 XmppClient::Private::StartTls(const std::string & domain) {
376 #if defined(FEATURE_ENABLE_SSL)
377   socket_->StartTls(domain);
378 #endif
379 }
380 
381 void
CloseConnection()382 XmppClient::Private::CloseConnection() {
383   socket_->Close();
384 }
385 
386 void
AddXmppTask(XmppTask * task,XmppEngine::HandlerLevel level)387 XmppClient::AddXmppTask(XmppTask * task, XmppEngine::HandlerLevel level) {
388   d_->engine_->AddStanzaHandler(task, level);
389 }
390 
391 void
RemoveXmppTask(XmppTask * task)392 XmppClient::RemoveXmppTask(XmppTask * task) {
393   d_->engine_->RemoveStanzaHandler(task);
394 }
395 
396 void
EnsureClosed()397 XmppClient::EnsureClosed() {
398   if (!d_->signal_closed_) {
399     d_->signal_closed_ = true;
400     delivering_signal_ = true;
401     SignalStateChange(XmppEngine::STATE_CLOSED);
402     delivering_signal_ = false;
403   }
404 }
405 
406 
407 }
408