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