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