11//
22// Option encoding routines for CUPS.
33//
4- // Copyright © 2021-2025 by OpenPrinting.
4+ // Copyright © 2021-2026 by OpenPrinting.
55// Copyright © 2007-2019 by Apple Inc.
66// Copyright © 1997-2007 by Easy Software Products.
77//
@@ -278,6 +278,7 @@ static const _ipp_option_t ipp_options[] =
278278//
279279
280280static int compare_ipp_options (_ipp_option_t * a , _ipp_option_t * b );
281+ static void encode_options (ipp_t * ipp , size_t num_options , cups_option_t * options , ipp_tag_t group_tag , int depth );
281282
282283
283284//
@@ -290,7 +291,8 @@ _cupsEncodeOption(
290291 ipp_tag_t group_tag , // I - Group tag
291292 _ipp_option_t * map , // I - Option mapping, if any
292293 const char * name , // I - Attribute name
293- const char * value ) // I - Value
294+ const char * value , // I - Value
295+ int depth ) // I - Depth of values
294296{
295297 size_t i , // Looping var
296298 count ; // Number of values
@@ -498,6 +500,16 @@ _cupsEncodeOption(
498500
499501 case IPP_TAG_BEGIN_COLLECTION :
500502 // Collection value
503+ if (depth >= _CUPS_MAX_OPTION_DEPTH )
504+ {
505+ // Don't allow "infinite" recursion of collection values...
506+ if (copy )
507+ free (copy );
508+
509+ ippDeleteAttribute (ipp , attr );
510+ return (NULL );
511+ }
512+
501513 num_cols = cupsParseOptions (val , /*end*/ NULL , 0 , & cols );
502514 if ((collection = ippNew ()) == NULL )
503515 {
@@ -510,8 +522,9 @@ _cupsEncodeOption(
510522 }
511523
512524 ippSetCollection (ipp , & attr , i , collection );
513- cupsEncodeOptions (collection , num_cols , cols , IPP_TAG_JOB );
525+ encode_options (collection , num_cols , cols , IPP_TAG_JOB , depth + 1 );
514526 cupsFreeOptions (num_cols , cols );
527+ ippDelete (collection );
515528 break ;
516529
517530 default :
@@ -538,7 +551,7 @@ cupsEncodeOption(ipp_t *ipp, // I - IPP request/response
538551 const char * name , // I - Option name
539552 const char * value ) // I - Option string value
540553{
541- return (_cupsEncodeOption (ipp , group_tag , _ippFindOption (name ), name , value ));
554+ return (_cupsEncodeOption (ipp , group_tag , _ippFindOption (name ), name , value , /*depth*/ 0 ));
542555}
543556
544557
@@ -556,11 +569,8 @@ cupsEncodeOptions(
556569 cups_option_t * options , // I - Options
557570 ipp_tag_t group_tag ) // I - Group to encode
558571{
559- size_t i ; // Looping var
560572 char * val ; // Pointer to option value
561- cups_option_t * option ; // Current option
562573 ipp_op_t op ; // Operation for this request
563- const ipp_op_t * ops ; // List of allowed operations
564574
565575
566576 DEBUG_printf ("cupsEncodeOptions(ipp=%p(%s), num_options=%u, options=%p, group_tag=%x)" , (void * )ipp , ipp ? ippOpString (ippGetOperation (ipp )) : "" , (unsigned )num_options , (void * )options , group_tag );
@@ -583,12 +593,91 @@ cupsEncodeOptions(
583593 ippAddString (ipp , IPP_TAG_OPERATION , IPP_TAG_MIMETYPE , "document-format" , NULL , "application/octet-stream" );
584594 }
585595
586- // Then loop through the options...
596+ // Then encode the options...
597+ encode_options (ipp , num_options , options , group_tag , /*depth*/ 0 );
598+ }
599+
600+
601+ #ifdef DEBUG
602+ //
603+ // '_ippCheckOptions()' - Validate that the option array is sorted properly.
604+ //
605+
606+ const char * // O - First out-of-order option or NULL
607+ _ippCheckOptions (void )
608+ {
609+ int i ; // Looping var
610+
611+
612+ for (i = 0 ; i < (int )(sizeof (ipp_options ) / sizeof (ipp_options [0 ]) - 1 ); i ++ )
613+ if (strcmp (ipp_options [i ].name , ipp_options [i + 1 ].name ) >= 0 )
614+ return (ipp_options [i + 1 ].name );
615+
616+ return (NULL );
617+ }
618+ #endif // DEBUG
619+
620+
621+ //
622+ // '_ippFindOption()' - Find the attribute information for an option.
623+ //
624+
625+ _ipp_option_t * // O - Attribute information
626+ _ippFindOption (const char * name ) // I - Option/attribute name
627+ {
628+ _ipp_option_t key ; // Search key
629+
630+
631+ // Lookup the proper value and group tags for this option...
632+ key .name = name ;
633+
634+ return ((_ipp_option_t * )bsearch (& key , ipp_options ,
635+ sizeof (ipp_options ) / sizeof (ipp_options [0 ]),
636+ sizeof (ipp_options [0 ]),
637+ (int (* )(const void * , const void * ))
638+ compare_ipp_options ));
639+ }
640+
641+
642+ //
643+ // 'compare_ipp_options()' - Compare two IPP options.
644+ //
645+
646+ static int // O - Result of comparison
647+ compare_ipp_options (_ipp_option_t * a , // I - First option
648+ _ipp_option_t * b ) // I - Second option
649+ {
650+ return (strcmp (a -> name , b -> name ));
651+ }
652+
653+
654+ /*
655+ * 'encode_options()' - Encode options to the specified depth.
656+ */
657+
658+ void
659+ encode_options (
660+ ipp_t * ipp , /* I - IPP request/response */
661+ size_t num_options , /* I - Number of options */
662+ cups_option_t * options , /* I - Options */
663+ ipp_tag_t group_tag , /* I - Group to encode */
664+ int depth ) /* I - Depth of options/collections */
665+ {
666+ size_t i ; /* Looping var */
667+ cups_option_t * option ; /* Current option */
668+ ipp_op_t op = ippGetOperation (ipp );
669+ /* Operation for this request */
670+ const ipp_op_t * ops ; /* List of allowed operations */
671+
672+
673+ DEBUG_printf ("4encode_options(ipp=%p(%s), num_options=%u, options=%p, group_tag=%x, depth=%d)" , (void * )ipp , ipp ? ippOpString (ippGetOperation (ipp )) : "" , (unsigned )num_options , (void * )options , group_tag , depth );
674+
675+ // Loop through the options...
587676 for (i = num_options , option = options ; i > 0 ; i -- , option ++ )
588677 {
589- _ipp_option_t * match ; // Matching attribute
678+ _ipp_option_t * match ; /* Matching attribute */
590679
591- // Skip document format options that are handled above ...
680+ // Skip document format options that are handled in cupsEncodeOptions2 ...
592681 if (!_cups_strcasecmp (option -> name , "raw" ) || !_cups_strcasecmp (option -> name , "document-format" ) || !option -> name [0 ])
593682 continue ;
594683
@@ -599,48 +688,38 @@ cupsEncodeOptions(
599688 continue ;
600689
601690 if (match -> operations )
602- {
603691 ops = match -> operations ;
604- }
605692 else if (group_tag == IPP_TAG_JOB )
606- {
607693 ops = ipp_job_creation ;
608- }
609694 else if (group_tag == IPP_TAG_DOCUMENT )
610- {
611695 ops = ipp_doc_creation ;
612- }
613696 else if (group_tag == IPP_TAG_SUBSCRIPTION )
614- {
615697 ops = ipp_sub_creation ;
616- }
617698 else if (group_tag == IPP_TAG_PRINTER )
618- {
619699 ops = ipp_set_printer ;
620- }
621700 else
622701 {
623- DEBUG_printf ("2cupsEncodeOptions : Skipping \"%s\"." , option -> name );
702+ DEBUG_printf ("5encode_options : Skipping \"%s\"." , option -> name );
624703 continue ;
625704 }
626705 }
627706 else
628707 {
629- int namelen ; // Length of name
708+ int namelen ; /* Length of name */
630709
631710 namelen = (int )strlen (option -> name );
632711
633712 if (namelen < 10 || (strcmp (option -> name + namelen - 8 , "-default" ) && strcmp (option -> name + namelen - 10 , "-supported" )))
634713 {
635714 if (group_tag != IPP_TAG_JOB && group_tag != IPP_TAG_DOCUMENT )
636715 {
637- DEBUG_printf ("2cupsEncodeOptions : Skipping \"%s\"." , option -> name );
716+ DEBUG_printf ("5encode_options : Skipping \"%s\"." , option -> name );
638717 continue ;
639718 }
640719 }
641720 else if (group_tag != IPP_TAG_PRINTER )
642721 {
643- DEBUG_printf ("2cupsEncodeOptions : Skipping \"%s\"." , option -> name );
722+ DEBUG_printf ("5encode_options : Skipping \"%s\"." , option -> name );
644723 continue ;
645724 }
646725
@@ -663,63 +742,10 @@ cupsEncodeOptions(
663742
664743 if (* ops == IPP_OP_CUPS_NONE && op != IPP_OP_CUPS_NONE )
665744 {
666- DEBUG_printf ("2cupsEncodeOptions : Skipping \"%s\"." , option -> name );
745+ DEBUG_printf ("5encode_options : Skipping \"%s\"." , option -> name );
667746 continue ;
668747 }
669748
670- _cupsEncodeOption (ipp , group_tag , match , option -> name , option -> value );
749+ _cupsEncodeOption (ipp , group_tag , match , option -> name , option -> value , depth );
671750 }
672751}
673-
674-
675- #ifdef DEBUG
676- //
677- // '_ippCheckOptions()' - Validate that the option array is sorted properly.
678- //
679-
680- const char * // O - First out-of-order option or NULL
681- _ippCheckOptions (void )
682- {
683- int i ; // Looping var
684-
685-
686- for (i = 0 ; i < (int )(sizeof (ipp_options ) / sizeof (ipp_options [0 ]) - 1 ); i ++ )
687- if (strcmp (ipp_options [i ].name , ipp_options [i + 1 ].name ) >= 0 )
688- return (ipp_options [i + 1 ].name );
689-
690- return (NULL );
691- }
692- #endif // DEBUG
693-
694-
695- //
696- // '_ippFindOption()' - Find the attribute information for an option.
697- //
698-
699- _ipp_option_t * // O - Attribute information
700- _ippFindOption (const char * name ) // I - Option/attribute name
701- {
702- _ipp_option_t key ; // Search key
703-
704-
705- // Lookup the proper value and group tags for this option...
706- key .name = name ;
707-
708- return ((_ipp_option_t * )bsearch (& key , ipp_options ,
709- sizeof (ipp_options ) / sizeof (ipp_options [0 ]),
710- sizeof (ipp_options [0 ]),
711- (int (* )(const void * , const void * ))
712- compare_ipp_options ));
713- }
714-
715-
716- //
717- // 'compare_ipp_options()' - Compare two IPP options.
718- //
719-
720- static int // O - Result of comparison
721- compare_ipp_options (_ipp_option_t * a , // I - First option
722- _ipp_option_t * b ) // I - Second option
723- {
724- return (strcmp (a -> name , b -> name ));
725- }
0 commit comments