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