1/* 2 * "$Id: ppdx.c 3835 2012-05-23 22:57:19Z msweet $" 3 * 4 * Example code for encoding and decoding large amounts of data in a PPD file. 5 * This would typically be used in a driver to save configuration/state 6 * information that could be used by an application. 7 * 8 * Copyright 2012 by Apple Inc. 9 * 10 * These coded instructions, statements, and computer programs are the 11 * property of Apple Inc. and are protected by Federal copyright 12 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 13 * which should have been included with this file. If this file is 14 * file is missing or damaged, see the license at "http://www.cups.org/". 15 * 16 * This file is subject to the Apple OS-Developed Software exception. 17 * 18 * Contents: 19 * 20 * ppdxReadData() - Read encoded data from a ppd_file_t *. 21 * ppdxWriteData() - Writes encoded data to stderr using PPD: messages. 22 */ 23 24/* 25 * Include necessary headers... 26 */ 27 28#include "ppdx.h" 29#include <stdio.h> 30#include <stdlib.h> 31#include <string.h> 32#include <zlib.h> /* For compression of the data */ 33 34 35/* 36 * Constants... 37 */ 38 39#define PPDX_MAX_VALUE (PPD_MAX_LINE - PPD_MAX_NAME - 4) 40 /* Max value length with delimiters + nul */ 41#define PPDX_MAX_CHUNK (PPDX_MAX_VALUE * 3 / 4) 42 /* Max length of each chunk when Base64-encoded */ 43 44 45/* 46 * 'ppdxReadData()' - Read encoded data from a ppd_file_t *. 47 * 48 * Reads chunked data in the PPD file "ppd" using the prefix "name". Returns 49 * an allocated pointer to the data (which is nul-terminated for convenience) 50 * along with the length of the data in the variable pointed to by "datasize", 51 * which can be NULL to indicate the caller doesn't need the length. 52 * 53 * Returns NULL if no data is present in the PPD with the prefix. 54 */ 55 56void * /* O - Data or NULL */ 57ppdxReadData(ppd_file_t *ppd, /* I - PPD file */ 58 const char *name, /* I - Keyword prefix */ 59 size_t *datasize) /* O - Size of data or NULL for don't care */ 60{ 61 char keyword[PPD_MAX_NAME], /* Keyword name */ 62 decoded[PPDX_MAX_CHUNK + 1]; 63 /* Decoded string */ 64 unsigned chunk = 0; /* Current chunk number */ 65 int len; /* Length of current chunk */ 66 ppd_attr_t *attr; /* Keyword/value from PPD file */ 67 Bytef *data; /* Pointer to data */ 68 size_t alloc_size; /* Allocated size of data buffer */ 69 z_stream decomp; /* Decompressor stream */ 70 int error; /* Error/status from inflate() */ 71 72 73 /* 74 * Range check input... 75 */ 76 77 if (datasize) 78 *datasize = 0; 79 80 if (!ppd || !name) 81 return (NULL); 82 83 /* 84 * First see if there are any instances of the named keyword in the PPD... 85 */ 86 87 snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); 88 if ((attr = ppdFindAttr(ppd, keyword, NULL)) == NULL) 89 return (NULL); 90 91 /* 92 * Allocate some memory and start decoding... 93 */ 94 95 data = malloc(257); 96 alloc_size = 256; 97 98 memset(&decomp, 0, sizeof(decomp)); 99 decomp.next_out = data; 100 decomp.avail_out = 256; 101 102 inflateInit(&decomp); 103 104 do 105 { 106 /* 107 * Grab the data from the current attribute and decode it... 108 */ 109 110 len = sizeof(decoded); 111 if (!httpDecode64_2(decoded, &len, attr->value) || len == 0) 112 break; 113 114// printf("chunk %04x has length %d\n", chunk, len); 115 116 /* 117 * Decompress this chunk... 118 */ 119 120 decomp.next_in = decoded; 121 decomp.avail_in = len; 122 123 do 124 { 125 Bytef *temp; /* Temporary pointer */ 126 size_t temp_size; /* Temporary allocation size */ 127 128// printf("Before inflate: avail_in=%d, avail_out=%d\n", decomp.avail_in, 129// decomp.avail_out); 130 131 if ((error = inflate(&decomp, Z_NO_FLUSH)) < Z_OK) 132 { 133 fprintf(stderr, "ERROR: inflate returned %d (%s)\n", error, decomp.msg); 134 break; 135 } 136 137// printf("After inflate: avail_in=%d, avail_out=%d, error=%d\n", 138// decomp.avail_in, decomp.avail_out, error); 139 140 if (decomp.avail_out == 0) 141 { 142 if (alloc_size < 2048) 143 temp_size = alloc_size * 2; 144 else if (alloc_size < PPDX_MAX_DATA) 145 temp_size = alloc_size + 2048; 146 else 147 break; 148 149 if ((temp = realloc(data, temp_size + 1)) == NULL) 150 { 151 free(data); 152 return (NULL); 153 } 154 155 decomp.next_out = temp + (decomp.next_out - data); 156 decomp.avail_out = temp_size - alloc_size; 157 data = temp; 158 alloc_size = temp_size; 159 } 160 } 161 while (decomp.avail_in > 0); 162 163 chunk ++; 164 snprintf(keyword, sizeof(keyword), "%s%04x", name, chunk); 165 } 166 while ((attr = ppdFindAttr(ppd, keyword, NULL)) != NULL); 167 168 inflateEnd(&decomp); 169 170 /* 171 * Nul-terminate the data (usually a string)... 172 */ 173 174 *(decomp.next_out) = '\0'; 175 176 if (datasize) 177 *datasize = decomp.next_out - data; 178 179 return (data); 180} 181 182 183/* 184 * 'ppdxWriteData()' - Writes encoded data to stderr using PPD: messages. 185 * 186 * Writes chunked data to the PPD file using PPD: messages sent to stderr for 187 * cupsd. "name" must be a valid PPD keyword string whose length is less than 188 * 37 characters to allow for chunk numbering. "data" provides a pointer to the 189 * data to be written, and "datasize" provides the length. 190 */ 191 192extern void 193ppdxWriteData(const char *name, /* I - Base name of keyword */ 194 const void *data, /* I - Data to write */ 195 size_t datasize) /* I - Number of bytes in data */ 196{ 197 char buffer[PPDX_MAX_CHUNK], /* Chunk buffer */ 198 encoded[PPDX_MAX_VALUE + 1], 199 /* Encoded data */ 200 pair[PPD_MAX_LINE], /* name=value pair */ 201 line[PPDX_MAX_STATUS], /* Line buffer */ 202 *lineptr, /* Current position in line buffer */ 203 *lineend; /* End of line buffer */ 204 unsigned chunk = 0; /* Current chunk number */ 205 int len; /* Length of current chunk */ 206 z_stream comp; /* Compressor stream */ 207 int error; /* Error/status from deflate() */ 208 209 210 /* 211 * Range check input... 212 */ 213 214 if (!name || (!data && datasize > 0) || datasize > PPDX_MAX_DATA) 215 return; 216 217 strlcpy(line, "PPD:", sizeof(line)); 218 lineptr = line + 4; 219 lineend = line + sizeof(line) - 2; 220 221 if (datasize > 0) 222 { 223 /* 224 * Compress and encode output... 225 */ 226 227 memset(&comp, 0, sizeof(comp)); 228 comp.next_in = (Bytef *)data; 229 comp.avail_in = datasize; 230 231 deflateInit(&comp, 9); 232 233 do 234 { 235 /* 236 * Compress a chunk... 237 */ 238 239 comp.next_out = buffer; 240 comp.avail_out = sizeof(buffer); 241 242 if ((error = deflate(&comp, Z_FINISH)) < Z_OK) 243 { 244 fprintf(stderr, "ERROR: deflate returned %d (%s)\n", error, comp.msg); 245 break; 246 } 247 248 /* 249 * Write a chunk... 250 */ 251 252 len = sizeof(buffer) - comp.avail_out; 253 httpEncode64_2(encoded, sizeof(encoded), buffer, len); 254 255 len = (int)snprintf(pair, sizeof(pair), " %s%04x=%s", name, chunk, 256 encoded); 257#ifdef DEBUG 258 fprintf(stderr, "DEBUG: *%s%04x: \"%s\"\n", name, chunk, encoded); 259#endif /* DEBUG */ 260 261 if ((lineptr + len) >= lineend) 262 { 263 *lineptr++ = '\n'; 264 *lineptr = '\0'; 265 266 fputs(line, stderr); 267 lineptr = line + 4; 268 } 269 270 strlcpy(lineptr, pair, lineend - lineptr); 271 lineptr += len; 272 273 /* 274 * Setup for the next one... 275 */ 276 277 chunk ++; 278 } 279 while (comp.avail_out == 0); 280 } 281 282 deflateEnd(&comp); 283 284 /* 285 * Write a trailing empty chunk to signal EOD... 286 */ 287 288 len = (int)snprintf(pair, sizeof(pair), " %s%04x=\"\"", name, chunk); 289#ifdef DEBUG 290 fprintf(stderr, "DEBUG: *%s%04x: \"\"\n", name, chunk); 291#endif /* DEBUG */ 292 293 if ((lineptr + len) >= lineend) 294 { 295 *lineptr++ = '\n'; 296 *lineptr = '\0'; 297 298 fputs(line, stderr); 299 lineptr = line + 4; 300 } 301 302 strlcpy(lineptr, pair, lineend - lineptr); 303 lineptr += len; 304 305 *lineptr++ = '\n'; 306 *lineptr = '\0'; 307 308 fputs(line, stderr); 309} 310 311 312/* 313 * End of "$Id: ppdx.c 3835 2012-05-23 22:57:19Z msweet $". 314 */ 315