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