Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / cast_channel / cast_channel_api.cc
1 // Copyright 2013 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 "chrome/browser/extensions/api/cast_channel/cast_channel_api.h"
6
7 #include <limits>
8
9 #include "base/json/json_writer.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h"
15 #include "chrome/browser/net/chrome_net_log.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "extensions/browser/event_router.h"
18 #include "net/base/ip_endpoint.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/net_util.h"
21 #include "url/gurl.h"
22
23 namespace extensions {
24
25 namespace Close = cast_channel::Close;
26 namespace OnError = cast_channel::OnError;
27 namespace OnMessage = cast_channel::OnMessage;
28 namespace Open = cast_channel::Open;
29 namespace Send = cast_channel::Send;
30 using cast_channel::CastSocket;
31 using cast_channel::ChannelAuthType;
32 using cast_channel::ChannelError;
33 using cast_channel::ChannelInfo;
34 using cast_channel::ConnectInfo;
35 using cast_channel::MessageInfo;
36 using cast_channel::ReadyState;
37 using content::BrowserThread;
38
39 namespace {
40
41 // T is an extension dictionary (MessageInfo or ChannelInfo)
42 template <class T>
43 std::string ParamToString(const T& info) {
44   scoped_ptr<base::DictionaryValue> dict = info.ToValue();
45   std::string out;
46   base::JSONWriter::Write(dict.get(), &out);
47   return out;
48 }
49
50 // Fills |channel_info| from the destination and state of |socket|.
51 void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) {
52   DCHECK(channel_info);
53   channel_info->channel_id = socket.id();
54   channel_info->url = socket.CastUrl();
55   const net::IPEndPoint& ip_endpoint = socket.ip_endpoint();
56   channel_info->connect_info.ip_address = ip_endpoint.ToStringWithoutPort();
57   channel_info->connect_info.port = ip_endpoint.port();
58   channel_info->connect_info.auth = socket.channel_auth();
59   channel_info->ready_state = socket.ready_state();
60   channel_info->error_state = socket.error_state();
61 }
62
63 }  // namespace
64
65 CastChannelAPI::CastChannelAPI(content::BrowserContext* context)
66     : browser_context_(context) {
67   DCHECK(browser_context_);
68 }
69
70 // static
71 CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) {
72   return BrowserContextKeyedAPIFactory<CastChannelAPI>::Get(context);
73 }
74
75 static base::LazyInstance<BrowserContextKeyedAPIFactory<CastChannelAPI> >
76     g_factory = LAZY_INSTANCE_INITIALIZER;
77
78 // static
79 BrowserContextKeyedAPIFactory<CastChannelAPI>*
80 CastChannelAPI::GetFactoryInstance() {
81   return g_factory.Pointer();
82 }
83
84 scoped_ptr<CastSocket> CastChannelAPI::CreateCastSocket(
85     const std::string& extension_id, const net::IPEndPoint& ip_endpoint,
86     ChannelAuthType channel_auth) {
87   if (socket_for_test_.get()) {
88     return socket_for_test_.Pass();
89   } else {
90     return scoped_ptr<CastSocket>(
91         new CastSocket(extension_id, ip_endpoint, channel_auth, this,
92                        g_browser_process->net_log()));
93   }
94 }
95
96 void CastChannelAPI::SetSocketForTest(scoped_ptr<CastSocket> socket_for_test) {
97   socket_for_test_ = socket_for_test.Pass();
98 }
99
100 void CastChannelAPI::OnError(const CastSocket* socket,
101                              cast_channel::ChannelError error) {
102   DCHECK_CURRENTLY_ON(BrowserThread::IO);
103   ChannelInfo channel_info;
104   FillChannelInfo(*socket, &channel_info);
105   channel_info.error_state = error;
106   scoped_ptr<base::ListValue> results = OnError::Create(channel_info);
107   scoped_ptr<Event> event(new Event(OnError::kEventName, results.Pass()));
108   extensions::EventRouter::Get(browser_context_)
109       ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
110 }
111
112 void CastChannelAPI::OnMessage(const CastSocket* socket,
113                                const MessageInfo& message_info) {
114   DCHECK_CURRENTLY_ON(BrowserThread::IO);
115   ChannelInfo channel_info;
116   FillChannelInfo(*socket, &channel_info);
117   scoped_ptr<base::ListValue> results =
118     OnMessage::Create(channel_info, message_info);
119   VLOG(1) << "Sending message " << ParamToString(message_info)
120           << " to channel " << ParamToString(channel_info);
121   scoped_ptr<Event> event(new Event(OnMessage::kEventName, results.Pass()));
122   extensions::EventRouter::Get(browser_context_)
123       ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass());
124 }
125
126 CastChannelAPI::~CastChannelAPI() {}
127
128 CastChannelAsyncApiFunction::CastChannelAsyncApiFunction()
129   : manager_(NULL), error_(cast_channel::CHANNEL_ERROR_NONE) { }
130
131 CastChannelAsyncApiFunction::~CastChannelAsyncApiFunction() { }
132
133 bool CastChannelAsyncApiFunction::PrePrepare() {
134   manager_ = ApiResourceManager<CastSocket>::Get(browser_context());
135   return true;
136 }
137
138 bool CastChannelAsyncApiFunction::Respond() {
139   return error_ != cast_channel::CHANNEL_ERROR_NONE;
140 }
141
142 CastSocket* CastChannelAsyncApiFunction::GetSocketOrCompleteWithError(
143     int channel_id) {
144   CastSocket* socket = GetSocket(channel_id);
145   if (!socket) {
146     SetResultFromError(cast_channel::CHANNEL_ERROR_INVALID_CHANNEL_ID);
147     AsyncWorkCompleted();
148   }
149   return socket;
150 }
151
152 int CastChannelAsyncApiFunction::AddSocket(CastSocket* socket) {
153   DCHECK_CURRENTLY_ON(BrowserThread::IO);
154   DCHECK(socket);
155   DCHECK(manager_);
156   const int id = manager_->Add(socket);
157   socket->set_id(id);
158   return id;
159 }
160
161 void CastChannelAsyncApiFunction::RemoveSocket(int channel_id) {
162   DCHECK_CURRENTLY_ON(BrowserThread::IO);
163   DCHECK(manager_);
164   manager_->Remove(extension_->id(), channel_id);
165 }
166
167 void CastChannelAsyncApiFunction::SetResultFromSocket(int channel_id) {
168   CastSocket* socket = GetSocket(channel_id);
169   DCHECK(socket);
170   ChannelInfo channel_info;
171   FillChannelInfo(*socket, &channel_info);
172   error_ = socket->error_state();
173   SetResultFromChannelInfo(channel_info);
174 }
175
176 void CastChannelAsyncApiFunction::SetResultFromError(ChannelError error) {
177   ChannelInfo channel_info;
178   channel_info.channel_id = -1;
179   channel_info.url = "";
180   channel_info.ready_state = cast_channel::READY_STATE_CLOSED;
181   channel_info.error_state = error;
182   SetResultFromChannelInfo(channel_info);
183   error_ = error;
184 }
185
186 CastSocket* CastChannelAsyncApiFunction::GetSocket(int channel_id) {
187   DCHECK_CURRENTLY_ON(BrowserThread::IO);
188   DCHECK(manager_);
189   return manager_->Get(extension_->id(), channel_id);
190 }
191
192 void CastChannelAsyncApiFunction::SetResultFromChannelInfo(
193     const ChannelInfo& channel_info) {
194   DCHECK_CURRENTLY_ON(BrowserThread::IO);
195   SetResult(channel_info.ToValue().release());
196 }
197
198 CastChannelOpenFunction::CastChannelOpenFunction()
199   : new_channel_id_(0) { }
200
201 CastChannelOpenFunction::~CastChannelOpenFunction() { }
202
203 // TODO(mfoltz): Remove URL parsing when clients have converted to use
204 // ConnectInfo.
205
206 // Allowed schemes for Cast device URLs.
207 const char kCastInsecureScheme[] = "cast";
208 const char kCastSecureScheme[] = "casts";
209
210 bool CastChannelOpenFunction::ParseChannelUrl(const GURL& url,
211                                               ConnectInfo* connect_info) {
212   DCHECK(connect_info);
213   VLOG(2) << "ParseChannelUrl";
214   bool auth_required = false;
215   if (url.SchemeIs(kCastSecureScheme)) {
216     auth_required = true;
217   } else if (!url.SchemeIs(kCastInsecureScheme)) {
218     return false;
219   }
220   // TODO(mfoltz): Test for IPv6 addresses.  Brackets or no brackets?
221   // TODO(mfoltz): Maybe enforce restriction to IPv4 private and IPv6
222   // link-local networks
223   const std::string& path = url.path();
224   // Shortest possible: //A:B
225   if (path.size() < 5) {
226     return false;
227   }
228   if (path.find("//") != 0) {
229     return false;
230   }
231   size_t colon = path.find_last_of(':');
232   if (colon == std::string::npos || colon < 3 || colon > path.size() - 2) {
233     return false;
234   }
235   const std::string& ip_address_str = path.substr(2, colon - 2);
236   const std::string& port_str = path.substr(colon + 1);
237   VLOG(2) << "IP: " << ip_address_str << " Port: " << port_str;
238   int port;
239   if (!base::StringToInt(port_str, &port))
240     return false;
241   connect_info->ip_address = ip_address_str;
242   connect_info->port = port;
243   connect_info->auth = auth_required ?
244     cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED :
245     cast_channel::CHANNEL_AUTH_TYPE_SSL;
246   return true;
247 };
248
249 net::IPEndPoint* CastChannelOpenFunction::ParseConnectInfo(
250     const ConnectInfo& connect_info) {
251   net::IPAddressNumber ip_address;
252   if (!net::ParseIPLiteralToNumber(connect_info.ip_address, &ip_address)) {
253     return NULL;
254   }
255   if (connect_info.port < 0 || connect_info.port >
256       std::numeric_limits<unsigned short>::max()) {
257     return NULL;
258   }
259   if (connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL_VERIFIED &&
260       connect_info.auth != cast_channel::CHANNEL_AUTH_TYPE_SSL) {
261     return NULL;
262   }
263   return new net::IPEndPoint(ip_address, connect_info.port);
264 }
265
266 bool CastChannelOpenFunction::PrePrepare() {
267   api_ = CastChannelAPI::Get(browser_context());
268   return CastChannelAsyncApiFunction::PrePrepare();
269 }
270
271 bool CastChannelOpenFunction::Prepare() {
272   params_ = Open::Params::Create(*args_);
273   EXTENSION_FUNCTION_VALIDATE(params_.get());
274   // The connect_info parameter may be a string URL like cast:// or casts:// or
275   // a ConnectInfo object.
276   std::string cast_url;
277   switch (params_->connect_info->GetType()) {
278     case base::Value::TYPE_STRING:
279       CHECK(params_->connect_info->GetAsString(&cast_url));
280       connect_info_.reset(new ConnectInfo);
281       if (!ParseChannelUrl(GURL(cast_url), connect_info_.get())) {
282         connect_info_.reset();
283       }
284       break;
285     case base::Value::TYPE_DICTIONARY:
286       connect_info_ = ConnectInfo::FromValue(*(params_->connect_info));
287       break;
288     default:
289       break;
290   }
291   if (connect_info_.get()) {
292     channel_auth_ = connect_info_->auth;
293     ip_endpoint_.reset(ParseConnectInfo(*connect_info_));
294     return ip_endpoint_.get() != NULL;
295   }
296   return false;
297 }
298
299 void CastChannelOpenFunction::AsyncWorkStart() {
300   DCHECK(api_);
301   DCHECK(ip_endpoint_.get());
302   scoped_ptr<CastSocket> socket = api_->CreateCastSocket(
303       extension_->id(), *ip_endpoint_, channel_auth_);
304   new_channel_id_ = AddSocket(socket.release());
305   GetSocket(new_channel_id_)->Connect(
306       base::Bind(&CastChannelOpenFunction::OnOpen, this));
307 }
308
309 void CastChannelOpenFunction::OnOpen(int result) {
310   DCHECK_CURRENTLY_ON(BrowserThread::IO);
311   SetResultFromSocket(new_channel_id_);
312   AsyncWorkCompleted();
313 }
314
315 CastChannelSendFunction::CastChannelSendFunction() { }
316
317 CastChannelSendFunction::~CastChannelSendFunction() { }
318
319 bool CastChannelSendFunction::Prepare() {
320   params_ = Send::Params::Create(*args_);
321   EXTENSION_FUNCTION_VALIDATE(params_.get());
322   return true;
323 }
324
325 void CastChannelSendFunction::AsyncWorkStart() {
326   CastSocket* socket = GetSocketOrCompleteWithError(
327       params_->channel.channel_id);
328   if (socket)
329     socket->SendMessage(params_->message,
330                         base::Bind(&CastChannelSendFunction::OnSend, this));
331 }
332
333 void CastChannelSendFunction::OnSend(int result) {
334   DCHECK_CURRENTLY_ON(BrowserThread::IO);
335   if (result < 0) {
336     SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
337   } else {
338     SetResultFromSocket(params_->channel.channel_id);
339   }
340   AsyncWorkCompleted();
341 }
342
343 CastChannelCloseFunction::CastChannelCloseFunction() { }
344
345 CastChannelCloseFunction::~CastChannelCloseFunction() { }
346
347 bool CastChannelCloseFunction::Prepare() {
348   params_ = Close::Params::Create(*args_);
349   EXTENSION_FUNCTION_VALIDATE(params_.get());
350   return true;
351 }
352
353 void CastChannelCloseFunction::AsyncWorkStart() {
354   CastSocket* socket = GetSocketOrCompleteWithError(
355       params_->channel.channel_id);
356   if (socket)
357     socket->Close(base::Bind(&CastChannelCloseFunction::OnClose, this));
358 }
359
360 void CastChannelCloseFunction::OnClose(int result) {
361   DCHECK_CURRENTLY_ON(BrowserThread::IO);
362   VLOG(1) << "CastChannelCloseFunction::OnClose result = " << result;
363   if (result < 0) {
364     SetResultFromError(cast_channel::CHANNEL_ERROR_SOCKET_ERROR);
365   } else {
366     int channel_id = params_->channel.channel_id;
367     SetResultFromSocket(channel_id);
368     RemoveSocket(channel_id);
369   }
370   AsyncWorkCompleted();
371 }
372
373 }  // namespace extensions