• 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 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