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