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