• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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