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