]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_formats.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / rgw / rgw_formats.cc
CommitLineData
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
28using namespace std;
29
7c673cae
FG
30RGWFormatter_Plain::RGWFormatter_Plain(const bool ukv)
31 : use_kv(ukv)
32{
33}
34
35RGWFormatter_Plain::~RGWFormatter_Plain()
36{
37 free(buf);
38}
39
40void 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
53void RGWFormatter_Plain::reset_buf()
54{
55 free(buf);
56 buf = NULL;
57 len = 0;
58 max_len = 0;
59}
60
61void RGWFormatter_Plain::reset()
62{
63 reset_buf();
64 stack.clear();
65 min_stack_level = 0;
66}
67
9f95a23c 68void 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 84void 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 91void 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 103void 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
111void RGWFormatter_Plain::close_section()
112{
113 stack.pop_back();
114}
115
9f95a23c 116void RGWFormatter_Plain::dump_unsigned(std::string_view name, uint64_t u)
7c673cae
FG
117{
118 dump_value_int(name, "%" PRIu64, u);
119}
120
9f95a23c 121void RGWFormatter_Plain::dump_int(std::string_view name, int64_t u)
7c673cae
FG
122{
123 dump_value_int(name, "%" PRId64, u);
124}
125
9f95a23c 126void RGWFormatter_Plain::dump_float(std::string_view name, double d)
7c673cae
FG
127{
128 dump_value_int(name, "%f", d);
129}
130
9f95a23c 131void 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 136std::ostream& RGWFormatter_Plain::dump_stream(std::string_view name)
7c673cae
FG
137{
138 // TODO: implement this!
139 ceph_abort();
140}
141
9f95a23c 142void 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
176int RGWFormatter_Plain::get_len() const
177{
178 // don't include null termination in length
179 return (len ? len - 1 : 0);
180}
181
182void RGWFormatter_Plain::write_raw_data(const char *data)
183{
184 write_data("%s", data);
185}
186
187void 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 }
221done:
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;
248done_free:
249 if (!p_on_stack)
250 free(p);
251}
252
9f95a23c 253void 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. */
290class HTMLHelper : public XMLFormatter {
291public:
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
300void 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">&nbsp;</td>)"
336 << R"(<td class="coldate">&nbsp;</td>)"
337 << R"(</tr>)";
338 }
339}
340
341void RGWSwiftWebsiteListingFormatter::generate_footer()
342{
343 ss << R"(</table></body></html>)";
344}
345
346std::string RGWSwiftWebsiteListingFormatter::format_name(
347 const std::string& item_name) const
348{
349 return item_name.substr(prefix.length());
350}
351
352void 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
366void 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">&nbsp;</td>)"
374 << R"(<td class="coldate">&nbsp;</td>)"
375 << R"(</tr>)";
376}