• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "net/tools/dump_cache/cache_dumper.h"
6 
7 #include "base/file_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "net/base/io_buffer.h"
10 #include "net/base/net_errors.h"
11 #include "net/disk_cache/blockfile/entry_impl.h"
12 #include "net/http/http_cache.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/http/http_response_info.h"
15 #include "net/tools/dump_cache/url_to_filename_encoder.h"
16 
CacheDumper(disk_cache::Backend * cache)17 CacheDumper::CacheDumper(disk_cache::Backend* cache)
18     : cache_(cache) {
19 }
20 
CreateEntry(const std::string & key,disk_cache::Entry ** entry,const net::CompletionCallback & callback)21 int CacheDumper::CreateEntry(const std::string& key,
22                              disk_cache::Entry** entry,
23                              const net::CompletionCallback& callback) {
24   return cache_->CreateEntry(key, entry, callback);
25 }
26 
WriteEntry(disk_cache::Entry * entry,int index,int offset,net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)27 int CacheDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
28                             net::IOBuffer* buf, int buf_len,
29                             const net::CompletionCallback& callback) {
30   return entry->WriteData(index, offset, buf, buf_len, callback, false);
31 }
32 
CloseEntry(disk_cache::Entry * entry,base::Time last_used,base::Time last_modified)33 void CacheDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
34                              base::Time last_modified) {
35   if (entry) {
36     static_cast<disk_cache::EntryImpl*>(entry)->SetTimes(last_used,
37                                                          last_modified);
38     entry->Close();
39   }
40 }
41 
42 // A version of CreateDirectory which supports lengthy filenames.
43 // Returns true on success, false on failure.
SafeCreateDirectory(const base::FilePath & path)44 bool SafeCreateDirectory(const base::FilePath& path) {
45 #ifdef WIN32_LARGE_FILENAME_SUPPORT
46   // Due to large paths on windows, it can't simply do a
47   // CreateDirectory("a/b/c").  Instead, create each subdirectory manually.
48   bool rv = false;
49   std::wstring::size_type pos(0);
50   std::wstring backslash(L"\\");
51 
52   // If the path starts with the long file header, skip over that
53   const std::wstring kLargeFilenamePrefix(L"\\\\?\\");
54   std::wstring header(kLargeFilenamePrefix);
55   if (path.value().find(header) == 0)
56     pos = 4;
57 
58   // Create the subdirectories individually
59   while ((pos = path.value().find(backslash, pos)) != std::wstring::npos) {
60     base::FilePath::StringType subdir = path.value().substr(0, pos);
61     CreateDirectoryW(subdir.c_str(), NULL);
62     // we keep going even if directory creation failed.
63     pos++;
64   }
65   // Now create the full path
66   return CreateDirectoryW(path.value().c_str(), NULL) == TRUE;
67 #else
68   return base::CreateDirectory(path);
69 #endif
70 }
71 
DiskDumper(const base::FilePath & path)72 DiskDumper::DiskDumper(const base::FilePath& path)
73     : path_(path), entry_(NULL) {
74   base::CreateDirectory(path);
75 }
76 
CreateEntry(const std::string & key,disk_cache::Entry ** entry,const net::CompletionCallback & callback)77 int DiskDumper::CreateEntry(const std::string& key,
78                             disk_cache::Entry** entry,
79                             const net::CompletionCallback& callback) {
80   // The URL may not start with a valid protocol; search for it.
81   int urlpos = key.find("http");
82   std::string url = urlpos > 0 ? key.substr(urlpos) : key;
83   std::string base_path = path_.MaybeAsASCII();
84   std::string new_path =
85       net::UrlToFilenameEncoder::Encode(url, base_path, false);
86   entry_path_ = base::FilePath::FromUTF8Unsafe(new_path);
87 
88 #ifdef WIN32_LARGE_FILENAME_SUPPORT
89   // In order for long filenames to work, we'll need to prepend
90   // the windows magic token.
91   const std::wstring kLongFilenamePrefix(L"\\\\?\\");
92   // There is no way to prepend to a filename.  We simply *have*
93   // to convert to a wstring to do this.
94   std::wstring name = kLongFilenamePrefix;
95   name.append(entry_path_.value());
96   entry_path_ = base::FilePath(name);
97 #endif
98 
99   entry_url_ = key;
100 
101   SafeCreateDirectory(entry_path_.DirName());
102 
103   base::FilePath::StringType file = entry_path_.value();
104 #ifdef WIN32_LARGE_FILENAME_SUPPORT
105   entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0,
106                        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
107   if (entry_ == INVALID_HANDLE_VALUE)
108     wprintf(L"CreateFileW (%s) failed: %d\n", file.c_str(), GetLastError());
109   return (entry_ != INVALID_HANDLE_VALUE) ? net::OK : net::ERR_FAILED;
110 #else
111   entry_ = base::OpenFile(entry_path_, "w+");
112   return (entry_ != NULL) ? net::OK : net::ERR_FAILED;
113 #endif
114 }
115 
116 // Utility Function to create a normalized header string from a
117 // HttpResponseInfo.  The output will be formatted exactly
118 // like so:
119 //     HTTP/<version> <status_code> <status_text>\n
120 //     [<header-name>: <header-values>\n]*
121 // meaning, each line is \n-terminated, and there is no extra whitespace
122 // beyond the single space separators shown (of course, values can contain
123 // whitespace within them).  If a given header-name appears more than once
124 // in the set of headers, they are combined into a single line like so:
125 //     <header-name>: <header-value1>, <header-value2>, ...<header-valueN>\n
126 //
127 // DANGER: For some headers (e.g., "Set-Cookie"), the normalized form can be
128 // a lossy format.  This is due to the fact that some servers generate
129 // Set-Cookie headers that contain unquoted commas (usually as part of the
130 // value of an "expires" attribute).  So, use this function with caution.  Do
131 // not expect to be able to re-parse Set-Cookie headers from this output.
132 //
133 // NOTE: Do not make any assumptions about the encoding of this output
134 // string.  It may be non-ASCII, and the encoding used by the server is not
135 // necessarily known to us.  Do not assume that this output is UTF-8!
GetNormalizedHeaders(const net::HttpResponseInfo & info,std::string * output)136 void GetNormalizedHeaders(const net::HttpResponseInfo& info,
137                           std::string* output) {
138   // Start with the status line
139   output->assign(info.headers->GetStatusLine());
140   output->append("\r\n");
141 
142   // Enumerate the headers
143   void* iter = 0;
144   std::string name, value;
145   while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
146     output->append(name);
147     output->append(": ");
148     output->append(value);
149     output->append("\r\n");
150   }
151 
152   // Mark the end of headers
153   output->append("\r\n");
154 }
155 
WriteEntry(disk_cache::Entry * entry,int index,int offset,net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)156 int DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset,
157                            net::IOBuffer* buf, int buf_len,
158                            const net::CompletionCallback& callback) {
159   if (!entry_)
160     return 0;
161 
162   std::string headers;
163   const char *data;
164   size_t len;
165   if (index == 0) {  // Stream 0 is the headers.
166     net::HttpResponseInfo response_info;
167     bool truncated;
168     if (!net::HttpCache::ParseResponseInfo(buf->data(), buf_len,
169                                            &response_info, &truncated))
170       return 0;
171 
172     // Skip this entry if it was truncated (results in an empty file).
173     if (truncated)
174       return buf_len;
175 
176     // Remove the size headers.
177     response_info.headers->RemoveHeader("transfer-encoding");
178     response_info.headers->RemoveHeader("content-length");
179     response_info.headers->RemoveHeader("x-original-url");
180 
181     // Convert the headers into a string ending with LF.
182     GetNormalizedHeaders(response_info, &headers);
183 
184     // Append a header for the original URL.
185     std::string url = entry_url_;
186     // strip off the "XXGET" which may be in the key.
187     std::string::size_type pos(0);
188     if ((pos = url.find("http")) != 0) {
189       if (pos != std::string::npos)
190         url = url.substr(pos);
191     }
192     std::string x_original_url = "X-Original-Url: " + url + "\r\n";
193     // we know that the last two bytes are CRLF.
194     headers.replace(headers.length() - 2, 0, x_original_url);
195 
196     data = headers.c_str();
197     len = headers.size();
198   } else if (index == 1) {
199     data = buf->data();
200     len = buf_len;
201   } else {
202     return 0;
203   }
204 
205 #ifdef WIN32_LARGE_FILENAME_SUPPORT
206   DWORD bytes;
207   if (!WriteFile(entry_, data, len, &bytes, 0))
208     return 0;
209 
210   return bytes;
211 #else
212   return fwrite(data, 1, len, entry_);
213 #endif
214 }
215 
CloseEntry(disk_cache::Entry * entry,base::Time last_used,base::Time last_modified)216 void DiskDumper::CloseEntry(disk_cache::Entry* entry, base::Time last_used,
217                           base::Time last_modified) {
218 #ifdef WIN32_LARGE_FILENAME_SUPPORT
219   CloseHandle(entry_);
220 #else
221   base::CloseFile(entry_);
222 #endif
223 }
224