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