| /* |
| * Example code for encoding and decoding large amounts of data in a PPD file. |
| * This would typically be used in a driver to save configuration/state |
| * information that could be used by an application. |
| * |
| * Copyright 2012 by Apple Inc. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "ppdx.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <zlib.h> /* For compression of the data */ |
| |
| |
| /* |
| * Constants... |
| */ |
| |
| #define PPDX_MAX_VALUE (PPD_MAX_LINE - PPD_MAX_NAME - 4) |
| /* Max value length with delimiters + nul */ |
| #define PPDX_MAX_CHUNK (PPDX_MAX_VALUE * 3 / 4) |
| /* Max length of each chunk when Base64-encoded */ |
| |
| |
| /* |
| * 'ppdxReadData()' - Read encoded data from a ppd_file_t *. |
| * |
| * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns |
| * an allocated pointer to the data (which is nul-terminated for convenience) |
| * along with the length of the data in the variable pointed to by "datasize", |
| * which can be NULL to indicate the caller doesn't need the length. |
| * |
| * Returns NULL if no data is present in the PPD with the prefix. |
| */ |
| |
| void * /* O - Data or NULL */ |
| ppdxReadData(ppd_file_t *ppd, /* I - PPD file */ |
| const char *name, /* I - Keyword prefix */ |
| size_t *datasize) /* O - Size of data or NULL for don't care */ |
| { |
| char keyword[PPD_MAX_NAME], /* Keyword name */ |
| decoded[PPDX_MAX_CHUNK + 1]; |
| /* Decoded string */ |
| unsigned chunk = 0; /* Current chunk number */ |
| int len; /* Length of current chunk */ |
| ppd_attr_t *attr; /* Keyword/value from PPD file */ |
| Bytef *data; /* Pointer to data */ |
| size_t alloc_size; /* Allocated size of data buffer */ |
| z_stream decomp; /* Decompressor stream */ |
| int error; /* Error/status from inflate() */ |
| |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (datasize) |
| *datasize = 0; |
| |
| if (!ppd || !name) |
| return (NULL); |
| |
| /* |
| * First see if there are any instances of the named keyword in the PPD... |
| */ |
| |
| snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); |
| if ((attr = ppdFindAttr(ppd, keyword, NULL)) == NULL) |
| return (NULL); |
| |
| /* |
| * Allocate some memory and start decoding... |
| */ |
| |
| data = malloc(257); |
| alloc_size = 256; |
| |
| memset(&decomp, 0, sizeof(decomp)); |
| decomp.next_out = data; |
| decomp.avail_out = 256; |
| |
| inflateInit(&decomp); |
| |
| do |
| { |
| /* |
| * Grab the data from the current attribute and decode it... |
| */ |
| |
| len = sizeof(decoded); |
| if (!httpDecode64_2(decoded, &len, attr->value) || len == 0) |
| break; |
| |
| // printf("chunk %04x has length %d\n", chunk, len); |
| |
| /* |
| * Decompress this chunk... |
| */ |
| |
| decomp.next_in = decoded; |
| decomp.avail_in = len; |
| |
| do |
| { |
| Bytef *temp; /* Temporary pointer */ |
| size_t temp_size; /* Temporary allocation size */ |
| |
| // printf("Before inflate: avail_in=%d, avail_out=%d\n", decomp.avail_in, |
| // decomp.avail_out); |
| |
| if ((error = inflate(&decomp, Z_NO_FLUSH)) < Z_OK) |
| { |
| fprintf(stderr, "ERROR: inflate returned %d (%s)\n", error, decomp.msg); |
| break; |
| } |
| |
| // printf("After inflate: avail_in=%d, avail_out=%d, error=%d\n", |
| // decomp.avail_in, decomp.avail_out, error); |
| |
| if (decomp.avail_out == 0) |
| { |
| if (alloc_size < 2048) |
| temp_size = alloc_size * 2; |
| else if (alloc_size < PPDX_MAX_DATA) |
| temp_size = alloc_size + 2048; |
| else |
| break; |
| |
| if ((temp = realloc(data, temp_size + 1)) == NULL) |
| { |
| free(data); |
| return (NULL); |
| } |
| |
| decomp.next_out = temp + (decomp.next_out - data); |
| decomp.avail_out = temp_size - alloc_size; |
| data = temp; |
| alloc_size = temp_size; |
| } |
| } |
| while (decomp.avail_in > 0); |
| |
| chunk ++; |
| snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); |
| } |
| while ((attr = ppdFindAttr(ppd, keyword, NULL)) != NULL); |
| |
| inflateEnd(&decomp); |
| |
| /* |
| * Nul-terminate the data (usually a string)... |
| */ |
| |
| *(decomp.next_out) = '\0'; |
| |
| if (datasize) |
| *datasize = decomp.next_out - data; |
| |
| return (data); |
| } |
| |
| |
| /* |
| * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages. |
| * |
| * Writes chunked data to the PPD file using PPD: messages sent to stderr for |
| * cupsd. "name" must be a valid PPD keyword string whose length is less than |
| * 37 characters to allow for chunk numbering. "data" provides a pointer to the |
| * data to be written, and "datasize" provides the length. |
| */ |
| |
| extern void |
| ppdxWriteData(const char *name, /* I - Base name of keyword */ |
| const void *data, /* I - Data to write */ |
| size_t datasize) /* I - Number of bytes in data */ |
| { |
| char buffer[PPDX_MAX_CHUNK], /* Chunk buffer */ |
| encoded[PPDX_MAX_VALUE + 1], |
| /* Encoded data */ |
| pair[PPD_MAX_LINE], /* name=value pair */ |
| line[PPDX_MAX_STATUS], /* Line buffer */ |
| *lineptr, /* Current position in line buffer */ |
| *lineend; /* End of line buffer */ |
| unsigned chunk = 0; /* Current chunk number */ |
| int len; /* Length of current chunk */ |
| z_stream comp; /* Compressor stream */ |
| int error; /* Error/status from deflate() */ |
| |
| |
| /* |
| * Range check input... |
| */ |
| |
| if (!name || (!data && datasize > 0) || datasize > PPDX_MAX_DATA) |
| return; |
| |
| strlcpy(line, "PPD:", sizeof(line)); |
| lineptr = line + 4; |
| lineend = line + sizeof(line) - 2; |
| |
| if (datasize > 0) |
| { |
| /* |
| * Compress and encode output... |
| */ |
| |
| memset(&comp, 0, sizeof(comp)); |
| comp.next_in = (Bytef *)data; |
| comp.avail_in = datasize; |
| |
| deflateInit(&comp, 9); |
| |
| do |
| { |
| /* |
| * Compress a chunk... |
| */ |
| |
| comp.next_out = buffer; |
| comp.avail_out = sizeof(buffer); |
| |
| if ((error = deflate(&comp, Z_FINISH)) < Z_OK) |
| { |
| fprintf(stderr, "ERROR: deflate returned %d (%s)\n", error, comp.msg); |
| break; |
| } |
| |
| /* |
| * Write a chunk... |
| */ |
| |
| len = sizeof(buffer) - comp.avail_out; |
| httpEncode64_2(encoded, sizeof(encoded), buffer, len); |
| |
| len = (int)snprintf(pair, sizeof(pair), " %s%04x=%s", name, chunk, |
| encoded); |
| #ifdef DEBUG |
| fprintf(stderr, "DEBUG: *%s%04x: \"%s\"\n", name, chunk, encoded); |
| #endif /* DEBUG */ |
| |
| if ((lineptr + len) >= lineend) |
| { |
| *lineptr++ = '\n'; |
| *lineptr = '\0'; |
| |
| fputs(line, stderr); |
| lineptr = line + 4; |
| } |
| |
| strlcpy(lineptr, pair, lineend - lineptr); |
| lineptr += len; |
| |
| /* |
| * Setup for the next one... |
| */ |
| |
| chunk ++; |
| } |
| while (comp.avail_out == 0); |
| } |
| |
| deflateEnd(&comp); |
| |
| /* |
| * Write a trailing empty chunk to signal EOD... |
| */ |
| |
| len = (int)snprintf(pair, sizeof(pair), " %s%04x=\"\"", name, chunk); |
| #ifdef DEBUG |
| fprintf(stderr, "DEBUG: *%s%04x: \"\"\n", name, chunk); |
| #endif /* DEBUG */ |
| |
| if ((lineptr + len) >= lineend) |
| { |
| *lineptr++ = '\n'; |
| *lineptr = '\0'; |
| |
| fputs(line, stderr); |
| lineptr = line + 4; |
| } |
| |
| strlcpy(lineptr, pair, lineend - lineptr); |
| lineptr += len; |
| |
| *lineptr++ = '\n'; |
| *lineptr = '\0'; |
| |
| fputs(line, stderr); |
| } |