Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / extensions / browser / api / cast_channel / cast_channel_api.cc
1 // Copyright 2014 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 "extensions/browser/api/cast_channel/cast_channel_api.h"
6
7 #include <limits>
8 #include <string>
9
10 #include "base/json/json_writer.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/default_tick_clock.h"
14 #include "base/values.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "extensions/browser/api/cast_channel/cast_socket.h"
17 #include "extensions/browser/api/cast_channel/logger.h"
18 #include "extensions/browser/api/cast_channel/logging.pb.h"
19 #include "extensions/browser/event_router.h"
20 #include "net/base/ip_endpoint.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/net_util.h"
23 #include "url/gurl.h"
24
25 // Default timeout interval for connection setup.
26 // Used if not otherwise specified at ConnectInfo::timeout.
27 const int kDefaultConnectTimeoutMillis = 5000;  // 5 seconds.
28
29 namespace extensions {
30
31 namespace Close = cast_channel::Close;
32 namespace OnError = cast_channel::OnError;
33 namespace OnMessage = cast_channel::OnMessage;
34 namespace Open = cast_channel::Open;
35 namespace Send = cast_channel::Send;
36 using cast_channel::CastSocket;
37 using cast_channel::ChannelAuthType;
38 using cast_channel::ChannelError;
39 using cast_channel::ChannelInfo;
40 using cast_channel::ConnectInfo;
41 using cast_channel::ErrorInfo;
42 using cast_channel::LastErrors;
43 using cast_channel::Logger;
44 using cast_channel::MessageInfo;
45 using cast_channel::ReadyState;
46 using content::BrowserThread;
47
48 namespace {
49
50 // T is an extension dictionary (MessageInfo or ChannelInfo)
51 template <class T>
52 std::string ParamToString(const T& info) {
53   scoped_ptr<base::DictionaryValue> dict = info.ToValue();
54   std::string out;
55   base::JSONWriter::Write(dict.get(), &out);
56   return out;
57 }
58
59 // Fills |channel_info| from the destination and state of |socket|.
60 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
61   DCHECK(channel_info);
62   channel_info->channel_id = socket.id();
63   channel_info->url = socket.CastUrl();
64   const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
65   channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
66   channel_info->connect_info.port = ip_endpoint.port();
67   channel_info->connect_info.auth = socket.channel_auth();
68   channel_info->ready_state = socket.ready_state();
69   channel_info->error_state = socket.error_state();
70 }
71
72 // Fills |error_info| from |error_state| and |last_errors|.
73 void FillErrorInfo(ChannelError error_state,
74                    const LastErrors& last_errors,
75                    ErrorInfo* error_info) {
76   error_info->error_state = error_state;
77   if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN)
78     error_info->event_type.reset(new int(last_errors.event_type));
79   if (last_errors.challenge_reply_error_type !=
80       cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) {
81     error_info->challenge_reply_error_type.reset(
82         new int(last_errors.challenge_reply_error_type));
83   }
84   if (last_errors.net_return_value <= 0)
85     error_info->net_return_value.reset(new int(last_errors.net_return_value));
86   if (last_errors.nss_error_code < 0)
87     error_info->nss_error_code.reset(new int(last_errors.nss_error_code));
88 }
89
90 bool IsValidConnectInfoPort(const ConnectInfo& connect_info) {
91   return connect_info.port > 0 && connect_info.port <
92     std::numeric_limits<uint16_t>::max();
93 }
94
95 bool IsValidConnectInfoAuth(const ConnectInfo& connect_info) {
96   return connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED ||
97     connect_info.auth == cast_channel::CHANNEL_AUTH_TYPE_SSL;
98 }
99
100 bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) {
101   net::IPAddressNumber ip_address;
102   return net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address);
103 }
104
105 }  // namespace
106
107 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
108     : browser_context_(context),
109       logger_(
110           new Logger(scoped_ptr<base::TickClock>(new base::DefaultTickClock),
111                      base::TimeTicks::UnixEpoch())) {
112   DCHECK(browser_context_);
113 }
114
115 // static
116 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
117   return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
118 }
119
120 scoped_refptr<Logger> CastChannelAPI::GetLogger() {
121   return logger_;
122 }
123
124 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
125     g_factory = LAZY_INSTANCE_INITIALIZER;
126
127 // static
128 BrowserContextKeyedAPIFactory<CastChannelAPI>*
129 CastChannelAPI::GetFactoryInstance() {
130   return g_factory.Pointer();
131 }
132
133 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket(
134     const std::string& extension_id, const net::IPEndPoint& ip_endpoint,
135     ChannelAuthType channel_auth, const base::TimeDelta& timeout) {
136   if (socket_for_test_.get()) {
137     return socket_for_test_.Pass();
138   } else {
139     return scoped_ptr<CastSocket>(
140         new CastSocket(extension_id,
141                        ip_endpoint,
142                        channel_auth,
143                        this,
144                        ExtensionsBrowserClient::Get()->GetNetLog(),
145                        timeout,
146                        logger_));
147   }
148 }
149
150 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
151   socket_for_test_ = socket_for_test.Pass();
152 }
153
154 void CastChannelAPI::OnError(const CastSocket* socket,
155                              cast_channel::ChannelError error_state,
156                              const cast_channel::LastErrors& last_errors) {
157   DCHECK_CURRENTLY_ON(BrowserThread::IO);
158   ChannelInfo channel_info;
159   FillChannelInfo(*socket, &channel_info);
160   channel_info.error_state = error_state;
161   ErrorInfo error_info;
162   FillErrorInfo(error_state, last_errors, &error_info);
163   scoped_ptr<base::ListValue> results =
164       OnError::Create(channel_info, error_info);
165   scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
166   extensions::EventRouter::Get(browser_context_)
167       ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
168 }
169
170 void CastChannelAPI::OnMessage(const CastSocket* socket,
171                                const MessageInfo& message_info) {
172   DCHECK_CURRENTLY_ON(BrowserThread::IO);
173   ChannelInfo channel_info;
174   FillChannelInfo(*socket, &channel_info);
175   scoped_ptr<base::ListValue> results =
176     OnMessage::Create(channel_info, message_info);
177   VLOG(1) << "Sending message " << ParamToString(message_info)
178           << " to channel " << ParamToString(channel_info);
179   scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
180   extensions::EventRouter::Get(browser_context_)
181       ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
182 }
183
184 CastChannelAPI::~CastChannelAPI() {}
185
186 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
187   : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { }
188
189 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
190
191 bool CastChannelAsyncApiFunction::PrePrepare() {
192   manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
193   return true;
194 }
195
196 bool CastChannelAsyncApiFunction::Respond() {
197   return error_ != cast_channel::CHANNEL_ERROR_NONE;
198 }
199
200 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
201     int channel_id) {
202   CastSocket* socket = GetSocket(channel_id);
203   if (!socket) {
204     SetResultFromError(channel_id,
205                        cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
206     AsyncWorkCompleted();
207   }
208   return socket;
209 }
210
211 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
212   DCHECK_CURRENTLY_ON(BrowserThread::IO);
213   DCHECK(socket);
214   DCHECK(manager_);
215   const int id = manager_->Add(socket);
216   socket->set_id(id);
217   return id;
218 }
219
220 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
221   DCHECK_CURRENTLY_ON(BrowserThread::IO);
222   DCHECK(manager_);
223   manager_->Remove(extension_->id(), channel_id);
224 }
225
226 void CastChannelAsyncApiFunction::SetResultFromSocket(
227     const CastSocket& socket) {
228   ChannelInfo channel_info;
229   FillChannelInfo(socket, &channel_info);
230   error_ = socket.error_state();
231   SetResultFromChannelInfo(channel_info);
232 }
233
234 void CastChannelAsyncApiFunction::SetResultFromError(int channel_id,
235                                                      ChannelError error) {
236   ChannelInfo channel_info;
237   channel_info.channel_id = channel_id;
238   channel_info.url = "";
239   channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
240   channel_info.error_state = error;
241   channel_info.connect_info.ip_address = "";
242   channel_info.connect_info.port = 0;
243   channel_info.connect_info.auth = cast_channel::CHANNEL_AUTH_TYPE_SSL;
244   SetResultFromChannelInfo(channel_info);
245   error_ = error;
246 }
247
248 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) {
249   DCHECK_CURRENTLY_ON(BrowserThread::IO);
250   DCHECK(manager_);
251   return manager_->Get(extension_->id(), channel_id);
252 }
253
254 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
255     const ChannelInfo& channel_info) {
256   DCHECK_CURRENTLY_ON(BrowserThread::IO);
257   SetResult(channel_info.ToValue().release());
258 }
259
260 CastChannelOpenFunction::CastChannelOpenFunction()
261   : new_channel_id_(0) { }
262
263 CastChannelOpenFunction::~CastChannelOpenFunction() { }
264
265 // TODO(mfoltz): Remove URL parsing when clients have converted to use
266 // ConnectInfo.
267
268 // Allowed schemes for Cast device URLs.
269 const char kCastInsecureScheme[] = "cast";
270 const char kCastSecureScheme[] = "casts";
271
272 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
273                                               ConnectInfo* connect_info) {
274   DCHECK(connect_info);
275   VLOG(2) << "ParseChannelUrl";
276   bool auth_required = false;
277   if (url.SchemeIs(kCastSecureScheme)) {
278     auth_required = true;
279   } else if (!url.SchemeIs(kCastInsecureScheme)) {
280     return false;
281   }
282   // TODO(mfoltz): Test for IPv6 addresses.  Brackets or no brackets?
283   // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
284   // link-local networks
285   const std::string& path = url.path();
286   // Shortest possible: //A:B
287   if (path.size() < 5) {
288     return false;
289   }
290   if (path.find("//") != 0) {
291     return false;
292   }
293   size_t colon = path.find_last_of(':');
294   if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
295     return false;
296   }
297   const std::string& ip_address_str = path.substr(2, colon - 2);
298   const std::string& port_str = path.substr(colon + 1);
299   VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
300   int port;
301   if (!base::StringToInt(port_str, &port))
302     return false;
303   connect_info->ip_address = ip_address_str;
304   connect_info->port = port;
305   connect_info->auth = auth_required ?
306     cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
307     cast_channel::CHANNEL_AUTH_TYPE_SSL;
308   return true;
309 }
310
311 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
312     const ConnectInfo& connect_info) {
313   net::IPAddressNumber ip_address;
314   CHECK(net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address));
315   return new net::IPEndPoint(ip_address, connect_info.port);
316 }
317
318 bool CastChannelOpenFunction::PrePrepare() {
319   api_ = CastChannelAPI::Get(browser_context());
320   return CastChannelAsyncApiFunction::PrePrepare();
321 }
322
323 bool CastChannelOpenFunction::Prepare() {
324   params_ = Open::Params::Create(*args_);
325   EXTENSION_FUNCTION_VALIDATE(params_.get());
326   // The connect_info parameter may be a string URL like cast:// or casts:// or
327   // a ConnectInfo object.
328   std::string cast_url;
329   switch (params_->connect_info->GetType()) {
330     case base::Value::TYPE_STRING:
331       CHECK(params_->connect_info->GetAsString(&cast_url));
332       connect_info_.reset(new ConnectInfo);
333       if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
334         connect_info_.reset();
335         SetError("Invalid connect_info (invalid Cast URL " + cast_url + ")");
336       }
337       break;
338     case base::Value::TYPE_DICTIONARY:
339       connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
340       if (!connect_info_.get()) {
341         SetError("connect_info.auth is required");
342       }
343       break;
344     default:
345       SetError("Invalid connect_info (unknown type)");
346       break;
347   }
348   if (!connect_info_.get()) {
349     return false;
350   }
351   if (!IsValidConnectInfoPort(*connect_info_)) {
352     SetError("Invalid connect_info (invalid port)");
353   } else if (!IsValidConnectInfoAuth(*connect_info_)) {
354     SetError("Invalid connect_info (invalid auth)");
355   } else if (!IsValidConnectInfoIpAddress(*connect_info_)) {
356     SetError("Invalid connect_info (invalid IP address)");
357   }
358   if (!GetError().empty()) {
359     return false;
360   }
361   channel_auth_ = connect_info_->auth;
362   ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
363   return true;
364 }
365
366 void CastChannelOpenFunction::AsyncWorkStart() {
367   DCHECK(api_);
368   DCHECK(ip_endpoint_.get());
369   scoped_ptr<CastSocket> socket = api_->CreateCastSocket(
370       extension_->id(),
371       *ip_endpoint_,
372       channel_auth_,
373       base::TimeDelta::FromMilliseconds(connect_info_->timeout.get()
374                                             ? *connect_info_->timeout
375                                             : kDefaultConnectTimeoutMillis));
376   new_channel_id_ = AddSocket(socket.release());
377   CastSocket* new_socket = GetSocket(new_channel_id_);
378   api_->GetLogger()->LogNewSocketEvent(*new_socket);
379   new_socket->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this));
380 }
381
382 void CastChannelOpenFunction::OnOpen(int result) {
383   DCHECK_CURRENTLY_ON(BrowserThread::IO);
384   VLOG(1) << "Connect finished, OnOpen invoked.";
385   CastSocket* socket = GetSocket(new_channel_id_);
386   if (!socket) {
387     SetResultFromError(new_channel_id_,
388                        cast_channel::CHANNEL_ERROR_CONNECT_ERROR);
389   } else {
390     SetResultFromSocket(*socket);
391   }
392   AsyncWorkCompleted();
393 }
394
395 CastChannelSendFunction::CastChannelSendFunction() { }
396
397 CastChannelSendFunction::~CastChannelSendFunction() { }
398
399 bool CastChannelSendFunction::Prepare() {
400   params_ = Send::Params::Create(*args_);
401   EXTENSION_FUNCTION_VALIDATE(params_.get());
402   if (params_->message.namespace_.empty()) {
403     SetError("message_info.namespace_ is required");
404     return false;
405   }
406   if (params_->message.source_id.empty()) {
407     SetError("message_info.source_id is required");
408     return false;
409   }
410   if (params_->message.destination_id.empty()) {
411     SetError("message_info.destination_id is required");
412     return false;
413   }
414   switch (params_->message.data->GetType()) {
415     case base::Value::TYPE_STRING:
416     case base::Value::TYPE_BINARY:
417       break;
418     default:
419       SetError("Invalid type of message_info.data");
420       return false;
421   }
422   return true;
423 }
424
425 void CastChannelSendFunction::AsyncWorkStart() {
426   CastSocket* socket = GetSocketOrCompleteWithError(
427       params_->channel.channel_id);
428   if (socket)
429     socket->SendMessage(params_->message,
430                         base::Bind(&CastChannelSendFunction::OnSend, this));
431 }
432
433 void CastChannelSendFunction::OnSend(int result) {
434   DCHECK_CURRENTLY_ON(BrowserThread::IO);
435   int channel_id = params_->channel.channel_id;
436   CastSocket* socket = GetSocket(channel_id);
437   if (result < 0 || !socket) {
438     SetResultFromError(channel_id,
439                        cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
440   } else {
441     SetResultFromSocket(*socket);
442   }
443   AsyncWorkCompleted();
444 }
445
446 CastChannelCloseFunction::CastChannelCloseFunction() { }
447
448 CastChannelCloseFunction::~CastChannelCloseFunction() { }
449
450 bool CastChannelCloseFunction::Prepare() {
451   params_ = Close::Params::Create(*args_);
452   EXTENSION_FUNCTION_VALIDATE(params_.get());
453   return true;
454 }
455
456 void CastChannelCloseFunction::AsyncWorkStart() {
457   CastSocket* socket = GetSocketOrCompleteWithError(
458       params_->channel.channel_id);
459   if (socket)
460     socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
461 }
462
463 void CastChannelCloseFunction::OnClose(int result) {
464   DCHECK_CURRENTLY_ON(BrowserThread::IO);
465   VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
466   int channel_id = params_->channel.channel_id;
467   CastSocket* socket = GetSocket(channel_id);
468   if (result < 0 || !socket) {
469     SetResultFromError(channel_id,
470                        cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
471   } else {
472     SetResultFromSocket(*socket);
473     // This will delete |socket|.
474     RemoveSocket(channel_id);
475     socket = NULL;
476   }
477   AsyncWorkCompleted();
478 }
479
480 CastChannelGetLogsFunction::CastChannelGetLogsFunction() {
481 }
482
483 CastChannelGetLogsFunction::~CastChannelGetLogsFunction() {
484 }
485
486 bool CastChannelGetLogsFunction::PrePrepare() {
487   api_ = CastChannelAPI::Get(browser_context());
488   return CastChannelAsyncApiFunction::PrePrepare();
489 }
490
491 bool CastChannelGetLogsFunction::Prepare() {
492   return true;
493 }
494
495 void CastChannelGetLogsFunction::AsyncWorkStart() {
496   DCHECK(api_);
497
498   size_t length = 0;
499   scoped_ptr<char[]> out = api_->GetLogger()->GetLogs(&length);
500   if (out.get()) {
501     SetResult(new base::BinaryValue(out.Pass(), length));
502   } else {
503     SetError("Unable to get logs.");
504   }
505
506   api_->GetLogger()->Reset();
507
508   AsyncWorkCompleted();
509 }
510
511 }  // namespace extensions