Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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, const std::string& s)
129 {
130   dump_format(name, "%s", s.c_str());
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 }