1/*
2 * IPP test program for CUPS.
3 *
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 1997-2005 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file.  If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
12 *
13 * This file is subject to the Apple OS-Developed Software exception.
14 */
15
16/*
17 * Include necessary headers...
18 */
19
20#include "file.h"
21#include "string-private.h"
22#include "ipp-private.h"
23#ifdef WIN32
24#  include <io.h>
25#else
26#  include <unistd.h>
27#  include <fcntl.h>
28#endif /* WIN32 */
29
30
31/*
32 * Local types...
33 */
34
35typedef struct _ippdata_t
36{
37  size_t	rpos,			/* Read position */
38		wused,			/* Bytes used */
39		wsize;			/* Max size of buffer */
40  ipp_uchar_t	*wbuffer;		/* Buffer */
41} _ippdata_t;
42
43
44/*
45 * Local globals...
46 */
47
48static ipp_uchar_t collection[] =	/* Collection buffer */
49		{
50		  0x01, 0x01,		/* IPP version */
51		  0x00, 0x02,		/* Print-Job operation */
52		  0x00, 0x00, 0x00, 0x01,
53		  			/* Request ID */
54
55		  IPP_TAG_OPERATION,
56
57		  IPP_TAG_CHARSET,
58		  0x00, 0x12,		/* Name length + name */
59		  'a','t','t','r','i','b','u','t','e','s','-',
60		  'c','h','a','r','s','e','t',
61		  0x00, 0x05,		/* Value length + value */
62		  'u','t','f','-','8',
63
64		  IPP_TAG_LANGUAGE,
65		  0x00, 0x1b,		/* Name length + name */
66		  'a','t','t','r','i','b','u','t','e','s','-',
67		  'n','a','t','u','r','a','l','-','l','a','n',
68		  'g','u','a','g','e',
69		  0x00, 0x02,		/* Value length + value */
70		  'e','n',
71
72		  IPP_TAG_URI,
73		  0x00, 0x0b,		/* Name length + name */
74		  'p','r','i','n','t','e','r','-','u','r','i',
75		  0x00, 0x1c,			/* Value length + value */
76		  'i','p','p',':','/','/','l','o','c','a','l',
77		  'h','o','s','t','/','p','r','i','n','t','e',
78		  'r','s','/','f','o','o',
79
80		  IPP_TAG_JOB,		/* job group tag */
81
82		  IPP_TAG_BEGIN_COLLECTION,
83		  			/* begCollection tag */
84		  0x00, 0x09,		/* Name length + name */
85		  'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l',
86		  0x00, 0x00,		/* No value */
87		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
88		    0x00, 0x00,		/* No name */
89		    0x00, 0x0a,		/* Value length + value */
90		    'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
91		    IPP_TAG_BEGIN_COLLECTION,
92		    			/* begCollection tag */
93		    0x00, 0x00,		/* Name length + name */
94		    0x00, 0x00,		/* No value */
95		      IPP_TAG_MEMBERNAME,
96		      			/* memberAttrName tag */
97		      0x00, 0x00,	/* No name */
98		      0x00, 0x0b,	/* Value length + value */
99		      'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
100		      IPP_TAG_INTEGER,	/* integer tag */
101		      0x00, 0x00,	/* No name */
102		      0x00, 0x04,	/* Value length + value */
103		      0x00, 0x00, 0x54, 0x56,
104		      IPP_TAG_MEMBERNAME,
105		      			/* memberAttrName tag */
106		      0x00, 0x00,	/* No name */
107		      0x00, 0x0b,	/* Value length + value */
108		      'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
109		      IPP_TAG_INTEGER,	/* integer tag */
110		      0x00, 0x00,	/* No name */
111		      0x00, 0x04,	/* Value length + value */
112		      0x00, 0x00, 0x6d, 0x24,
113		    IPP_TAG_END_COLLECTION,
114		    			/* endCollection tag */
115		    0x00, 0x00,		/* No name */
116		    0x00, 0x00,		/* No value */
117		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
118		    0x00, 0x00,		/* No name */
119		    0x00, 0x0b,		/* Value length + value */
120		    'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
121		    IPP_TAG_KEYWORD,	/* keyword tag */
122		    0x00, 0x00,		/* No name */
123		    0x00, 0x04,		/* Value length + value */
124		    'b', 'l', 'u', 'e',
125
126		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
127		    0x00, 0x00,		/* No name */
128		    0x00, 0x0a,		/* Value length + value */
129		    'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
130		    IPP_TAG_KEYWORD,	/* keyword tag */
131		    0x00, 0x00,		/* No name */
132		    0x00, 0x05,		/* Value length + value */
133		    'p', 'l', 'a', 'i', 'n',
134		  IPP_TAG_END_COLLECTION,
135		  			/* endCollection tag */
136		  0x00, 0x00,		/* No name */
137		  0x00, 0x00,		/* No value */
138
139		  IPP_TAG_BEGIN_COLLECTION,
140		  			/* begCollection tag */
141		  0x00, 0x00,		/* No name */
142		  0x00, 0x00,		/* No value */
143		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
144		    0x00, 0x00,		/* No name */
145		    0x00, 0x0a,		/* Value length + value */
146		    'm', 'e', 'd', 'i', 'a', '-', 's', 'i', 'z', 'e',
147		    IPP_TAG_BEGIN_COLLECTION,
148		    			/* begCollection tag */
149		    0x00, 0x00,		/* Name length + name */
150		    0x00, 0x00,		/* No value */
151		      IPP_TAG_MEMBERNAME,
152		      			/* memberAttrName tag */
153		      0x00, 0x00,	/* No name */
154		      0x00, 0x0b,	/* Value length + value */
155		      'x', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
156		      IPP_TAG_INTEGER,	/* integer tag */
157		      0x00, 0x00,	/* No name */
158		      0x00, 0x04,	/* Value length + value */
159		      0x00, 0x00, 0x52, 0x08,
160		      IPP_TAG_MEMBERNAME,
161		      			/* memberAttrName tag */
162		      0x00, 0x00,	/* No name */
163		      0x00, 0x0b,	/* Value length + value */
164		      'y', '-', 'd', 'i', 'm', 'e', 'n', 's', 'i', 'o', 'n',
165		      IPP_TAG_INTEGER,	/* integer tag */
166		      0x00, 0x00,	/* No name */
167		      0x00, 0x04,	/* Value length + value */
168		      0x00, 0x00, 0x74, 0x04,
169		    IPP_TAG_END_COLLECTION,
170		    			/* endCollection tag */
171		    0x00, 0x00,		/* No name */
172		    0x00, 0x00,		/* No value */
173		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
174		    0x00, 0x00,		/* No name */
175		    0x00, 0x0b,		/* Value length + value */
176		    'm', 'e', 'd', 'i', 'a', '-', 'c', 'o', 'l', 'o', 'r',
177		    IPP_TAG_KEYWORD,	/* keyword tag */
178		    0x00, 0x00,		/* No name */
179		    0x00, 0x05,		/* Value length + value */
180		    'p', 'l', 'a', 'i', 'd',
181
182		    IPP_TAG_MEMBERNAME,	/* memberAttrName tag */
183		    0x00, 0x00,		/* No name */
184		    0x00, 0x0a,		/* Value length + value */
185		    'm', 'e', 'd', 'i', 'a', '-', 't', 'y', 'p', 'e',
186		    IPP_TAG_KEYWORD,	/* keyword tag */
187		    0x00, 0x00,		/* No name */
188		    0x00, 0x06,		/* Value length + value */
189		    'g', 'l', 'o', 's', 's', 'y',
190		  IPP_TAG_END_COLLECTION,
191		  			/* endCollection tag */
192		  0x00, 0x00,		/* No name */
193		  0x00, 0x00,		/* No value */
194
195		  IPP_TAG_END		/* end tag */
196		};
197
198static ipp_uchar_t mixed[] =		/* Mixed value buffer */
199		{
200		  0x01, 0x01,		/* IPP version */
201		  0x00, 0x02,		/* Print-Job operation */
202		  0x00, 0x00, 0x00, 0x01,
203		  			/* Request ID */
204
205		  IPP_TAG_OPERATION,
206
207		  IPP_TAG_INTEGER,	/* integer tag */
208		  0x00, 0x1f,		/* Name length + name */
209		  'n', 'o', 't', 'i', 'f', 'y', '-', 'l', 'e', 'a', 's', 'e',
210		  '-', 'd', 'u', 'r', 'a', 't', 'i', 'o', 'n', '-', 's', 'u',
211		  'p', 'p', 'o', 'r', 't', 'e', 'd',
212		  0x00, 0x04,		/* Value length + value */
213		  0x00, 0x00, 0x00, 0x01,
214
215		  IPP_TAG_RANGE,	/* rangeOfInteger tag */
216		  0x00, 0x00,		/* No name */
217		  0x00, 0x08,		/* Value length + value */
218		  0x00, 0x00, 0x00, 0x10,
219		  0x00, 0x00, 0x00, 0x20,
220
221		  IPP_TAG_END		/* end tag */
222		};
223
224
225/*
226 * Local functions...
227 */
228
229void	hex_dump(const char *title, ipp_uchar_t *buffer, size_t bytes);
230void	print_attributes(ipp_t *ipp, int indent);
231ssize_t	read_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
232ssize_t	write_cb(_ippdata_t *data, ipp_uchar_t *buffer, size_t bytes);
233
234
235/*
236 * 'main()' - Main entry.
237 */
238
239int				/* O - Exit status */
240main(int  argc,			/* I - Number of command-line arguments */
241     char *argv[])		/* I - Command-line arguments */
242{
243  _ippdata_t	data;		/* IPP buffer */
244  ipp_uchar_t	buffer[8192];	/* Write buffer data */
245  ipp_t		*cols[2],	/* Collections */
246		*size;		/* media-size collection */
247  ipp_t		*request;	/* Request */
248  ipp_attribute_t *media_col,	/* media-col attribute */
249		*media_size,	/* media-size attribute */
250		*attr;		/* Other attribute */
251  ipp_state_t	state;		/* State */
252  size_t	length;		/* Length of data */
253  cups_file_t	*fp;		/* File pointer */
254  size_t	i;		/* Looping var */
255  int		status;		/* Status of tests (0 = success, 1 = fail) */
256#ifdef DEBUG
257  const char	*name;		/* Option name */
258#endif /* DEBUG */
259
260
261  status = 0;
262
263  if (argc == 1)
264  {
265   /*
266    * Test request generation code...
267    */
268
269    printf("Create Sample Request: ");
270
271    request = ippNew();
272    request->request.op.version[0]   = 0x01;
273    request->request.op.version[1]   = 0x01;
274    request->request.op.operation_id = IPP_OP_PRINT_JOB;
275    request->request.op.request_id   = 1;
276
277    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
278        	 "attributes-charset", NULL, "utf-8");
279    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
280        	 "attributes-natural-language", NULL, "en");
281    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
282        	 "printer-uri", NULL, "ipp://localhost/printers/foo");
283
284    cols[0] = ippNew();
285    size    = ippNew();
286    ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", 21590);
287    ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", 27940);
288    ippAddCollection(cols[0], IPP_TAG_JOB, "media-size", size);
289    ippDelete(size);
290    ippAddString(cols[0], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-color", NULL,
291                 "blue");
292    ippAddString(cols[0], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
293                 "plain");
294
295    cols[1] = ippNew();
296    size    = ippNew();
297    ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "x-dimension", 21000);
298    ippAddInteger(size, IPP_TAG_ZERO, IPP_TAG_INTEGER, "y-dimension", 29700);
299    ippAddCollection(cols[1], IPP_TAG_JOB, "media-size", size);
300    ippDelete(size);
301    ippAddString(cols[1], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-color", NULL,
302                 "plaid");
303    ippAddString(cols[1], IPP_TAG_JOB, IPP_TAG_KEYWORD, "media-type", NULL,
304		 "glossy");
305
306    ippAddCollections(request, IPP_TAG_JOB, "media-col", 2,
307                      (const ipp_t **)cols);
308    ippDelete(cols[0]);
309    ippDelete(cols[1]);
310
311    length = ippLength(request);
312    if (length != sizeof(collection))
313    {
314      printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
315             (int)length, (int)sizeof(collection));
316      status = 1;
317    }
318    else
319      puts("PASS");
320
321   /*
322    * Write test #1...
323    */
324
325    printf("Write Sample to Memory: ");
326
327    data.wused   = 0;
328    data.wsize   = sizeof(buffer);
329    data.wbuffer = buffer;
330
331    while ((state = ippWriteIO(&data, (ipp_iocb_t)write_cb, 1, NULL,
332                               request)) != IPP_STATE_DATA)
333      if (state == IPP_STATE_ERROR)
334	break;
335
336    if (state != IPP_STATE_DATA)
337    {
338      printf("FAIL - %d bytes written.\n", (int)data.wused);
339      status = 1;
340    }
341    else if (data.wused != sizeof(collection))
342    {
343      printf("FAIL - wrote %d bytes, expected %d bytes!\n", (int)data.wused,
344             (int)sizeof(collection));
345      hex_dump("Bytes Written", data.wbuffer, data.wused);
346      hex_dump("Baseline", collection, sizeof(collection));
347      status = 1;
348    }
349    else if (memcmp(data.wbuffer, collection, data.wused))
350    {
351      for (i = 0; i < data.wused; i ++)
352        if (data.wbuffer[i] != collection[i])
353	  break;
354
355      printf("FAIL - output does not match baseline at 0x%04x!\n", (unsigned)i);
356      hex_dump("Bytes Written", data.wbuffer, data.wused);
357      hex_dump("Baseline", collection, sizeof(collection));
358      status = 1;
359    }
360    else
361      puts("PASS");
362
363    ippDelete(request);
364
365   /*
366    * Read the data back in and confirm...
367    */
368
369    printf("Read Sample from Memory: ");
370
371    request     = ippNew();
372    data.rpos = 0;
373
374    while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
375                              request)) != IPP_STATE_DATA)
376      if (state == IPP_STATE_ERROR)
377	break;
378
379    length = ippLength(request);
380
381    if (state != IPP_STATE_DATA)
382    {
383      printf("FAIL - %d bytes read.\n", (int)data.rpos);
384      status = 1;
385    }
386    else if (data.rpos != data.wused)
387    {
388      printf("FAIL - read %d bytes, expected %d bytes!\n", (int)data.rpos,
389             (int)data.wused);
390      print_attributes(request, 8);
391      status = 1;
392    }
393    else if (length != sizeof(collection))
394    {
395      printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
396             (int)length, (int)sizeof(collection));
397      print_attributes(request, 8);
398      status = 1;
399    }
400    else
401      puts("PASS");
402
403    fputs("ippFindAttribute(media-col): ", stdout);
404    if ((media_col = ippFindAttribute(request, "media-col",
405                                      IPP_TAG_BEGIN_COLLECTION)) == NULL)
406    {
407      if ((media_col = ippFindAttribute(request, "media-col",
408                                        IPP_TAG_ZERO)) == NULL)
409        puts("FAIL (not found)");
410      else
411        printf("FAIL (wrong type - %s)\n", ippTagString(media_col->value_tag));
412
413      status = 1;
414    }
415    else if (media_col->num_values != 2)
416    {
417      printf("FAIL (wrong count - %d)\n", media_col->num_values);
418      status = 1;
419    }
420    else
421      puts("PASS");
422
423    if (media_col)
424    {
425      fputs("ippFindAttribute(media-size 1): ", stdout);
426      if ((media_size = ippFindAttribute(media_col->values[0].collection,
427					 "media-size",
428					 IPP_TAG_BEGIN_COLLECTION)) == NULL)
429      {
430	if ((media_size = ippFindAttribute(media_col->values[0].collection,
431					   "media-col",
432					   IPP_TAG_ZERO)) == NULL)
433	  puts("FAIL (not found)");
434	else
435	  printf("FAIL (wrong type - %s)\n",
436	         ippTagString(media_size->value_tag));
437
438	status = 1;
439      }
440      else
441      {
442	if ((attr = ippFindAttribute(media_size->values[0].collection,
443				     "x-dimension", IPP_TAG_INTEGER)) == NULL)
444	{
445	  if ((attr = ippFindAttribute(media_size->values[0].collection,
446				       "x-dimension", IPP_TAG_ZERO)) == NULL)
447	    puts("FAIL (missing x-dimension)");
448	  else
449	    printf("FAIL (wrong type for x-dimension - %s)\n",
450		   ippTagString(attr->value_tag));
451
452	  status = 1;
453	}
454	else if (attr->values[0].integer != 21590)
455	{
456	  printf("FAIL (wrong value for x-dimension - %d)\n",
457		 attr->values[0].integer);
458	  status = 1;
459	}
460	else if ((attr = ippFindAttribute(media_size->values[0].collection,
461					  "y-dimension",
462					  IPP_TAG_INTEGER)) == NULL)
463	{
464	  if ((attr = ippFindAttribute(media_size->values[0].collection,
465				       "y-dimension", IPP_TAG_ZERO)) == NULL)
466	    puts("FAIL (missing y-dimension)");
467	  else
468	    printf("FAIL (wrong type for y-dimension - %s)\n",
469		   ippTagString(attr->value_tag));
470
471	  status = 1;
472	}
473	else if (attr->values[0].integer != 27940)
474	{
475	  printf("FAIL (wrong value for y-dimension - %d)\n",
476		 attr->values[0].integer);
477	  status = 1;
478	}
479	else
480	  puts("PASS");
481      }
482
483      fputs("ippFindAttribute(media-size 2): ", stdout);
484      if ((media_size = ippFindAttribute(media_col->values[1].collection,
485					 "media-size",
486					 IPP_TAG_BEGIN_COLLECTION)) == NULL)
487      {
488	if ((media_size = ippFindAttribute(media_col->values[1].collection,
489					   "media-col",
490					   IPP_TAG_ZERO)) == NULL)
491	  puts("FAIL (not found)");
492	else
493	  printf("FAIL (wrong type - %s)\n",
494	         ippTagString(media_size->value_tag));
495
496	status = 1;
497      }
498      else
499      {
500	if ((attr = ippFindAttribute(media_size->values[0].collection,
501				     "x-dimension",
502				     IPP_TAG_INTEGER)) == NULL)
503	{
504	  if ((attr = ippFindAttribute(media_size->values[0].collection,
505				       "x-dimension", IPP_TAG_ZERO)) == NULL)
506	    puts("FAIL (missing x-dimension)");
507	  else
508	    printf("FAIL (wrong type for x-dimension - %s)\n",
509		   ippTagString(attr->value_tag));
510
511	  status = 1;
512	}
513	else if (attr->values[0].integer != 21000)
514	{
515	  printf("FAIL (wrong value for x-dimension - %d)\n",
516		 attr->values[0].integer);
517	  status = 1;
518	}
519	else if ((attr = ippFindAttribute(media_size->values[0].collection,
520					  "y-dimension",
521					  IPP_TAG_INTEGER)) == NULL)
522	{
523	  if ((attr = ippFindAttribute(media_size->values[0].collection,
524				       "y-dimension", IPP_TAG_ZERO)) == NULL)
525	    puts("FAIL (missing y-dimension)");
526	  else
527	    printf("FAIL (wrong type for y-dimension - %s)\n",
528		   ippTagString(attr->value_tag));
529
530	  status = 1;
531	}
532	else if (attr->values[0].integer != 29700)
533	{
534	  printf("FAIL (wrong value for y-dimension - %d)\n",
535		 attr->values[0].integer);
536	  status = 1;
537	}
538	else
539	  puts("PASS");
540      }
541    }
542
543   /*
544    * Test hierarchical find...
545    */
546
547    fputs("ippFindAttribute(media-col/media-size/x-dimension): ", stdout);
548    if ((attr = ippFindAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
549    {
550      if (ippGetInteger(attr, 0) != 21590)
551      {
552        printf("FAIL (wrong value for x-dimension - %d)\n", ippGetInteger(attr, 0));
553        status = 1;
554      }
555      else
556        puts("PASS");
557    }
558    else
559    {
560      puts("FAIL (not found)");
561      status = 1;
562    }
563
564    fputs("ippFindNextAttribute(media-col/media-size/x-dimension): ", stdout);
565    if ((attr = ippFindNextAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
566    {
567      if (ippGetInteger(attr, 0) != 21000)
568      {
569        printf("FAIL (wrong value for x-dimension - %d)\n", ippGetInteger(attr, 0));
570        status = 1;
571      }
572      else
573        puts("PASS");
574    }
575    else
576    {
577      puts("FAIL (not found)");
578      status = 1;
579    }
580
581    fputs("ippFindNextAttribute(media-col/media-size/x-dimension) again: ", stdout);
582    if ((attr = ippFindNextAttribute(request, "media-col/media-size/x-dimension", IPP_TAG_INTEGER)) != NULL)
583    {
584      printf("FAIL (got %d, expected nothing)\n", ippGetInteger(attr, 0));
585      status = 1;
586    }
587    else
588      puts("PASS");
589
590    ippDelete(request);
591
592   /*
593    * Read the mixed data and confirm we converted everything to rangeOfInteger
594    * values...
595    */
596
597    printf("Read Mixed integer/rangeOfInteger from Memory: ");
598
599    request = ippNew();
600    data.rpos    = 0;
601    data.wused   = sizeof(mixed);
602    data.wsize   = sizeof(mixed);
603    data.wbuffer = mixed;
604
605    while ((state = ippReadIO(&data, (ipp_iocb_t)read_cb, 1, NULL,
606                              request)) != IPP_STATE_DATA)
607      if (state == IPP_STATE_ERROR)
608	break;
609
610    length = ippLength(request);
611
612    if (state != IPP_STATE_DATA)
613    {
614      printf("FAIL - %d bytes read.\n", (int)data.rpos);
615      status = 1;
616    }
617    else if (data.rpos != sizeof(mixed))
618    {
619      printf("FAIL - read %d bytes, expected %d bytes!\n", (int)data.rpos,
620             (int)sizeof(mixed));
621      print_attributes(request, 8);
622      status = 1;
623    }
624    else if (length != (sizeof(mixed) + 4))
625    {
626      printf("FAIL - wrong ippLength(), %d instead of %d bytes!\n",
627             (int)length, (int)sizeof(mixed) + 4);
628      print_attributes(request, 8);
629      status = 1;
630    }
631    else
632      puts("PASS");
633
634    fputs("ippFindAttribute(notify-lease-duration-supported): ", stdout);
635    if ((attr = ippFindAttribute(request, "notify-lease-duration-supported",
636                                 IPP_TAG_ZERO)) == NULL)
637    {
638      puts("FAIL (not found)");
639      status = 1;
640    }
641    else if (attr->value_tag != IPP_TAG_RANGE)
642    {
643      printf("FAIL (wrong type - %s)\n", ippTagString(attr->value_tag));
644      status = 1;
645    }
646    else if (attr->num_values != 2)
647    {
648      printf("FAIL (wrong count - %d)\n", attr->num_values);
649      status = 1;
650    }
651    else if (attr->values[0].range.lower != 1 ||
652             attr->values[0].range.upper != 1 ||
653             attr->values[1].range.lower != 16 ||
654             attr->values[1].range.upper != 32)
655    {
656      printf("FAIL (wrong values - %d,%d and %d,%d)\n",
657             attr->values[0].range.lower,
658             attr->values[0].range.upper,
659             attr->values[1].range.lower,
660             attr->values[1].range.upper);
661      status = 1;
662    }
663    else
664      puts("PASS");
665
666    ippDelete(request);
667
668#ifdef DEBUG
669   /*
670    * Test that private option array is sorted...
671    */
672
673    fputs("_ippCheckOptions: ", stdout);
674    if ((name = _ippCheckOptions()) == NULL)
675      puts("PASS");
676    else
677    {
678      printf("FAIL (\"%s\" out of order)\n", name);
679      status = 1;
680    }
681#endif /* DEBUG */
682
683   /*
684    * Test _ippFindOption() private API...
685    */
686
687    fputs("_ippFindOption(\"printer-type\"): ", stdout);
688    if (_ippFindOption("printer-type"))
689      puts("PASS");
690    else
691    {
692      puts("FAIL");
693      status = 1;
694    }
695
696   /*
697    * Summarize...
698    */
699
700    putchar('\n');
701
702    if (status)
703      puts("Core IPP tests failed.");
704    else
705      puts("Core IPP tests passed.");
706  }
707  else
708  {
709   /*
710    * Read IPP files...
711    */
712
713    for (i = 1; i < (size_t)argc; i ++)
714    {
715      if ((fp = cupsFileOpen(argv[i], "r")) == NULL)
716      {
717	printf("Unable to open \"%s\" - %s\n", argv[i], strerror(errno));
718	status = 1;
719	continue;
720      }
721
722      request = ippNew();
723      while ((state = ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL,
724                                request)) == IPP_STATE_ATTRIBUTE);
725
726      if (state != IPP_STATE_DATA)
727      {
728	printf("Error reading IPP message from \"%s\"!\n", argv[i]);
729	status = 1;
730      }
731      else
732      {
733	printf("\n%s:\n", argv[i]);
734	print_attributes(request, 4);
735      }
736
737      ippDelete(request);
738      cupsFileClose(fp);
739    }
740  }
741
742  return (status);
743}
744
745
746/*
747 * 'hex_dump()' - Produce a hex dump of a buffer.
748 */
749
750void
751hex_dump(const char  *title,		/* I - Title */
752         ipp_uchar_t *buffer,		/* I - Buffer to dump */
753         size_t      bytes)		/* I - Number of bytes */
754{
755  size_t	i, j;			/* Looping vars */
756  int		ch;			/* Current ASCII char */
757
758
759 /*
760  * Show lines of 16 bytes at a time...
761  */
762
763  printf("    %s:\n", title);
764
765  for (i = 0; i < bytes; i += 16)
766  {
767   /*
768    * Show the offset...
769    */
770
771    printf("    %04x ", (unsigned)i);
772
773   /*
774    * Then up to 16 bytes in hex...
775    */
776
777    for (j = 0; j < 16; j ++)
778      if ((i + j) < bytes)
779        printf(" %02x", buffer[i + j]);
780      else
781        printf("   ");
782
783   /*
784    * Then the ASCII representation of the bytes...
785    */
786
787    putchar(' ');
788    putchar(' ');
789
790    for (j = 0; j < 16 && (i + j) < bytes; j ++)
791    {
792      ch = buffer[i + j] & 127;
793
794      if (ch < ' ' || ch == 127)
795        putchar('.');
796      else
797        putchar(ch);
798    }
799
800    putchar('\n');
801  }
802}
803
804
805/*
806 * 'print_attributes()' - Print the attributes in a request...
807 */
808
809void
810print_attributes(ipp_t *ipp,		/* I - IPP request */
811                 int   indent)		/* I - Indentation */
812{
813  ipp_tag_t		group;		/* Current group */
814  ipp_attribute_t	*attr;		/* Current attribute */
815  char                  buffer[2048];   /* Value string */
816
817
818  for (group = IPP_TAG_ZERO, attr = ipp->attrs; attr; attr = attr->next)
819  {
820    if (!attr->name && indent == 4)
821    {
822      group = IPP_TAG_ZERO;
823      putchar('\n');
824      continue;
825    }
826
827    if (group != attr->group_tag)
828    {
829      group = attr->group_tag;
830
831      printf("\n%*s%s:\n\n", indent - 4, "", ippTagString(group));
832    }
833
834    ippAttributeString(attr, buffer, sizeof(buffer));
835
836    printf("%*s%s (%s%s): %s\n", indent, "", attr->name ? attr->name : "(null)", attr->num_values > 1 ? "1setOf " : "", ippTagString(attr->value_tag), buffer);
837  }
838}
839
840
841/*
842 * 'read_cb()' - Read data from a buffer.
843 */
844
845ssize_t					/* O - Number of bytes read */
846read_cb(_ippdata_t   *data,		/* I - Data */
847        ipp_uchar_t *buffer,		/* O - Buffer to read */
848	size_t      bytes)		/* I - Number of bytes to read */
849{
850  size_t	count;			/* Number of bytes */
851
852
853 /*
854  * Copy bytes from the data buffer to the read buffer...
855  */
856
857  if ((count = data->wsize - data->rpos) > bytes)
858    count = bytes;
859
860  memcpy(buffer, data->wbuffer + data->rpos, count);
861  data->rpos += count;
862
863 /*
864  * Return the number of bytes read...
865  */
866
867  return ((ssize_t)count);
868}
869
870
871/*
872 * 'write_cb()' - Write data into a buffer.
873 */
874
875ssize_t					/* O - Number of bytes written */
876write_cb(_ippdata_t   *data,		/* I - Data */
877         ipp_uchar_t *buffer,		/* I - Buffer to write */
878	 size_t      bytes)		/* I - Number of bytes to write */
879{
880  size_t	count;			/* Number of bytes */
881
882
883 /*
884  * Loop until all bytes are written...
885  */
886
887  if ((count = data->wsize - data->wused) > bytes)
888    count = bytes;
889
890  memcpy(data->wbuffer + data->wused, buffer, count);
891  data->wused += count;
892
893 /*
894  * Return the number of bytes written...
895  */
896
897  return ((ssize_t)count);
898}
899