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