]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
11fdf7f2 | 3 | |
7c673cae FG |
4 | /* |
5 | * Ceph - scalable distributed file system | |
6 | * | |
7 | * Copyright (C) 2011 New Dream Network | |
8 | * | |
9 | * This is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License version 2.1, as published by the Free Software | |
12 | * Foundation. See file COPYING. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <boost/format.hpp> | |
17 | ||
18 | #include "common/escape.h" | |
19 | #include "common/Formatter.h" | |
20 | #include "rgw/rgw_common.h" | |
21 | #include "rgw/rgw_formats.h" | |
22 | #include "rgw/rgw_rest.h" | |
23 | ||
24 | #define LARGE_SIZE 8192 | |
25 | ||
26 | #define dout_subsys ceph_subsys_rgw | |
27 | ||
20effc67 TL |
28 | using namespace std; |
29 | ||
7c673cae FG |
30 | RGWFormatter_Plain::RGWFormatter_Plain(const bool ukv) |
31 | : use_kv(ukv) | |
32 | { | |
33 | } | |
34 | ||
35 | RGWFormatter_Plain::~RGWFormatter_Plain() | |
36 | { | |
37 | free(buf); | |
38 | } | |
39 | ||
40 | void RGWFormatter_Plain::flush(ostream& os) | |
41 | { | |
42 | if (!buf) | |
43 | return; | |
44 | ||
45 | if (len) { | |
46 | os << buf; | |
47 | os.flush(); | |
48 | } | |
49 | ||
50 | reset_buf(); | |
51 | } | |
52 | ||
53 | void RGWFormatter_Plain::reset_buf() | |
54 | { | |
55 | free(buf); | |
56 | buf = NULL; | |
57 | len = 0; | |
58 | max_len = 0; | |
59 | } | |
60 | ||
61 | void RGWFormatter_Plain::reset() | |
62 | { | |
63 | reset_buf(); | |
64 | stack.clear(); | |
65 | min_stack_level = 0; | |
66 | } | |
67 | ||
9f95a23c | 68 | void RGWFormatter_Plain::open_array_section(std::string_view name) |
7c673cae FG |
69 | { |
70 | struct plain_stack_entry new_entry; | |
71 | new_entry.is_array = true; | |
72 | new_entry.size = 0; | |
73 | ||
74 | if (use_kv && min_stack_level > 0 && !stack.empty()) { | |
75 | struct plain_stack_entry& entry = stack.back(); | |
76 | ||
77 | if (!entry.is_array) | |
78 | dump_format(name, ""); | |
79 | } | |
80 | ||
81 | stack.push_back(new_entry); | |
82 | } | |
83 | ||
9f95a23c | 84 | void RGWFormatter_Plain::open_array_section_in_ns(std::string_view name, const char *ns) |
7c673cae FG |
85 | { |
86 | ostringstream oss; | |
87 | oss << name << " " << ns; | |
88 | open_array_section(oss.str().c_str()); | |
89 | } | |
90 | ||
9f95a23c | 91 | void RGWFormatter_Plain::open_object_section(std::string_view name) |
7c673cae FG |
92 | { |
93 | struct plain_stack_entry new_entry; | |
94 | new_entry.is_array = false; | |
95 | new_entry.size = 0; | |
96 | ||
97 | if (use_kv && min_stack_level > 0) | |
98 | dump_format(name, ""); | |
99 | ||
100 | stack.push_back(new_entry); | |
101 | } | |
102 | ||
9f95a23c | 103 | void RGWFormatter_Plain::open_object_section_in_ns(std::string_view name, |
7c673cae FG |
104 | const char *ns) |
105 | { | |
106 | ostringstream oss; | |
107 | oss << name << " " << ns; | |
108 | open_object_section(oss.str().c_str()); | |
109 | } | |
110 | ||
111 | void RGWFormatter_Plain::close_section() | |
112 | { | |
113 | stack.pop_back(); | |
114 | } | |
115 | ||
9f95a23c | 116 | void RGWFormatter_Plain::dump_unsigned(std::string_view name, uint64_t u) |
7c673cae FG |
117 | { |
118 | dump_value_int(name, "%" PRIu64, u); | |
119 | } | |
120 | ||
9f95a23c | 121 | void RGWFormatter_Plain::dump_int(std::string_view name, int64_t u) |
7c673cae FG |
122 | { |
123 | dump_value_int(name, "%" PRId64, u); | |
124 | } | |
125 | ||
9f95a23c | 126 | void RGWFormatter_Plain::dump_float(std::string_view name, double d) |
7c673cae FG |
127 | { |
128 | dump_value_int(name, "%f", d); | |
129 | } | |
130 | ||
9f95a23c | 131 | void RGWFormatter_Plain::dump_string(std::string_view name, std::string_view s) |
7c673cae | 132 | { |
9f95a23c | 133 | dump_format(name, "%.*s", s.size(), s.data()); |
7c673cae FG |
134 | } |
135 | ||
9f95a23c | 136 | std::ostream& RGWFormatter_Plain::dump_stream(std::string_view name) |
7c673cae FG |
137 | { |
138 | // TODO: implement this! | |
139 | ceph_abort(); | |
140 | } | |
141 | ||
9f95a23c | 142 | void RGWFormatter_Plain::dump_format_va(std::string_view name, const char *ns, bool quoted, const char *fmt, va_list ap) |
7c673cae FG |
143 | { |
144 | char buf[LARGE_SIZE]; | |
145 | ||
146 | struct plain_stack_entry& entry = stack.back(); | |
147 | ||
148 | if (!min_stack_level) | |
149 | min_stack_level = stack.size(); | |
150 | ||
151 | bool should_print = ((stack.size() == min_stack_level && !entry.size) || use_kv); | |
152 | ||
153 | entry.size++; | |
154 | ||
155 | if (!should_print) | |
156 | return; | |
157 | ||
158 | vsnprintf(buf, LARGE_SIZE, fmt, ap); | |
159 | ||
160 | const char *eol; | |
161 | if (wrote_something) { | |
162 | if (use_kv && entry.is_array && entry.size > 1) | |
163 | eol = ", "; | |
164 | else | |
165 | eol = "\n"; | |
166 | } else | |
167 | eol = ""; | |
168 | wrote_something = true; | |
169 | ||
170 | if (use_kv && !entry.is_array) | |
9f95a23c | 171 | write_data("%s%.*s: %s", eol, name.size(), name.data(), buf); |
7c673cae FG |
172 | else |
173 | write_data("%s%s", eol, buf); | |
174 | } | |
175 | ||
176 | int RGWFormatter_Plain::get_len() const | |
177 | { | |
178 | // don't include null termination in length | |
179 | return (len ? len - 1 : 0); | |
180 | } | |
181 | ||
182 | void RGWFormatter_Plain::write_raw_data(const char *data) | |
183 | { | |
184 | write_data("%s", data); | |
185 | } | |
186 | ||
187 | void RGWFormatter_Plain::write_data(const char *fmt, ...) | |
188 | { | |
189 | #define LARGE_ENOUGH_LEN 128 | |
190 | int n, size = LARGE_ENOUGH_LEN; | |
191 | char s[size + 8]; | |
192 | char *p, *np; | |
193 | bool p_on_stack; | |
194 | va_list ap; | |
195 | int pos; | |
196 | ||
197 | p = s; | |
198 | p_on_stack = true; | |
199 | ||
200 | while (1) { | |
201 | va_start(ap, fmt); | |
202 | n = vsnprintf(p, size, fmt, ap); | |
203 | va_end(ap); | |
204 | ||
205 | if (n > -1 && n < size) | |
206 | goto done; | |
207 | /* Else try again with more space. */ | |
208 | if (n > -1) /* glibc 2.1 */ | |
209 | size = n+1; /* precisely what is needed */ | |
210 | else /* glibc 2.0 */ | |
211 | size *= 2; /* twice the old size */ | |
212 | if (p_on_stack) | |
213 | np = (char *)malloc(size + 8); | |
214 | else | |
215 | np = (char *)realloc(p, size + 8); | |
216 | if (!np) | |
217 | goto done_free; | |
218 | p = np; | |
219 | p_on_stack = false; | |
220 | } | |
221 | done: | |
222 | #define LARGE_ENOUGH_BUF 4096 | |
223 | if (!buf) { | |
11fdf7f2 | 224 | max_len = std::max(LARGE_ENOUGH_BUF, size); |
7c673cae FG |
225 | buf = (char *)malloc(max_len); |
226 | if (!buf) { | |
227 | cerr << "ERROR: RGWFormatter_Plain::write_data: failed allocating " << max_len << " bytes" << std::endl; | |
228 | goto done_free; | |
229 | } | |
230 | } | |
231 | ||
232 | if (len + size > max_len) { | |
233 | max_len = len + size + LARGE_ENOUGH_BUF; | |
234 | void *_realloc = NULL; | |
235 | if ((_realloc = realloc(buf, max_len)) == NULL) { | |
236 | cerr << "ERROR: RGWFormatter_Plain::write_data: failed allocating " << max_len << " bytes" << std::endl; | |
237 | goto done_free; | |
238 | } else { | |
239 | buf = (char *)_realloc; | |
240 | } | |
241 | } | |
242 | ||
243 | pos = len; | |
244 | if (len) | |
245 | pos--; // squash null termination | |
246 | strcpy(buf + pos, p); | |
247 | len = pos + strlen(p) + 1; | |
248 | done_free: | |
249 | if (!p_on_stack) | |
250 | free(p); | |
251 | } | |
252 | ||
9f95a23c | 253 | void RGWFormatter_Plain::dump_value_int(std::string_view name, const char *fmt, ...) |
7c673cae FG |
254 | { |
255 | char buf[LARGE_SIZE]; | |
256 | va_list ap; | |
257 | ||
258 | if (!min_stack_level) | |
259 | min_stack_level = stack.size(); | |
260 | ||
261 | struct plain_stack_entry& entry = stack.back(); | |
262 | bool should_print = ((stack.size() == min_stack_level && !entry.size) || use_kv); | |
263 | ||
264 | entry.size++; | |
265 | ||
266 | if (!should_print) | |
267 | return; | |
268 | ||
269 | va_start(ap, fmt); | |
270 | vsnprintf(buf, LARGE_SIZE, fmt, ap); | |
271 | va_end(ap); | |
272 | ||
273 | const char *eol; | |
274 | if (wrote_something) { | |
275 | eol = "\n"; | |
276 | } else | |
277 | eol = ""; | |
278 | wrote_something = true; | |
279 | ||
280 | if (use_kv && !entry.is_array) | |
9f95a23c | 281 | write_data("%s%.*s: %s", eol, name.size(), name.data(), buf); |
7c673cae FG |
282 | else |
283 | write_data("%s%s", eol, buf); | |
284 | ||
285 | } | |
286 | ||
287 | ||
288 | /* An utility class that serves as a mean to access the protected static | |
289 | * methods of XMLFormatter. */ | |
290 | class HTMLHelper : public XMLFormatter { | |
291 | public: | |
292 | static std::string escape(const std::string& unescaped_str) { | |
11fdf7f2 TL |
293 | int len = escape_xml_attr_len(unescaped_str.c_str()); |
294 | std::string escaped(len, 0); | |
295 | escape_xml_attr(unescaped_str.c_str(), escaped.data()); | |
296 | return escaped; | |
7c673cae FG |
297 | } |
298 | }; | |
299 | ||
300 | void RGWSwiftWebsiteListingFormatter::generate_header( | |
301 | const std::string& dir_path, | |
302 | const std::string& css_path) | |
303 | { | |
304 | ss << R"(<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 )" | |
305 | << R"(Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">)"; | |
306 | ||
11fdf7f2 | 307 | ss << "<html><head><title>Listing of " << xml_stream_escaper(dir_path) |
7c673cae FG |
308 | << "</title>"; |
309 | ||
310 | if (! css_path.empty()) { | |
311 | ss << boost::format(R"(<link rel="stylesheet" type="text/css" href="%s" />)") | |
312 | % url_encode(css_path); | |
313 | } else { | |
314 | ss << R"(<style type="text/css">)" | |
315 | << R"(h1 {font-size: 1em; font-weight: bold;})" | |
316 | << R"(th {text-align: left; padding: 0px 1em 0px 1em;})" | |
317 | << R"(td {padding: 0px 1em 0px 1em;})" | |
318 | << R"(a {text-decoration: none;})" | |
319 | << R"(</style>)"; | |
320 | } | |
321 | ||
322 | ss << "</head><body>"; | |
323 | ||
11fdf7f2 | 324 | ss << R"(<h1 id="title">Listing of )" << xml_stream_escaper(dir_path) << "</h1>" |
7c673cae FG |
325 | << R"(<table id="listing">)" |
326 | << R"(<tr id="heading">)" | |
327 | << R"(<th class="colname">Name</th>)" | |
328 | << R"(<th class="colsize">Size</th>)" | |
329 | << R"(<th class="coldate">Date</th>)" | |
330 | << R"(</tr>)"; | |
331 | ||
332 | if (! prefix.empty()) { | |
333 | ss << R"(<tr id="parent" class="item">)" | |
334 | << R"(<td class="colname"><a href="../">../</a></td>)" | |
335 | << R"(<td class="colsize"> </td>)" | |
336 | << R"(<td class="coldate"> </td>)" | |
337 | << R"(</tr>)"; | |
338 | } | |
339 | } | |
340 | ||
341 | void RGWSwiftWebsiteListingFormatter::generate_footer() | |
342 | { | |
343 | ss << R"(</table></body></html>)"; | |
344 | } | |
345 | ||
346 | std::string RGWSwiftWebsiteListingFormatter::format_name( | |
347 | const std::string& item_name) const | |
348 | { | |
349 | return item_name.substr(prefix.length()); | |
350 | } | |
351 | ||
352 | void RGWSwiftWebsiteListingFormatter::dump_object(const rgw_bucket_dir_entry& objent) | |
353 | { | |
354 | const auto name = format_name(objent.key.name); | |
355 | ss << boost::format(R"(<tr class="item %s">)") | |
356 | % "default" | |
357 | << boost::format(R"(<td class="colname"><a href="%s">%s</a></td>)") | |
358 | % url_encode(name) | |
359 | % HTMLHelper::escape(name) | |
360 | << boost::format(R"(<td class="colsize">%lld</td>)") % objent.meta.size | |
361 | << boost::format(R"(<td class="coldate">%s</td>)") | |
362 | % dump_time_to_str(objent.meta.mtime) | |
363 | << R"(</tr>)"; | |
364 | } | |
365 | ||
366 | void RGWSwiftWebsiteListingFormatter::dump_subdir(const std::string& name) | |
367 | { | |
368 | const auto fname = format_name(name); | |
369 | ss << R"(<tr class="item subdir">)" | |
370 | << boost::format(R"(<td class="colname"><a href="%s">%s</a></td>)") | |
371 | % url_encode(fname) | |
372 | % HTMLHelper::escape(fname) | |
373 | << R"(<td class="colsize"> </td>)" | |
374 | << R"(<td class="coldate"> </td>)" | |
375 | << R"(</tr>)"; | |
376 | } |