From c5ce534c03d0698509f3eb7ff90ca41610625570 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Thu, 19 Feb 2026 18:44:33 -0500 Subject: [PATCH] Updated cupsFileGetConf and cupsFilePutConf to escape more characters. --- cups/file.c | 145 +++++++++++++++++++++++++++++++++--------------- cups/testfile.c | 63 +++++++++++++++++++-- 2 files changed, 159 insertions(+), 49 deletions(-) diff --git a/cups/file.c b/cups/file.c index 7f48706335..a053368c82 100644 --- a/cups/file.c +++ b/cups/file.c @@ -725,11 +725,9 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ * Range check input... */ - DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT - ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum)); + DEBUG_printf(("2cupsFileGetConf(fp=%p, buf=%p, buflen=" CUPS_LLFMT ", value=%p, linenum=%p)", (void *)fp, (void *)buf, CUPS_LLCAST buflen, (void *)value, (void *)linenum)); - if (!fp || (fp->mode != 'r' && fp->mode != 's') || - !buf || buflen < 2 || !value) + if (!fp || (fp->mode != 'r' && fp->mode != 's') || !buf || buflen < 2 || !value) { if (value) *value = NULL; @@ -748,28 +746,30 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ (*linenum) ++; /* - * Strip any comments... + * Handle escaped characters and strip any comments... */ - if ((ptr = strchr(buf, '#')) != NULL) + for (ptr = buf; *ptr; ptr ++) { - if (ptr > buf && ptr[-1] == '\\') + if (*ptr == '#') { - // Unquote the #... - _cups_strcpy(ptr - 1, ptr); + // Strip comment text... + *ptr = '\0'; + break; } - else + else if (*ptr == '\\' && (ptr[1] == '\\' || ptr[1] == '#' || ptr[1] == 'n' || ptr[1] == 'r')) { - // Strip the comment and any trailing whitespace... - while (ptr > buf) - { - if (!_cups_isspace(ptr[-1])) - break; + /* + * \\, \#, \n, or \r, remove backslash and update the escaped char as + * needed... + */ - ptr --; - } + _cups_strcpy(ptr, ptr + 1); - *ptr = '\0'; + if (*ptr == 'n') + *ptr = '\n'; + else if (*ptr == 'r') + *ptr = '\r'; } } @@ -777,7 +777,11 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ * Strip leading whitespace... */ - for (ptr = buf; _cups_isspace(*ptr); ptr ++); + for (ptr = buf; *ptr; ptr ++) + { + if (!_cups_isspace(*ptr)) + break; + } if (ptr > buf) _cups_strcpy(buf, ptr); @@ -793,14 +797,16 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ */ for (ptr = buf; *ptr; ptr ++) + { if (_cups_isspace(*ptr)) break; + } if (*ptr) { /* * Have a value, skip any other spaces... - */ + */ while (_cups_isspace(*ptr)) *ptr++ = '\0'; @@ -810,12 +816,14 @@ cupsFileGetConf(cups_file_t *fp, /* I - CUPS file */ /* * Strip trailing whitespace and > for lines that begin with <... - */ + */ ptr += strlen(ptr) - 1; if (buf[0] == '<' && *ptr == '>') + { *ptr-- = '\0'; + } else if (buf[0] == '<' && *ptr != '>') { /* @@ -1497,19 +1505,18 @@ cupsFilePutChar(cups_file_t *fp, /* I - CUPS file */ /* * 'cupsFilePutConf()' - Write a configuration line. * - * This function handles any comment escaping of the value. + * This function handles any escaping of the value. * * @since CUPS 1.4/macOS 10.6@ */ ssize_t /* O - Number of bytes written or -1 on error */ cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */ - const char *directive, /* I - Directive */ - const char *value) /* I - Value */ + const char *directive, /* I - Directive */ + const char *value) /* I - Value */ { ssize_t bytes, /* Number of bytes written */ temp; /* Temporary byte count */ - const char *ptr; /* Pointer into value */ if (!fp || !directive || !*directive) @@ -1518,34 +1525,84 @@ cupsFilePutConf(cups_file_t *fp, /* I - CUPS file */ if ((bytes = cupsFilePuts(fp, directive)) < 0) return (-1); - if (cupsFilePutChar(fp, ' ') < 0) - return (-1); - bytes ++; - if (value && *value) { - if ((ptr = strchr(value, '#')) != NULL) + const char *start, // Start of current fragment + *ptr; // Pointer into value + + if (cupsFilePutChar(fp, ' ') < 0) + return (-1); + bytes ++; + + for (start = ptr = value; *ptr; ptr ++) + { + if (strchr("#\\\n\r", *ptr) != NULL) + { + /* + * Character that needs to be escaped... + */ + + if (ptr > start) + { + /* + * Write unescaped portion... + */ + + if ((temp = cupsFileWrite(fp, start, (size_t)(ptr - start))) < 0) + return (-1); + + bytes += temp; + } + + start = ptr + 1; + + if (*ptr == '\\') + { + /* + * "\" (for escaping) + */ + + if (cupsFilePuts(fp, "\\\\") < 0) + return (-1); + } + else if (*ptr == '#') + { + /* + * "#" (for comment) + */ + + if (cupsFilePuts(fp, "\\#") < 0) + return (-1); + } + else if (*ptr == '\n') + { + /* + * LF + */ + + if (cupsFilePuts(fp, "\\n") < 0) + return (-1); + } + else if (cupsFilePuts(fp, "\\r") < 0) + { + return (-1); + } + + bytes += 2; + } + } + + if (ptr > start) { /* - * Need to quote the first # in the info string... + * Write remaining unescaped portion... */ - if ((temp = cupsFileWrite(fp, value, (size_t)(ptr - value))) < 0) - return (-1); - bytes += temp; - - if (cupsFilePutChar(fp, '\\') < 0) - return (-1); - bytes ++; + if ((temp = cupsFileWrite(fp, start, (size_t)(ptr - start))) < 0) + return (-1); - if ((temp = cupsFilePuts(fp, ptr)) < 0) - return (-1); bytes += temp; } - else if ((temp = cupsFilePuts(fp, value)) < 0) - return (-1); - else - bytes += temp; } if (cupsFilePutChar(fp, '\n') < 0) diff --git a/cups/testfile.c b/cups/testfile.c index 4f8fbefaf4..97293f9399 100644 --- a/cups/testfile.c +++ b/cups/testfile.c @@ -552,6 +552,17 @@ read_write_tests(int compression) /* I - Use compression? */ off_t length; /* Length of file */ static const char *partial_line = "partial line"; /* Partial line */ + static const char * const values[] = /* cupsFileGet/PutConf values */ + { + "simple", + "'single-quoted value'", + "\"double-quoted value\"", + "value with # comment", + "value with \n newline", + "value with \r carriage return", + "value with \\ backslash", + "pathological\r\nvalue\\#with comment" + }; /* @@ -628,6 +639,27 @@ read_write_tests(int compression) /* I - Use compression? */ status ++; } + /* + * cupsFilePutConf() + */ + + fputs("cupsFilePutConf(): ", stdout); + for (i = 0; i < (int)(sizeof(values) / sizeof(values[0])); i ++) + { + if (cupsFilePutConf(fp, "TestConf", values[i]) < 0) + break; + } + + if (i >= (int)(sizeof(values) / sizeof(values[0]))) + { + puts("PASS"); + } + else + { + printf("FAIL (%s)\n", strerror(errno)); + status ++; + } + /* * cupsFilePutChar() */ @@ -684,11 +716,11 @@ read_write_tests(int compression) /* I - Use compression? */ fputs("cupsFileTell(): ", stdout); - if ((length = cupsFileTell(fp)) == 81933283) + if ((length = cupsFileTell(fp)) == 81933542) puts("PASS"); else { - printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length); + printf("FAIL (" CUPS_LLFMT " instead of 81933542)\n", CUPS_LLCAST length); status ++; } @@ -766,7 +798,7 @@ read_write_tests(int compression) /* I - Use compression? */ linenum = 1; - fputs("cupsFileGetConf(): ", stdout); + fputs("cupsFileGetConf(TestLine): ", stdout); for (i = 0, value = NULL; i < 1000; i ++) if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) @@ -789,6 +821,27 @@ read_write_tests(int compression) /* I - Use compression? */ status ++; } + fputs("cupsFileGetConf(TestConf): ", stdout); + + for (i = 0; i < (int)(sizeof(values) / sizeof(values[0])); i ++) + { + if (!cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) + { + printf("FAIL (%s)\n", strerror(errno)); + status ++; + break; + } + else if (_cups_strcasecmp(line, "TestConf") || !value || strcmp(value, values[i])) + { + printf("FAIL (Expected 'TestConf %s', got '%s %s')\n", values[i], line, value); + status ++; + break; + } + } + + if (i >= (int)(sizeof(values) / sizeof(values[0]))) + puts("PASS"); + /* * cupsFileGetChar() */ @@ -869,11 +922,11 @@ read_write_tests(int compression) /* I - Use compression? */ fputs("cupsFileTell(): ", stdout); - if ((length = cupsFileTell(fp)) == 81933283) + if ((length = cupsFileTell(fp)) == 81933542) puts("PASS"); else { - printf("FAIL (" CUPS_LLFMT " instead of 81933283)\n", CUPS_LLCAST length); + printf("FAIL (" CUPS_LLFMT " instead of 81933542)\n", CUPS_LLCAST length); status ++; } -- 2.52.0