1 /*
2 * virbuffer.c: buffers for libvirt
3 *
4 * Copyright (C) 2005-2008, 2010-2015 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include <stdarg.h>
24
25 #include "virbuffer.h"
26 #include "virstring.h"
27 #include "viralloc.h"
28
29 #define VIR_FROM_THIS VIR_FROM_NONE
30
31 /**
32 * virBufferAdjustIndent:
33 * @buf: the buffer
34 * @indent: adjustment to make
35 *
36 * Alter the auto-indent value by adding indent (positive to increase,
37 * negative to decrease). Automatic indentation is performed by all
38 * additive functions when the existing buffer is empty or ends with a
39 * newline (however, note that no indentation is added after newlines
40 * embedded in an appended string). If @indent would cause overflow, the
41 * indentation level is truncated.
42 */
43 void
virBufferAdjustIndent(virBuffer * buf,int indent)44 virBufferAdjustIndent(virBuffer *buf, int indent)
45 {
46 if (!buf)
47 return;
48
49 if (indent > 0) {
50 if (INT_MAX - indent < buf->indent) {
51 buf->indent = INT_MAX;
52 return;
53 }
54 } else {
55 if (buf->indent < -indent) {
56 buf->indent = 0;
57 return;
58 }
59 }
60
61 buf->indent += indent;
62 }
63
64
65 /**
66 * virBufferSetIndent:
67 * @buf: the buffer
68 * @indent: new indentation size.
69 *
70 * Set the auto-indent value to @indent. See virBufferAdjustIndent on how auto
71 * indentation is applied.
72 */
73 void
virBufferSetIndent(virBuffer * buf,int indent)74 virBufferSetIndent(virBuffer *buf, int indent)
75 {
76 if (!buf)
77 return;
78
79 buf->indent = indent;
80 }
81
82
83 /**
84 * virBufferGetIndent:
85 * @buf: the buffer
86 *
87 * Return the current auto-indent setting of @buf.
88 */
89 size_t
virBufferGetIndent(const virBuffer * buf)90 virBufferGetIndent(const virBuffer *buf)
91 {
92 return buf->indent;
93 }
94
95
96 /**
97 * virBufferGetEffectiveIndent:
98 * @buf: the buffer
99 *
100 * Returns the number of spaces that need to be appended to @buf to honour
101 * auto-indentation.
102 */
103 size_t
virBufferGetEffectiveIndent(const virBuffer * buf)104 virBufferGetEffectiveIndent(const virBuffer *buf)
105 {
106 if (buf->str && buf->str->len && buf->str->str[buf->str->len - 1] != '\n')
107 return 0;
108
109 return buf->indent;
110 }
111
112
113 /**
114 * virBufferInitialize
115 * @buf: the buffer
116 *
117 * Ensures that the internal GString container is allocated.
118 */
119 static void
virBufferInitialize(virBuffer * buf)120 virBufferInitialize(virBuffer *buf)
121 {
122 if (!buf->str)
123 buf->str = g_string_new(NULL);
124 }
125
126
127 static void
virBufferApplyIndent(virBuffer * buf)128 virBufferApplyIndent(virBuffer *buf)
129 {
130 const char space[] = " ";
131 size_t spacesz = sizeof(space) - 1;
132 size_t toindent = virBufferGetEffectiveIndent(buf);
133
134 if (toindent == 0)
135 return;
136
137 while (toindent > spacesz) {
138 g_string_append_len(buf->str, space, spacesz);
139 toindent -= spacesz;
140 }
141
142 g_string_append_len(buf->str, space, toindent);
143 }
144
145
146 /**
147 * virBufferAdd:
148 * @buf: the buffer to append to
149 * @str: the string
150 * @len: the number of bytes to add, or -1
151 *
152 * Add a string range to an XML buffer. If @len == -1, the length of
153 * str is recomputed to the full string. Auto indentation may be applied.
154 *
155 */
156 void
virBufferAdd(virBuffer * buf,const char * str,int len)157 virBufferAdd(virBuffer *buf, const char *str, int len)
158 {
159 if (!str || !buf)
160 return;
161
162 virBufferInitialize(buf);
163 virBufferApplyIndent(buf);
164
165 if (len < 0)
166 g_string_append(buf->str, str);
167 else
168 g_string_append_len(buf->str, str, len);
169 }
170
171 /**
172 * virBufferAddBuffer:
173 * @buf: the buffer to append to
174 * @toadd: the buffer to append
175 *
176 * Add a buffer into another buffer without need to go through:
177 * virBufferContentAndReset(), virBufferAdd(). Auto indentation
178 * is (intentionally) NOT applied!
179 *
180 * The @toadd virBuffer is consumed and cleared.
181 */
182 void
virBufferAddBuffer(virBuffer * buf,virBuffer * toadd)183 virBufferAddBuffer(virBuffer *buf, virBuffer *toadd)
184 {
185 if (!toadd || !toadd->str)
186 return;
187
188 if (buf) {
189 virBufferInitialize(buf);
190 g_string_append_len(buf->str, toadd->str->str, toadd->str->len);
191 }
192
193 virBufferFreeAndReset(toadd);
194 }
195
196 /**
197 * virBufferAddChar:
198 * @buf: the buffer to append to
199 * @c: the character to add
200 *
201 * Add a single character 'c' to a buffer. Auto indentation may be applied.
202 *
203 */
204 void
virBufferAddChar(virBuffer * buf,char c)205 virBufferAddChar(virBuffer *buf, char c)
206 {
207 virBufferAdd(buf, &c, 1);
208 }
209
210 /**
211 * virBufferCurrentContent:
212 * @buf: Buffer
213 *
214 * Get the current content from the buffer. The content is only valid
215 * until the next operation on @buf, and an empty string is returned if
216 * no content is present yet.
217 *
218 * Returns the buffer content or NULL in case of error.
219 */
220 const char *
virBufferCurrentContent(virBuffer * buf)221 virBufferCurrentContent(virBuffer *buf)
222 {
223 if (!buf)
224 return NULL;
225
226 if (!buf->str ||
227 buf->str->len == 0)
228 return "";
229
230 return buf->str->str;
231 }
232
233 /**
234 * virBufferContentAndReset:
235 * @buf: Buffer
236 *
237 * Get the content from the buffer and free (only) the buffer structure.
238 * The caller owns the returned string & should free it when no longer
239 * required. The buffer object is reset to its initial state. This
240 * interface intentionally returns NULL instead of an empty string if
241 * there is no content.
242 *
243 * Returns the buffer content or NULL in case of error.
244 */
245 char *
virBufferContentAndReset(virBuffer * buf)246 virBufferContentAndReset(virBuffer *buf)
247 {
248 char *str = NULL;
249
250 if (!buf)
251 return NULL;
252
253 if (buf->str)
254 str = g_string_free(buf->str, false);
255
256 memset(buf, 0, sizeof(*buf));
257 return str;
258 }
259
260 /**
261 * virBufferFreeAndReset:
262 * @buf: the buffer to free and reset
263 *
264 * Frees the buffer content and resets the buffer structure.
265 */
virBufferFreeAndReset(virBuffer * buf)266 void virBufferFreeAndReset(virBuffer *buf)
267 {
268 if (!buf)
269 return;
270
271 if (buf->str)
272 g_string_free(buf->str, true);
273
274 memset(buf, 0, sizeof(*buf));
275 }
276
277 /**
278 * virBufferUse:
279 * @buf: the usage of the string in the buffer
280 *
281 * Return the string usage in bytes
282 */
283 size_t
virBufferUse(const virBuffer * buf)284 virBufferUse(const virBuffer *buf)
285 {
286 if (!buf || !buf->str)
287 return 0;
288
289 return buf->str->len;
290 }
291
292 /**
293 * virBufferAsprintf:
294 * @buf: the buffer to append to
295 * @format: the format
296 * @...: the variable list of arguments
297 *
298 * Do a formatted print to an XML buffer. Auto indentation may be applied.
299 */
300 void
virBufferAsprintf(virBuffer * buf,const char * format,...)301 virBufferAsprintf(virBuffer *buf, const char *format, ...)
302 {
303 va_list argptr;
304 va_start(argptr, format);
305 virBufferVasprintf(buf, format, argptr);
306 va_end(argptr);
307 }
308
309 /**
310 * virBufferVasprintf:
311 * @buf: the buffer to append to
312 * @format: the format
313 * @argptr: the variable list of arguments
314 *
315 * Do a formatted print to an XML buffer. Auto indentation may be applied.
316 */
317 void
virBufferVasprintf(virBuffer * buf,const char * format,va_list argptr)318 virBufferVasprintf(virBuffer *buf, const char *format, va_list argptr)
319 {
320 if ((format == NULL) || (buf == NULL))
321 return;
322
323 virBufferInitialize(buf);
324 virBufferApplyIndent(buf);
325
326 g_string_append_vprintf(buf->str, format, argptr);
327 }
328
329
330 /**
331 * virBufferEscapeString:
332 * @buf: the buffer to append to
333 * @format: a printf like format string but with only one %s parameter
334 * @str: the string argument which needs to be escaped
335 *
336 * Do a formatted print with a single string to an XML buffer. The
337 * string is escaped for use in XML. If @str is NULL, nothing is
338 * added (not even the rest of @format). Auto indentation may be
339 * applied.
340 */
341 void
virBufferEscapeString(virBuffer * buf,const char * format,const char * str)342 virBufferEscapeString(virBuffer *buf, const char *format, const char *str)
343 {
344 int len;
345 g_autofree char *escaped = NULL;
346 char *out;
347 const char *cur;
348 const char forbidden_characters[] = {
349 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
350 /*\t*/ /*\n*/ 0x0B, 0x0C, /*\r*/ 0x0E, 0x0F, 0x10,
351 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
352 0x19, '"', '&', '\'', '<', '>',
353 '\0'
354 };
355
356 if ((format == NULL) || (buf == NULL) || (str == NULL))
357 return;
358
359 len = strlen(str);
360 if (strcspn(str, forbidden_characters) == len) {
361 virBufferAsprintf(buf, format, str);
362 return;
363 }
364
365 escaped = g_malloc0_n(len + 1, 6);
366
367 cur = str;
368 out = escaped;
369 while (*cur != 0) {
370 if (*cur == '<') {
371 *out++ = '&';
372 *out++ = 'l';
373 *out++ = 't';
374 *out++ = ';';
375 } else if (*cur == '>') {
376 *out++ = '&';
377 *out++ = 'g';
378 *out++ = 't';
379 *out++ = ';';
380 } else if (*cur == '&') {
381 *out++ = '&';
382 *out++ = 'a';
383 *out++ = 'm';
384 *out++ = 'p';
385 *out++ = ';';
386 } else if (*cur == '"') {
387 *out++ = '&';
388 *out++ = 'q';
389 *out++ = 'u';
390 *out++ = 'o';
391 *out++ = 't';
392 *out++ = ';';
393 } else if (*cur == '\'') {
394 *out++ = '&';
395 *out++ = 'a';
396 *out++ = 'p';
397 *out++ = 'o';
398 *out++ = 's';
399 *out++ = ';';
400 } else if (!strchr(forbidden_characters, *cur)) {
401 /*
402 * default case, just copy !
403 * Note that character over 0x80 are likely to give problem
404 * with UTF-8 XML, but since our string don't have an encoding
405 * it's hard to handle properly we have to assume it's UTF-8 too
406 */
407 *out++ = *cur;
408 } else {
409 /* silently ignore control characters */
410 }
411 cur++;
412 }
413 *out = 0;
414
415 virBufferAsprintf(buf, format, escaped);
416 }
417
418 /**
419 * virBufferEscapeSexpr:
420 * @buf: the buffer to append to
421 * @format: a printf like format string but with only one %s parameter
422 * @str: the string argument which needs to be escaped
423 *
424 * Do a formatted print with a single string to an sexpr buffer. The
425 * string is escaped to avoid generating a sexpr that xen will choke
426 * on. This doesn't fully escape the sexpr, just enough for our code
427 * to work. Auto indentation may be applied.
428 */
429 void
virBufferEscapeSexpr(virBuffer * buf,const char * format,const char * str)430 virBufferEscapeSexpr(virBuffer *buf,
431 const char *format,
432 const char *str)
433 {
434 virBufferEscape(buf, '\\', "\\'", format, str);
435 }
436
437 /**
438 * virBufferEscapeRegex:
439 * @buf: the buffer to append to
440 * @format: a printf like format string but with only one %s parameter
441 * @str: the string argument which needs to be escaped
442 *
443 * Do a formatted print with a single string to a buffer. The @str is
444 * escaped to avoid using POSIX extended regular expression meta-characters.
445 * Escaping is not applied to characters specified in @format. Auto
446 * indentation may be applied.
447 */
448 void
virBufferEscapeRegex(virBuffer * buf,const char * format,const char * str)449 virBufferEscapeRegex(virBuffer *buf,
450 const char *format,
451 const char *str)
452 {
453 virBufferEscape(buf, '\\', "^$.|?*+()[]{}\\", format, str);
454 }
455
456
457 /**
458 * virBufferEscapeSQL:
459 * @buf: the buffer to append to
460 * @format: a printf like format string but with only one %s parameter
461 * @str: the string argument which needs to be escaped
462 *
463 * Do a formatted print with a single string to a buffer. The @str is
464 * escaped to prevent SQL injection (format is expected to contain \"%s\").
465 * Auto indentation may be applied.
466 */
467 void
virBufferEscapeSQL(virBuffer * buf,const char * format,const char * str)468 virBufferEscapeSQL(virBuffer *buf,
469 const char *format,
470 const char *str)
471 {
472 virBufferEscape(buf, '\\', "'\"\\", format, str);
473 }
474
475
476 /**
477 * virBufferEscape:
478 * @buf: the buffer to append to
479 * @escape: the escape character to inject
480 * @toescape: NUL-terminated list of characters to escape
481 * @format: a printf like format string but with only one %s parameter
482 * @str: the string argument which needs to be escaped
483 *
484 * Do a formatted print with a single string to a buffer. Any characters
485 * in the provided list that are contained in @str are escaped with the
486 * given escape. Escaping is not applied to characters specified in @format.
487 * Auto indentation may be applied.
488 */
489 void
virBufferEscape(virBuffer * buf,char escape,const char * toescape,const char * format,const char * str)490 virBufferEscape(virBuffer *buf, char escape, const char *toescape,
491 const char *format, const char *str)
492 {
493 int len;
494 g_autofree char *escaped = NULL;
495 char *out;
496 const char *cur;
497
498 if ((format == NULL) || (buf == NULL) || (str == NULL))
499 return;
500
501 len = strlen(str);
502 if (strcspn(str, toescape) == len) {
503 virBufferAsprintf(buf, format, str);
504 return;
505 }
506
507 escaped = g_malloc0_n(len + 1, 2);
508
509 cur = str;
510 out = escaped;
511 while (*cur != 0) {
512 if (strchr(toescape, *cur))
513 *out++ = escape;
514 *out++ = *cur;
515 cur++;
516 }
517 *out = 0;
518
519 virBufferAsprintf(buf, format, escaped);
520 }
521
522
523 /**
524 * virBufferURIEncodeString:
525 * @buf: the buffer to append to
526 * @str: the string argument which will be URI-encoded
527 *
528 * Append the string to the buffer. The string will be URI-encoded
529 * during the append (ie any non alphanumeric characters are replaced
530 * with '%xx' hex sequences). Auto indentation may be applied.
531 */
532 void
virBufferURIEncodeString(virBuffer * buf,const char * str)533 virBufferURIEncodeString(virBuffer *buf, const char *str)
534 {
535 if ((buf == NULL) || (str == NULL))
536 return;
537
538 virBufferInitialize(buf);
539 virBufferApplyIndent(buf);
540
541 g_string_append_uri_escaped(buf->str, str, NULL, false);
542 }
543
544 /**
545 * virBufferEscapeShell:
546 * @buf: the buffer to append to
547 * @str: an unquoted string
548 *
549 * Quotes a string so that the shell (/bin/sh) will interpret the
550 * quoted string to mean str. Auto indentation may be applied.
551 */
552 void
virBufferEscapeShell(virBuffer * buf,const char * str)553 virBufferEscapeShell(virBuffer *buf, const char *str)
554 {
555 int len;
556 g_autofree char *escaped = NULL;
557 char *out;
558 const char *cur;
559
560 if ((buf == NULL) || (str == NULL))
561 return;
562
563 /* Only quote if str includes shell metacharacters. */
564 if (*str && !strpbrk(str, "\r\t\n !\"#$&'()*;<>?[\\]^`{|}~")) {
565 virBufferAdd(buf, str, -1);
566 return;
567 }
568
569 if (*str) {
570 len = strlen(str);
571
572 escaped = g_malloc0_n(len + 1, 4);
573 } else {
574 virBufferAddLit(buf, "''");
575 return;
576 }
577
578 cur = str;
579 out = escaped;
580
581 *out++ = '\'';
582 while (*cur != 0) {
583 if (*cur == '\'') {
584 *out++ = '\'';
585 /* Replace literal ' with a close ', a \', and a open ' */
586 *out++ = '\\';
587 *out++ = '\'';
588 }
589 *out++ = *cur++;
590 }
591 *out++ = '\'';
592 *out = 0;
593
594 virBufferAdd(buf, escaped, -1);
595 }
596
597 /**
598 * virBufferStrcatVArgs:
599 * @buf: the buffer to append to
600 * @ap: variable argument structure
601 *
602 * See virBufferStrcat.
603 */
604 void
virBufferStrcatVArgs(virBuffer * buf,va_list ap)605 virBufferStrcatVArgs(virBuffer *buf,
606 va_list ap)
607 {
608 char *str;
609
610 while ((str = va_arg(ap, char *)) != NULL)
611 virBufferAdd(buf, str, -1);
612 }
613
614 /**
615 * virBufferStrcat:
616 * @buf: the buffer to append to
617 * @...: the variable list of strings, the last argument must be NULL
618 *
619 * Concatenate strings to an XML buffer. Auto indentation may be applied
620 * after each string argument.
621 */
622 void
virBufferStrcat(virBuffer * buf,...)623 virBufferStrcat(virBuffer *buf, ...)
624 {
625 va_list ap;
626
627 if (!buf)
628 return;
629
630 va_start(ap, buf);
631 virBufferStrcatVArgs(buf, ap);
632 va_end(ap);
633 }
634
635 /**
636 * virBufferTrim:
637 * @buf: the buffer to trim
638 * @str: the string to be trimmed from the tail
639 *
640 * Trim the supplied string from the tail of the buffer.
641 */
642 void
virBufferTrim(virBuffer * buf,const char * str)643 virBufferTrim(virBuffer *buf, const char *str)
644 {
645 size_t len = 0;
646
647 if (!buf || !buf->str)
648 return;
649
650 if (!str)
651 return;
652
653 len = strlen(str);
654
655 if (len > buf->str->len ||
656 memcmp(&buf->str->str[buf->str->len - len], str, len) != 0)
657 return;
658
659 g_string_truncate(buf->str, buf->str->len - len);
660 }
661
662 /**
663 * virBufferTrimChars:
664 * @buf: the buffer to trim
665 * @trim: the characters to be trimmed
666 *
667 * Trim the tail of the buffer. The longest string that can be formed with
668 * the characters from @trim is trimmed.
669 */
670 void
virBufferTrimChars(virBuffer * buf,const char * trim)671 virBufferTrimChars(virBuffer *buf, const char *trim)
672 {
673 ssize_t i;
674
675 if (!buf || !buf->str)
676 return;
677
678 if (!trim)
679 return;
680
681 for (i = buf->str->len - 1; i > 0; i--) {
682 if (!strchr(trim, buf->str->str[i]))
683 break;
684 }
685
686 g_string_truncate(buf->str, i + 1);
687 }
688
689 /**
690 * virBufferTrimLen:
691 * @buf: the buffer to trim
692 * @len: the number of bytes to trim
693 *
694 * Trim the tail of a buffer.
695 */
696 void
virBufferTrimLen(virBuffer * buf,int len)697 virBufferTrimLen(virBuffer *buf, int len)
698 {
699 if (!buf || !buf->str)
700 return;
701
702 if (len > buf->str->len)
703 return;
704
705 g_string_truncate(buf->str, buf->str->len - len);
706 }
707
708 /**
709 * virBufferAddStr:
710 * @buf: the buffer to append to
711 * @str: string to append
712 *
713 * Appends @str to @buffer. Applies autoindentation on the separate lines of
714 * @str.
715 */
716 void
virBufferAddStr(virBuffer * buf,const char * str)717 virBufferAddStr(virBuffer *buf,
718 const char *str)
719 {
720 const char *end;
721
722 if (!buf || !str)
723 return;
724
725 while (*str) {
726 if ((end = strchr(str, '\n'))) {
727 virBufferAdd(buf, str, (end - str) + 1);
728 str = end + 1;
729 } else {
730 virBufferAdd(buf, str, -1);
731 break;
732 }
733 }
734 }
735