Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / httpfs / http_fs.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 "nacl_io/httpfs/http_fs.h"
6
7 #include <assert.h>
8 #include <ctype.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15
16 #include <vector>
17
18 #include <ppapi/c/pp_errors.h>
19
20 #include "nacl_io/dir_node.h"
21 #include "nacl_io/httpfs/http_fs_node.h"
22 #include "nacl_io/kernel_handle.h"
23 #include "nacl_io/log.h"
24 #include "nacl_io/osinttypes.h"
25 #include "nacl_io/osunistd.h"
26 #include "sdk_util/string_util.h"
27
28 namespace nacl_io {
29
30 std::string NormalizeHeaderKey(const std::string& s) {
31   // Capitalize the first letter and any letter following a hyphen:
32   // e.g. ACCEPT-ENCODING -> Accept-Encoding
33   std::string result;
34   bool upper = true;
35   for (size_t i = 0; i < s.length(); ++i) {
36     char c = s[i];
37     result += upper ? toupper(c) : tolower(c);
38     upper = c == '-';
39   }
40
41   return result;
42 }
43
44 Error HttpFs::Access(const Path& path, int a_mode) {
45   NodeMap_t::iterator iter = node_cache_.find(path.Join());
46   if (iter == node_cache_.end()) {
47     // If we can't find the node in the cache, fetch it
48     std::string url = MakeUrl(path);
49     ScopedNode node(new HttpFsNode(this, url, cache_content_));
50     Error error = node->Init(0);
51     if (error)
52       return error;
53
54     error = node->GetStat(NULL);
55     if (error)
56       return error;
57   }
58
59   // Don't allow write or execute access.
60   if (a_mode & (W_OK | X_OK))
61     return EACCES;
62
63   return 0;
64 }
65
66 Error HttpFs::Open(const Path& path, int open_flags, ScopedNode* out_node) {
67   out_node->reset(NULL);
68
69   NodeMap_t::iterator iter = node_cache_.find(path.Join());
70   if (iter != node_cache_.end()) {
71     *out_node = iter->second;
72     return 0;
73   }
74
75   // If we can't find the node in the cache, create it
76   std::string url = MakeUrl(path);
77   ScopedNode node(new HttpFsNode(this, url, cache_content_));
78   Error error = node->Init(open_flags);
79   if (error)
80     return error;
81
82   error = node->GetStat(NULL);
83   if (error)
84     return error;
85
86   ScopedNode parent;
87   error = FindOrCreateDir(path.Parent(), &parent);
88   if (error)
89     return error;
90
91   error = parent->AddChild(path.Basename(), node);
92   if (error)
93     return error;
94
95   node_cache_[path.Join()] = node;
96   *out_node = node;
97   return 0;
98 }
99
100 Error HttpFs::Unlink(const Path& path) {
101   NodeMap_t::iterator iter = node_cache_.find(path.Join());
102   if (iter == node_cache_.end())
103     return ENOENT;
104
105   if (iter->second->IsaDir())
106     return EISDIR;
107
108   return EACCES;
109 }
110
111 Error HttpFs::Mkdir(const Path& path, int permissions) {
112   NodeMap_t::iterator iter = node_cache_.find(path.Join());
113   if (iter != node_cache_.end()) {
114     if (iter->second->IsaDir())
115       return EEXIST;
116   }
117   return EACCES;
118 }
119
120 Error HttpFs::Rmdir(const Path& path) {
121   NodeMap_t::iterator iter = node_cache_.find(path.Join());
122   if (iter == node_cache_.end())
123     return ENOENT;
124
125   if (!iter->second->IsaDir())
126     return ENOTDIR;
127
128   return EACCES;
129 }
130
131 Error HttpFs::Remove(const Path& path) {
132   NodeMap_t::iterator iter = node_cache_.find(path.Join());
133   if (iter == node_cache_.end())
134     return ENOENT;
135
136   return EACCES;
137 }
138
139 Error HttpFs::Rename(const Path& path, const Path& newpath) {
140   NodeMap_t::iterator iter = node_cache_.find(path.Join());
141   if (iter == node_cache_.end())
142     return ENOENT;
143
144   return EACCES;
145 }
146
147 PP_Resource HttpFs::MakeUrlRequestInfo(const std::string& url,
148                                        const char* method,
149                                        StringMap_t* additional_headers) {
150   URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface();
151   VarInterface* var_interface = ppapi_->GetVarInterface();
152
153   PP_Resource request_info = interface->Create(ppapi_->GetInstance());
154   if (!request_info)
155     return 0;
156
157   interface->SetProperty(request_info,
158                          PP_URLREQUESTPROPERTY_URL,
159                          var_interface->VarFromUtf8(url.c_str(), url.length()));
160   interface->SetProperty(request_info,
161                          PP_URLREQUESTPROPERTY_METHOD,
162                          var_interface->VarFromUtf8(method, strlen(method)));
163   interface->SetProperty(request_info,
164                          PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS,
165                          PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE));
166   interface->SetProperty(request_info,
167                          PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS,
168                          PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE));
169
170   // Merge the filesystem headers with the request headers. If the field is
171   // already set it |additional_headers|, don't use the one from headers_.
172   for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end();
173        ++iter) {
174     const std::string& key = NormalizeHeaderKey(iter->first);
175     if (additional_headers->find(key) == additional_headers->end()) {
176       additional_headers->insert(std::make_pair(key, iter->second));
177     }
178   }
179
180   // Join the headers into one string.
181   std::string headers;
182   for (StringMap_t::iterator iter = additional_headers->begin();
183        iter != additional_headers->end();
184        ++iter) {
185     headers += iter->first + ": " + iter->second + '\n';
186   }
187
188   interface->SetProperty(
189       request_info,
190       PP_URLREQUESTPROPERTY_HEADERS,
191       var_interface->VarFromUtf8(headers.c_str(), headers.length()));
192
193   return request_info;
194 }
195
196 HttpFs::HttpFs()
197     : allow_cors_(false),
198       allow_credentials_(false),
199       cache_stat_(true),
200       cache_content_(true) {
201 }
202
203 Error HttpFs::Init(const FsInitArgs& args) {
204   Error error = Filesystem::Init(args);
205   if (error)
206     return error;
207
208   // Parse filesystem args.
209   for (StringMap_t::const_iterator iter = args.string_map.begin();
210        iter != args.string_map.end();
211        ++iter) {
212     if (iter->first == "SOURCE") {
213       url_root_ = iter->second;
214       is_blob_url_ = strncmp(url_root_.c_str(), "blob:", 5) == 0;
215
216       // Make sure url_root_ ends with a slash, except for blob URLs.
217       if (!is_blob_url_ && !url_root_.empty() &&
218           url_root_[url_root_.length() - 1] != '/') {
219         url_root_ += '/';
220       }
221     } else if (iter->first == "manifest") {
222       char* text;
223       error = LoadManifest(iter->second, &text);
224       if (error)
225         return error;
226
227       error = ParseManifest(text);
228       if (error) {
229         free(text);
230         return error;
231       }
232
233       free(text);
234     } else if (iter->first == "allow_cross_origin_requests") {
235       allow_cors_ = iter->second == "true";
236     } else if (iter->first == "allow_credentials") {
237       allow_credentials_ = iter->second == "true";
238     } else if (iter->first == "cache_stat") {
239       cache_stat_ = iter->second == "true";
240     } else if (iter->first == "cache_content") {
241       cache_content_ = iter->second == "true";
242     } else {
243       // Assume it is a header to pass to an HTTP request.
244       headers_[NormalizeHeaderKey(iter->first)] = iter->second;
245     }
246   }
247
248   return 0;
249 }
250
251 void HttpFs::Destroy() {
252 }
253
254 Error HttpFs::FindOrCreateDir(const Path& path, ScopedNode* out_node) {
255   out_node->reset(NULL);
256   std::string strpath = path.Join();
257   NodeMap_t::iterator iter = node_cache_.find(strpath);
258   if (iter != node_cache_.end()) {
259     *out_node = iter->second;
260     return 0;
261   }
262
263   // If the node does not exist, create it.
264   ScopedNode node(new DirNode(this));
265   Error error = node->Init(0);
266   if (error)
267     return error;
268
269   // If not the root node, find the parent node and add it to the parent
270   if (!path.IsRoot()) {
271     ScopedNode parent;
272     error = FindOrCreateDir(path.Parent(), &parent);
273     if (error)
274       return error;
275
276     error = parent->AddChild(path.Basename(), node);
277     if (error)
278       return error;
279   }
280
281   // Add it to the node cache.
282   node_cache_[strpath] = node;
283   *out_node = node;
284   return 0;
285 }
286
287 Error HttpFs::ParseManifest(const char* text) {
288   std::vector<std::string> lines;
289   sdk_util::SplitString(text, '\n', &lines);
290
291   for (size_t i = 0; i < lines.size(); i++) {
292     std::vector<std::string> words;
293     sdk_util::SplitString(lines[i], ' ', &words);
294
295     // Remove empty words (due to multiple consecutive spaces).
296     std::vector<std::string> non_empty_words;
297     for (std::vector<std::string>::const_iterator it = words.begin();
298          it != words.end();
299          ++it) {
300       if (!it->empty())
301         non_empty_words.push_back(*it);
302     }
303
304     if (non_empty_words.size() == 3) {
305       const std::string& modestr = non_empty_words[0];
306       const std::string& lenstr = non_empty_words[1];
307       const std::string& name = non_empty_words[2];
308
309       assert(modestr.size() == 4);
310       assert(name[0] == '/');
311
312       // Only support regular and streams for now
313       // Ignore EXEC bit
314       int mode = S_IFREG;
315       switch (modestr[0]) {
316         case '-':
317           mode = S_IFREG;
318           break;
319         case 'c':
320           mode = S_IFCHR;
321           break;
322         default:
323           LOG_ERROR("Unable to parse type %s for %s.",
324                     modestr.c_str(),
325                     name.c_str());
326           return EINVAL;
327       }
328
329       switch (modestr[1]) {
330         case '-':
331           break;
332         case 'r':
333           mode |= S_IRUSR | S_IRGRP | S_IROTH;
334           break;
335         default:
336           LOG_ERROR("Unable to parse read %s for %s.",
337                     modestr.c_str(),
338                     name.c_str());
339           return EINVAL;
340       }
341
342       switch (modestr[2]) {
343         case '-':
344           break;
345         case 'w':
346           mode |= S_IWUSR | S_IWGRP | S_IWOTH;
347           break;
348         default:
349           LOG_ERROR("Unable to parse write %s for %s.",
350                     modestr.c_str(),
351                     name.c_str());
352           return EINVAL;
353       }
354
355       Path path(name);
356       std::string url = MakeUrl(path);
357
358       HttpFsNode* http_node = new HttpFsNode(this, url, cache_content_);
359       http_node->SetMode(mode);
360       ScopedNode node(http_node);
361
362       Error error = node->Init(0);
363       if (error)
364         return error;
365       http_node->SetCachedSize(atoi(lenstr.c_str()));
366
367       ScopedNode dir_node;
368       error = FindOrCreateDir(path.Parent(), &dir_node);
369       if (error)
370         return error;
371
372       error = dir_node->AddChild(path.Basename(), node);
373       if (error)
374         return error;
375
376       std::string pname = path.Join();
377       node_cache_[pname] = node;
378     }
379   }
380
381   return 0;
382 }
383
384 Error HttpFs::LoadManifest(const std::string& manifest_name,
385                            char** out_manifest) {
386   Path manifest_path(manifest_name);
387   ScopedNode manifest_node;
388   *out_manifest = NULL;
389
390   int error = Open(manifest_path, O_RDONLY, &manifest_node);
391   if (error)
392     return error;
393
394   off_t size;
395   error = manifest_node->GetSize(&size);
396   if (error)
397     return error;
398
399   char* text = (char*)malloc(size + 1);
400   assert(text != NULL);
401   if (text == NULL)
402     return ENOMEM;
403   int len;
404   error = manifest_node->Read(HandleAttr(), text, size, &len);
405   if (error)
406     return error;
407
408   text[len] = 0;
409   *out_manifest = text;
410   return 0;
411 }
412
413 std::string HttpFs::MakeUrl(const Path& path) {
414   return url_root_ +
415          (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join());
416 }
417
418 }  // namespace nacl_io