1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "jingle/notifier/base/xmpp_connection.h"
6
7#include "base/compiler_specific.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop.h"
10#include "base/strings/string_piece.h"
11#include "jingle/glue/chrome_async_socket.h"
12#include "jingle/glue/task_pump.h"
13#include "jingle/glue/xmpp_client_socket_factory.h"
14#include "jingle/notifier/base/weak_xmpp_client.h"
15#include "net/socket/client_socket_factory.h"
16#include "net/ssl/ssl_config_service.h"
17#include "net/url_request/url_request_context.h"
18#include "talk/xmpp/xmppclientsettings.h"
19
20namespace notifier {
21
22XmppConnection::Delegate::~Delegate() {}
23
24namespace {
25
26buzz::AsyncSocket* CreateSocket(
27    const buzz::XmppClientSettings& xmpp_client_settings,
28    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) {
29  bool use_fake_ssl_client_socket =
30      (xmpp_client_settings.protocol() == cricket::PROTO_SSLTCP);
31  // The default SSLConfig is good enough for us for now.
32  const net::SSLConfig ssl_config;
33  // These numbers were taken from similar numbers in
34  // XmppSocketAdapter.
35  const size_t kReadBufSize = 64U * 1024U;
36  const size_t kWriteBufSize = 64U * 1024U;
37  jingle_glue::XmppClientSocketFactory* const client_socket_factory =
38      new jingle_glue::XmppClientSocketFactory(
39          net::ClientSocketFactory::GetDefaultFactory(),
40          ssl_config,
41          request_context_getter,
42          use_fake_ssl_client_socket);
43  return new jingle_glue::ChromeAsyncSocket(client_socket_factory,
44                                            kReadBufSize, kWriteBufSize);
45}
46
47}  // namespace
48
49XmppConnection::XmppConnection(
50    const buzz::XmppClientSettings& xmpp_client_settings,
51    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
52    Delegate* delegate,
53    buzz::PreXmppAuth* pre_xmpp_auth)
54    : task_pump_(new jingle_glue::TaskPump()),
55      on_connect_called_(false),
56      delegate_(delegate) {
57  DCHECK(delegate_);
58  // Owned by |task_pump_|, but is guaranteed to live at least as long
59  // as this function.
60  WeakXmppClient* weak_xmpp_client = new WeakXmppClient(task_pump_.get());
61  weak_xmpp_client->SignalStateChange.connect(
62      this, &XmppConnection::OnStateChange);
63  weak_xmpp_client->SignalLogInput.connect(
64      this, &XmppConnection::OnInputLog);
65  weak_xmpp_client->SignalLogOutput.connect(
66      this, &XmppConnection::OnOutputLog);
67  const char kLanguage[] = "en";
68  buzz::XmppReturnStatus connect_status =
69      weak_xmpp_client->Connect(xmpp_client_settings, kLanguage,
70                                CreateSocket(xmpp_client_settings,
71                                             request_context_getter),
72                                pre_xmpp_auth);
73  // buzz::XmppClient::Connect() should never fail.
74  DCHECK_EQ(connect_status, buzz::XMPP_RETURN_OK);
75  weak_xmpp_client->Start();
76  weak_xmpp_client_ = weak_xmpp_client->AsWeakPtr();
77}
78
79XmppConnection::~XmppConnection() {
80  DCHECK(CalledOnValidThread());
81  ClearClient();
82  task_pump_->Stop();
83  base::MessageLoop* current_message_loop = base::MessageLoop::current();
84  CHECK(current_message_loop);
85  // We do this because XmppConnection may get destroyed as a result
86  // of a signal from XmppClient.  If we delete |task_pump_| here, bad
87  // things happen when the stack pops back up to the XmppClient's
88  // (which is deleted by |task_pump_|) function.
89  current_message_loop->DeleteSoon(FROM_HERE, task_pump_.release());
90}
91
92void XmppConnection::OnStateChange(buzz::XmppEngine::State state) {
93  DCHECK(CalledOnValidThread());
94  VLOG(1) << "XmppClient state changed to " << state;
95  if (!weak_xmpp_client_.get()) {
96    LOG(DFATAL) << "weak_xmpp_client_ unexpectedly NULL";
97    return;
98  }
99  if (!delegate_) {
100    LOG(DFATAL) << "delegate_ unexpectedly NULL";
101    return;
102  }
103  switch (state) {
104    case buzz::XmppEngine::STATE_OPEN:
105      if (on_connect_called_) {
106        LOG(DFATAL) << "State changed to STATE_OPEN more than once";
107      } else {
108        delegate_->OnConnect(weak_xmpp_client_);
109        on_connect_called_ = true;
110      }
111      break;
112    case buzz::XmppEngine::STATE_CLOSED: {
113      int subcode = 0;
114      buzz::XmppEngine::Error error =
115          weak_xmpp_client_->GetError(&subcode);
116      const buzz::XmlElement* stream_error =
117          weak_xmpp_client_->GetStreamError();
118      ClearClient();
119      Delegate* delegate = delegate_;
120      delegate_ = NULL;
121      delegate->OnError(error, subcode, stream_error);
122      break;
123    }
124    default:
125      // Do nothing.
126      break;
127  }
128}
129
130void XmppConnection::OnInputLog(const char* data, int len) {
131  DCHECK(CalledOnValidThread());
132  VLOG(2) << "XMPP Input: " << base::StringPiece(data, len);
133}
134
135void XmppConnection::OnOutputLog(const char* data, int len) {
136  DCHECK(CalledOnValidThread());
137  VLOG(2) << "XMPP Output: " << base::StringPiece(data, len);
138}
139
140void XmppConnection::ClearClient() {
141  if (weak_xmpp_client_.get()) {
142    weak_xmpp_client_->Invalidate();
143    DCHECK(!weak_xmpp_client_.get());
144  }
145}
146
147}  // namespace notifier
148