]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Fix some clang-reported issues.
authorMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 5 Oct 2021 20:50:39 +0000 (16:50 -0400)
committerMichael R Sweet <michael.r.sweet@gmail.com>
Tue, 5 Oct 2021 20:50:39 +0000 (16:50 -0400)
16 files changed:
backend/ipp.c
cups/dest.c
notifier/rss.c
ppdc/ppdc-import.cxx
ppdc/ppdc-source.cxx
ppdc/ppdhtml.cxx
ppdc/ppdi.cxx
ppdc/ppdmerge.cxx
ppdc/ppdpo.cxx
scheduler/auth.c
scheduler/cupsfilter.c
scheduler/ipp.c
systemv/cupsctl.c
tools/ippeveps.c
tools/ipptool.c
xcode/CUPS.xcodeproj/project.pbxproj

index 3779c88fa45ff41c59b0dcfb76c072979b25f3a9..6964e4535fa2112df874a45a5021008264be3ea1 100644 (file)
@@ -1431,7 +1431,7 @@ main(int  argc,                           /* I - Number of command-line args */
   */
 
   if (version == 10)
-    create_job = send_document = 0;
+    create_job = 0;
 
  /*
   * Start monitoring the printer in the background...
@@ -1497,9 +1497,7 @@ main(int  argc,                           /* I - Number of command-line args */
       * One or more options are not supported...
       */
 
-      ipp_attribute_t  *attr;          /* Unsupported attribute */
-
-      if ((attr = ippFindAttribute(response, "sides", IPP_TAG_ZERO)) != NULL)
+      if (ippFindAttribute(response, "sides", IPP_TAG_ZERO))
       {
        /*
         * The sides value is not supported, revert to one-sided as needed...
index 60a9539307a7c6e17cf247313af7591fd1510380..fa6202c772cb0009bbdd3f91c59ea0247c150bae 100644 (file)
@@ -274,7 +274,7 @@ cupsAddDest(const char  *name,              /* I  - Destination name */
     if (instance && !cupsGetDest(name, NULL, num_dests, *dests))
     {
       // Add destination first...
-      if ((dest = cups_add_dest(name, NULL, &num_dests, dests)) == NULL)
+      if (!cups_add_dest(name, NULL, &num_dests, dests))
         return (num_dests);
     }
 
index 516b61545859a01ce07a5e29fc59d7b589b0a89d..3efc3f9ece93e3283caa50807063569c06f9f8e0 100644 (file)
@@ -566,7 +566,17 @@ new_message(int    sequence_number,        /* I - notify-sequence-number */
 
 
   if ((msg = calloc(1, sizeof(_cups_rss_t))) == NULL)
+  {
+#ifdef __clang_analyzer__
+    // These free calls are really unnecessary (a failure here ultimately causes
+    // an exit, which frees all memory much faster) but it makes Clang happy...
+    free(subject);
+    free(text);
+    free(link_url);
+#endif // __clang_analyzer__
+
     return (NULL);
+  }
 
   msg->sequence_number = sequence_number;
   msg->subject         = subject;
index b4a8341386d25a770f096bfeb7450fa583b6e075..d12d0c244e9ae652ddae52d3c83d7e09a9f2a6d0 100644 (file)
@@ -57,274 +57,276 @@ ppdcSource::import_ppd(const char *f)     // I - Filename
   }
 
   // See if the driver has already been imported...
-  if ((driver = find_driver(ppd->pcfilename)) == NULL)
+  if (find_driver(ppd->pcfilename))
   {
-    // Create a new PPD file...
-    if ((fp = cupsFileOpen(f, "r")) == NULL)
-    {
-      ppdClose(ppd);
-      return (0);
-    }
-
-    driver       = new ppdcDriver();
-    driver->type = PPDC_DRIVER_PS;
-
-    drivers->add(driver);
+    ppdClose(ppd);
+    return (1);
+  }
 
-    // Read the initial comments from the PPD file and use them as the
-    // copyright/license text...
-    cupsFileGets(fp, line, sizeof(line));
-                                       // Skip *PPD-Adobe-M.m
+  // Create a new PPD file...
+  if ((fp = cupsFileOpen(f, "r")) == NULL)
+  {
+    ppdClose(ppd);
+    return (0);
+  }
 
-    while (cupsFileGets(fp, line, sizeof(line)))
-      if (strncmp(line, "*%", 2))
-        break;
-      else if (strncmp(line, "*%%%% ", 6))
-      {
-        for (ptr = line + 2; isspace(*ptr); ptr ++);
+  driver       = new ppdcDriver();
+  driver->type = PPDC_DRIVER_PS;
 
-        driver->add_copyright(ptr);
-      }
+  drivers->add(driver);
 
-    cupsFileClose(fp);
+  // Read the initial comments from the PPD file and use them as the
+  // copyright/license text...
+  cupsFileGets(fp, line, sizeof(line));
+                                     // Skip *PPD-Adobe-M.m
 
-    // Then add the stuff from the PPD file...
-    if (ppd->modelname && ppd->manufacturer &&
-        !_cups_strncasecmp(ppd->modelname, ppd->manufacturer,
-                     strlen(ppd->manufacturer)))
+  while (cupsFileGets(fp, line, sizeof(line)))
+    if (strncmp(line, "*%", 2))
+      break;
+    else if (strncmp(line, "*%%%% ", 6))
     {
-      ptr = ppd->modelname + strlen(ppd->manufacturer);
+      for (ptr = line + 2; isspace(*ptr); ptr ++);
 
-      while (isspace(*ptr))
-        ptr ++;
+      driver->add_copyright(ptr);
     }
-    else
-      ptr = ppd->modelname;
-
-    if (ppd->nickname)
-      driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname));
-
-    if (ppd->shortnickname)
-      driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL,
-                                    ppd->shortnickname));
-
-    driver->manufacturer        = new ppdcString(ppd->manufacturer);
-    driver->model_name          = new ppdcString(ptr);
-    driver->pc_file_name        = new ppdcString(ppd->pcfilename);
-    attr = ppdFindAttr(ppd, "FileVersion", NULL);
-    driver->version             = new ppdcString(attr ? attr->value : NULL);
-    driver->model_number        = ppd->model_number;
-    driver->manual_copies       = ppd->manual_copies;
-    driver->color_device        = ppd->color_device;
-    driver->throughput          = ppd->throughput;
-    driver->variable_paper_size = ppd->variable_sizes;
-    driver->max_width           = ppd->custom_max[0];
-    driver->max_length          = ppd->custom_max[1];
-    driver->min_width           = ppd->custom_min[0];
-    driver->min_length          = ppd->custom_min[1];
-    driver->left_margin         = ppd->custom_margins[0];
-    driver->bottom_margin       = ppd->custom_margins[1];
-    driver->right_margin        = ppd->custom_margins[2];
-    driver->top_margin          = ppd->custom_margins[3];
-
-    for (i = 0; i < ppd->num_filters; i ++)
-    {
-      strlcpy(line, ppd->filters[i], sizeof(line));
 
-      for (ptr = line; *ptr; ptr ++)
-        if (isspace(*ptr & 255))
-         break;
-      *ptr++ = '\0';
+  cupsFileClose(fp);
+
+  // Then add the stuff from the PPD file...
+  if (ppd->modelname && ppd->manufacturer &&
+      !_cups_strncasecmp(ppd->modelname, ppd->manufacturer,
+                  strlen(ppd->manufacturer)))
+  {
+    ptr = ppd->modelname + strlen(ppd->manufacturer);
 
-      cost = strtol(ptr, &ptr, 10);
+    while (isspace(*ptr))
+      ptr ++;
+  }
+  else
+    ptr = ppd->modelname;
+
+  if (ppd->nickname)
+    driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname));
+
+  if (ppd->shortnickname)
+    driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL,
+                                 ppd->shortnickname));
+
+  driver->manufacturer        = new ppdcString(ppd->manufacturer);
+  driver->model_name          = new ppdcString(ptr);
+  driver->pc_file_name        = new ppdcString(ppd->pcfilename);
+  attr = ppdFindAttr(ppd, "FileVersion", NULL);
+  driver->version             = new ppdcString(attr ? attr->value : NULL);
+  driver->model_number        = ppd->model_number;
+  driver->manual_copies       = ppd->manual_copies;
+  driver->color_device        = ppd->color_device;
+  driver->throughput          = ppd->throughput;
+  driver->variable_paper_size = ppd->variable_sizes;
+  driver->max_width           = ppd->custom_max[0];
+  driver->max_length          = ppd->custom_max[1];
+  driver->min_width           = ppd->custom_min[0];
+  driver->min_length          = ppd->custom_min[1];
+  driver->left_margin         = ppd->custom_margins[0];
+  driver->bottom_margin       = ppd->custom_margins[1];
+  driver->right_margin        = ppd->custom_margins[2];
+  driver->top_margin          = ppd->custom_margins[3];
+
+  for (i = 0; i < ppd->num_filters; i ++)
+  {
+    strlcpy(line, ppd->filters[i], sizeof(line));
 
-      while (isspace(*ptr & 255))
-        ptr ++;
+    for (ptr = line; *ptr; ptr ++)
+      if (isspace(*ptr & 255))
+       break;
+    *ptr++ = '\0';
 
-      filter = new ppdcFilter(line, ptr, cost);
-      driver->add_filter(filter);
-    }
+    cost = strtol(ptr, &ptr, 10);
 
-    attr = ppdFindAttr(ppd, "DefaultFont", NULL);
-    driver->default_font  = new ppdcString(attr ? attr->value : NULL);
+    while (isspace(*ptr & 255))
+      ptr ++;
 
-    // Collect media sizes...
-    ppd_option_t       *region_option,         // PageRegion option
-                       *size_option;           // PageSize option
-    ppd_choice_t       *region_choice,         // PageRegion choice
-                       *size_choice;           // PageSize choice
+    filter = new ppdcFilter(line, ptr, cost);
+    driver->add_filter(filter);
+  }
 
-    region_option = ppdFindOption(ppd, "PageRegion");
-    size_option   = ppdFindOption(ppd, "PageSize");
+  attr = ppdFindAttr(ppd, "DefaultFont", NULL);
+  driver->default_font  = new ppdcString(attr ? attr->value : NULL);
 
-    for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
-    {
-      // Don't do custom size here...
-      if (!_cups_strcasecmp(size->name, "Custom"))
-        continue;
+  // Collect media sizes...
+  ppd_option_t *region_option,         // PageRegion option
+                     *size_option;             // PageSize option
+  ppd_choice_t *region_choice,         // PageRegion choice
+                     *size_choice;             // PageSize choice
 
-      // Get the code for the PageSize and PageRegion options...
-      region_choice = ppdFindChoice(region_option, size->name);
-      size_choice   = ppdFindChoice(size_option, size->name);
+  region_option = ppdFindOption(ppd, "PageRegion");
+  size_option   = ppdFindOption(ppd, "PageSize");
 
-      // Create a new media size record and add it to the driver...
-      csize = new ppdcMediaSize(size->name, size_choice->text, size->width,
-                                size->length, size->left, size->bottom,
-                               size->width - size->right,
-                               size->length - size->top,
-                               size_choice->code, region_choice->code);
+  for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++)
+  {
+    // Don't do custom size here...
+    if (!_cups_strcasecmp(size->name, "Custom"))
+      continue;
 
-       driver->add_size(csize);
+    // Get the code for the PageSize and PageRegion options...
+    region_choice = ppdFindChoice(region_option, size->name);
+    size_choice   = ppdFindChoice(size_option, size->name);
 
-       if (!_cups_strcasecmp(size_option->defchoice, size->name))
-         driver->set_default_size(csize);
-    }
+    // Create a new media size record and add it to the driver...
+    csize = new ppdcMediaSize(size->name, size_choice->text, size->width,
+                             size->length, size->left, size->bottom,
+                             size->width - size->right,
+                             size->length - size->top,
+                             size_choice->code, region_choice->code);
 
-    // Now all of the options...
-    for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
-    {
-      cgroup = new ppdcGroup(group->name, group->text);
-      driver->add_group(cgroup);
+     driver->add_size(csize);
 
-      for (j = group->num_options, option = group->options; j > 0; j --, option ++)
-      {
-        if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion"))
-          continue;
+     if (!_cups_strcasecmp(size_option->defchoice, size->name))
+       driver->set_default_size(csize);
+  }
 
-        coption = new ppdcOption((ppdcOptType)option->ui, option->keyword,
-                                option->text, (ppdcOptSection)option->section,
-                                option->order);
-        cgroup->add_option(coption);
+  // Now all of the options...
+  for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
+  {
+    cgroup = new ppdcGroup(group->name, group->text);
+    driver->add_group(cgroup);
 
-        for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++)
-        {
-         if (!strcmp(choice->choice, "Custom"))
-           continue;
+    for (j = group->num_options, option = group->options; j > 0; j --, option ++)
+    {
+      if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion"))
+       continue;
 
-          cchoice = new ppdcChoice(choice->choice, choice->text, choice->code);
-          coption->add_choice(cchoice);
+      coption = new ppdcOption((ppdcOptType)option->ui, option->keyword,
+                              option->text, (ppdcOptSection)option->section,
+                              option->order);
+      cgroup->add_option(coption);
 
-          if (!_cups_strcasecmp(option->defchoice, choice->choice))
-            coption->set_defchoice(cchoice);
-        }
+      for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++)
+      {
+       if (!strcmp(choice->choice, "Custom"))
+         continue;
+
+       cchoice = new ppdcChoice(choice->choice, choice->text, choice->code);
+       coption->add_choice(cchoice);
+
+       if (!_cups_strcasecmp(option->defchoice, choice->choice))
+         coption->set_defchoice(cchoice);
       }
     }
+  }
 
-    // Now the constraints...
-    for (i = ppd->num_consts, constraint = ppd->consts;
-         i > 0;
-        i --, constraint ++)
-    {
-      // Look for mirrored constraints...
-      for (j = i - 1, constraint2 = constraint + 1;
-           j > 0;
-          j --, constraint2 ++)
-       if (!strcmp(constraint->option1, constraint2->option2) &&
-           !strcmp(constraint->choice1, constraint2->choice2) &&
-           !strcmp(constraint->option2, constraint2->option1) &&
-           !strcmp(constraint->choice2, constraint2->choice1))
-          break;
-
-      if (j)
-        continue;
-
-      cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2,
-                                       constraint->option1, constraint->choice1);
-      driver->add_constraint(cconstraint);
-    }
+  // Now the constraints...
+  for (i = ppd->num_consts, constraint = ppd->consts;
+       i > 0;
+       i --, constraint ++)
+  {
+    // Look for mirrored constraints...
+    for (j = i - 1, constraint2 = constraint + 1;
+        j > 0;
+        j --, constraint2 ++)
+      if (!strcmp(constraint->option1, constraint2->option2) &&
+         !strcmp(constraint->choice1, constraint2->choice2) &&
+         !strcmp(constraint->option2, constraint2->option1) &&
+         !strcmp(constraint->choice2, constraint2->choice1))
+       break;
+
+    if (j)
+      continue;
+
+    cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2,
+                                    constraint->option1, constraint->choice1);
+    driver->add_constraint(cconstraint);
+  }
+
+  for (i = 0; i < ppd->num_attrs; i ++)
+  {
+    attr = ppd->attrs[i];
 
-    for (i = 0; i < ppd->num_attrs; i ++)
+    if (!strcmp(attr->name, "Font"))
     {
-      attr = ppd->attrs[i];
+      // Font...
+      char             encoding[256],  // Encoding string
+                     version[256],     // Version string
+                     charset[256],     // Charset string
+                     status[256];      // Status string
+      ppdcFontStatus   fstatus;        // Status enumeration
+
 
-      if (!strcmp(attr->name, "Font"))
+      if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version,
+                charset, status) != 4)
       {
-        // Font...
-       char            encoding[256],  // Encoding string
-                       version[256],   // Version string
-                       charset[256],   // Charset string
-                       status[256];    // Status string
-       ppdcFontStatus  fstatus;        // Status enumeration
-
-
-        if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version,
-                  charset, status) != 4)
-       {
-         _cupsLangPrintf(stderr, _("ppdc: Bad font attribute: %s"),
-                         attr->value);
-         continue;
-       }
+       _cupsLangPrintf(stderr, _("ppdc: Bad font attribute: %s"),
+                       attr->value);
+       continue;
+      }
 
-        if (!strcmp(status, "ROM"))
-         fstatus = PPDC_FONT_ROM;
-       else
-         fstatus = PPDC_FONT_DISK;
+      if (!strcmp(status, "ROM"))
+       fstatus = PPDC_FONT_ROM;
+      else
+       fstatus = PPDC_FONT_DISK;
 
-        font = new ppdcFont(attr->spec, encoding, version, charset, fstatus);
+      font = new ppdcFont(attr->spec, encoding, version, charset, fstatus);
 
-       driver->add_font(font);
-      }
-      else if (!strcmp(attr->name, "CustomPageSize"))
-      {
-        driver->set_custom_size_code(attr->value);
-      }
-      else if ((strncmp(attr->name, "Default", 7) ||
-               !strcmp(attr->name, "DefaultColorSpace")) &&
-              strcmp(attr->name, "ColorDevice") &&
-              strcmp(attr->name, "Manufacturer") &&
-              strcmp(attr->name, "ModelName") &&
-              strcmp(attr->name, "MaxMediaHeight") &&
-              strcmp(attr->name, "MaxMediaWidth") &&
-              strcmp(attr->name, "NickName") &&
-              strcmp(attr->name, "ParamCustomPageSize") &&
-              strcmp(attr->name, "ShortNickName") &&
-              strcmp(attr->name, "Throughput") &&
-              strcmp(attr->name, "PCFileName") &&
-              strcmp(attr->name, "FileVersion") &&
-              strcmp(attr->name, "FormatVersion") &&
-              strcmp(attr->name, "HWMargins") &&
-              strcmp(attr->name, "VariablePaperSize") &&
-              strcmp(attr->name, "LanguageEncoding") &&
-              strcmp(attr->name, "LanguageVersion") &&
-              strcmp(attr->name, "cupsFilter") &&
-              strcmp(attr->name, "cupsFlipDuplex") &&
-              strcmp(attr->name, "cupsLanguages") &&
-              strcmp(attr->name, "cupsManualCopies") &&
-              strcmp(attr->name, "cupsModelNumber") &&
-              strcmp(attr->name, "cupsVersion"))
-      {
-        if ((ptr = strchr(attr->name, '.')) != NULL &&
-           ((ptr - attr->name) == 2 || (ptr - attr->name) == 5))
-       {
-         // Might be a localization attribute; test further...
-         if (isalpha(attr->name[0] & 255) &&
-             isalpha(attr->name[1] & 255) &&
-             (attr->name[2] == '.' ||
-              (attr->name[2] == '_' && isalpha(attr->name[3] & 255) &&
-               isalpha(attr->name[4] & 255))))
-            continue;
-       }
-
-        // Attribute...
-        driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
-                                     attr->value));
-      }
-      else if (!strncmp(attr->name, "Default", 7) &&
-               !ppdFindOption(ppd, attr->name + 7) &&
-              strcmp(attr->name, "DefaultFont") &&
-              strcmp(attr->name, "DefaultImageableArea") &&
-              strcmp(attr->name, "DefaultPaperDimension") &&
-              strcmp(attr->name, "DefaultFont"))
+      driver->add_font(font);
+    }
+    else if (!strcmp(attr->name, "CustomPageSize"))
+    {
+      driver->set_custom_size_code(attr->value);
+    }
+    else if ((strncmp(attr->name, "Default", 7) ||
+             !strcmp(attr->name, "DefaultColorSpace")) &&
+            strcmp(attr->name, "ColorDevice") &&
+            strcmp(attr->name, "Manufacturer") &&
+            strcmp(attr->name, "ModelName") &&
+            strcmp(attr->name, "MaxMediaHeight") &&
+            strcmp(attr->name, "MaxMediaWidth") &&
+            strcmp(attr->name, "NickName") &&
+            strcmp(attr->name, "ParamCustomPageSize") &&
+            strcmp(attr->name, "ShortNickName") &&
+            strcmp(attr->name, "Throughput") &&
+            strcmp(attr->name, "PCFileName") &&
+            strcmp(attr->name, "FileVersion") &&
+            strcmp(attr->name, "FormatVersion") &&
+            strcmp(attr->name, "HWMargins") &&
+            strcmp(attr->name, "VariablePaperSize") &&
+            strcmp(attr->name, "LanguageEncoding") &&
+            strcmp(attr->name, "LanguageVersion") &&
+            strcmp(attr->name, "cupsFilter") &&
+            strcmp(attr->name, "cupsFlipDuplex") &&
+            strcmp(attr->name, "cupsLanguages") &&
+            strcmp(attr->name, "cupsManualCopies") &&
+            strcmp(attr->name, "cupsModelNumber") &&
+            strcmp(attr->name, "cupsVersion"))
+    {
+      if ((ptr = strchr(attr->name, '.')) != NULL &&
+         ((ptr - attr->name) == 2 || (ptr - attr->name) == 5))
       {
-        // Default attribute...
-        driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
-                                     attr->value));
+       // Might be a localization attribute; test further...
+       if (isalpha(attr->name[0] & 255) &&
+           isalpha(attr->name[1] & 255) &&
+           (attr->name[2] == '.' ||
+            (attr->name[2] == '_' && isalpha(attr->name[3] & 255) &&
+             isalpha(attr->name[4] & 255))))
+         continue;
       }
+
+      // Attribute...
+      driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
+                                   attr->value));
+    }
+    else if (!strncmp(attr->name, "Default", 7) &&
+            !ppdFindOption(ppd, attr->name + 7) &&
+            strcmp(attr->name, "DefaultFont") &&
+            strcmp(attr->name, "DefaultImageableArea") &&
+            strcmp(attr->name, "DefaultPaperDimension") &&
+            strcmp(attr->name, "DefaultFont"))
+    {
+      // Default attribute...
+      driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text,
+                                   attr->value));
     }
   }
 
-  if (ppd)
-    ppdClose(ppd);
+  ppdClose(ppd);
 
   return (1);
 }
index d1e942662d1ccabbddcf6d626e0e92d83f440d26..0a0e40592e1a8b49d595c1475a5d81441653d0e0 100644 (file)
@@ -2792,7 +2792,7 @@ ppdcSource::scan_file(ppdcFile   *fp,     // I - File to read
       if (have_cutter <= 0 || cond_state)
         continue;
 
-      if ((o = d->find_option("CutMedia")) == NULL)
+      if (!d->find_option("CutMedia"))
       {
         o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f);
 
@@ -2805,9 +2805,8 @@ ppdcSource::scan_file(ppdcFile   *fp,     // I - File to read
 
        c = new ppdcChoice("True", NULL, "<</CutMedia 4>>setpagedevice");
        o->add_choice(c);
+        o = NULL;
       }
-
-      o = NULL;
     }
     else if (!_cups_strcasecmp(temp, "Darkness"))
     {
index 75636a9447bbc9e0d38fb809c048328184f02a36..cb2a784b2bb7ad787379511639adc1b6347abc10 100644 (file)
@@ -80,7 +80,6 @@ main(int  argc,                               // I - Number of command-line arguments
 
          default :                     // Unknown
              usage();
-             break;
        }
     }
     else
@@ -100,7 +99,7 @@ main(int  argc,                              // I - Number of command-line arguments
       for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next())
        for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next())
        {
-         if ((compo = composite->find_option(o->name->value)) == NULL)
+         if (!composite->find_option(o->name->value))
            composite->add_option(new ppdcOption(o));
        }
 
index a2f9648f28f7a0190adeb7120f508c46099c37f7..f5aa4db5206502d946b8cee7256bf0185239e529 100644 (file)
@@ -71,7 +71,6 @@ main(int  argc,                               // I - Number of command-line arguments
 
          default :                     // Unknown
              usage();
-             break;
         }
     }
     else
index 1ab804399d6cf2eda53cc6971dfb2fa6488f6e28..cd83964fae12ba5be68d15fff4ea34e09833a5d1 100644 (file)
@@ -75,7 +75,6 @@ main(int  argc,                               // I - Number of command-line arguments
 
          default :                     // Unknown
              usage();
-             break;
         }
     }
     else
index 498b34e3d7f0d3c7ab4d3ac67c921f8cd99aa796..01f765e442f5ec126c190e67dd53587f2a4a7ef6 100644 (file)
@@ -100,7 +100,6 @@ main(int  argc,                             // I - Number of command-line arguments
 
          default :                     // Unknown
              usage();
-             break;
        }
     }
     else
index 580dcde08e801914c98822f12cdab2529e2b1623..f29ceb497de65eb73b219da8edea0156957c10dc 100644 (file)
@@ -67,6 +67,7 @@ typedef struct sockpeercred cupsd_ucred_t;
  * Local functions...
  */
 
+static int             check_admin_task(cupsd_client_t *con);
 #ifdef HAVE_AUTHORIZATION_H
 static int             check_authref(cupsd_client_t *con, const char *right);
 #endif /* HAVE_AUTHORIZATION_H */
@@ -1529,894 +1530,893 @@ cupsdFreeLocation(cupsd_location_t *loc)/* I - Location to free */
 
 
 /*
- * 'cupsdCheckAdminTask()' - Do additional checks on administrative tasks
+ * 'cupsdIsAuthorized()' - Check to see if the user is authorized...
  */
 
-int                                      /* O - 1 if admin task authorized */
-cupsdCheckAdminTask(cupsd_client_t *con) /* I - Connection */
+http_status_t                          /* O - HTTP_OK if authorized or error code */
+cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
+                  const char     *owner)/* I - Owner of object */
 {
-  int ret = 1; /* Return value */
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Administrative task");
+  int                  i,              /* Looping vars */
+                       auth,           /* Authorization status */
+                       type;           /* Type of authentication */
+  http_addr_t          *hostaddr = httpGetAddress(con->http);
+                                       /* Client address */
+  const char           *hostname = httpGetHostname(con->http, NULL, 0);
+                                       /* Client hostname */
+  unsigned             address[4];     /* Authorization address */
+  cupsd_location_t     *best;          /* Best match for location so far */
+  size_t               hostlen;        /* Length of hostname */
+  char                 *name,          /* Current username */
+                       username[256],  /* Username to authorize */
+                       ownername[256], /* Owner name to authorize */
+                       *ptr;           /* Pointer into username */
+  struct passwd                *pw;            /* User password data */
+  static const char * const levels[] = /* Auth levels */
+               {
+                 "ANON",
+                 "USER",
+                 "GROUP"
+               };
+  static const char * const types[] =  /* Auth types */
+               {
+                 "None",
+                 "Basic",
+                 "Negotiate"
+               };
 
- /*
-  * If the client accesses locally via domain socket, find out whether it
-  * is a Snap. Grant access if it is not a Snap, if it is a classic Snap
-  * or if it is a confined Snap which plugs "cups-control", deny access
-  * if it is a confined Snap not plugging "cups-control" or if an error
-  * occurs in the process of finding this out.
-  */
 
-#if defined(AF_LOCAL) && defined(SUPPORT_SNAPPED_CLIENTS)
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location ? con->best->location : "(null)" : "");
+  if (owner)
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner);
 
  /*
-  * Get the client's file descriptor and from this its AppArmor context
+  * If there is no "best" authentication rule for this request, then
+  * access is allowed from the local system and denied from other
+  * addresses...
   */
 
-  if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL)
+  if (!con->best)
   {
-    int                 peerfd;         /* Peer's file descriptor */
-
-    peerfd = httpGetFd(con->http);
-
-    if (peerfd < 0)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to get peer file descriptor of client connecting via domain socket");
-    }
+    if (httpAddrLocalhost(httpGetAddress(con->http)) ||
+        !strcmp(hostname, ServerName) ||
+       cupsArrayFind(ServerAlias, (void *)hostname))
+      return (HTTP_OK);
     else
-    {
-      char *context = NULL;      /* AppArmor profile name of client */
-#  undef CHECK_METHOD_FOUND
-#  ifdef SUPPORT_SNAPPED_CUPSD
-      int status = 65535;        /* Status of client Snap context check */
-#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
-#      define CHECK_METHOD_FOUND 1
-      char *args[] = { "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl arguments */
-      SnapdClient *client = NULL; /* Data structure of snapd access */
-      const char *cookie;        /* snapd access cookie */
-      GError *error = NULL;      /* Glib error */
-#    else
-#      ifdef HAVE_SNAPCTL_IS_CONNECTED
-#        define CHECK_METHOD_FOUND 1
-      char *args[] = { SNAPCTL, "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl command line */
-      int fds[2],                /* Pipe file descriptors for stderr of
-                                   snapctl */
-         nullfd;                /* /dev/null file descriptor for stdout of
-                                   snapctl */
-      pid_t pid;                 /* PID of snapctl */
-      cups_file_t *snapctl_stderr; /* CUPS FP for stderr of snapctl */
-      char buf[1024];            /* Buffer for snapctl's stderr output */
-      int wstatus;               /* Wait result of forked snapctl process */
-#      endif /* HAVE_SNAPCTL_IS_CONNECTED */
-#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
-#  else
-#    if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB)
-#      define CHECK_METHOD_FOUND 1
-      char *snap_name = NULL;    /* Client Snap name */
-      char *dot;                 /* Pointer to dot in AppArmor profile name */
-      SnapdClient *snapd = NULL; /* Data structure of snapd access */
-      GError *error = NULL;      /* Glib error */
-      SnapdSnap *snap = NULL;    /* Data structure of client Snap */
-      GPtrArray *plugs = NULL;   /* Plug search result of client Snap */
-#    endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */
-#  endif /* SUPPORT_SNAPPED_CUPSD */
-
+      return (HTTP_FORBIDDEN);
+  }
 
-#  ifndef SUPPORT_SNAPPED_CUPSD
+  best = con->best;
 
-      /* If AppArmor is not enabled, then we can't identify the client */
-      /* With cupsd running in a Snap, the "mount-observe" interface
-         needs to be plugged, therefore we do this only if not snapped. */
-      if (!aa_is_enabled())
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: No AppArmor in use");
-       goto snap_check_done;
-      }
+  if ((type = best->type) == CUPSD_AUTH_DEFAULT)
+    type = cupsdDefaultAuthType();
 
-#  endif /* !SUPPORT_SNAPPED_CUPSD */
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: level=CUPSD_AUTH_%s, type=%s, satisfy=CUPSD_AUTH_SATISFY_%s, num_names=%d", levels[best->level], types[type], best->satisfy ? "ANY" : "ALL", cupsArrayCount(best->names));
 
-      if (aa_getpeercon(peerfd, &context, NULL) < 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor profile could not be retrieved for client process - Error: %s", strerror(errno));
-       goto snap_check_done;
-      } else
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor profile of client process: %s", context);
+  if (best->limit == CUPSD_AUTH_LIMIT_IPP)
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", best->op, ippOpString(best->op));
 
-#  ifdef OUR_SNAP_NAME
-      /* Is the client one of the utilities of our Snap? */
-      if (!strncmp(context, "snap." OUR_SNAP_NAME ".", strlen(OUR_SNAP_NAME) + 6))
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap is the same Snap we are running in, access granted");
-       goto snap_check_done;
-      }
-#  endif /* OUR_SNAP_NAME */
+ /*
+  * Check host/ip-based accesses...
+  */
 
-#  ifdef SUPPORT_SNAPPED_CUPSD
+#ifdef AF_INET6
+  if (httpAddrFamily(hostaddr) == AF_INET6)
+  {
+   /*
+    * Copy IPv6 address...
+    */
 
-     /*
-      * Run
-      *
-      * snapctl is-connected --apparmor-label AA_CONTEXT CUPS_CONTROL_SLOT
-      *
-      * or an equivalent library function call using the
-      * snapd_client_run_snapctl2_sync() function of libsnapd-glib.
-      *
-      * Here AA_CONTEXT is the AppArmor profile name of the client, or
-      * "unconfined" for an unconfined client and CUPS_CONTROL_SLOT
-      * the name of the slot of the CUPS Snap to which clients plug
-      * with their cups-control plug in order to do administrative
-      * CUPS tasks.
-      *
-      * The exit status of the command/function call tells which type
-      * of client we have to do with:
-      *
-      *    0: The client is a confined Snap and plugs cups-control
-      *           -> Grant access
-      *    1: The client is a confined Snap and does not plug cups-control
-      *           -> Deny access
-      *   10: The client is a classic Snap
-      *           -> Grant access
-      *   11: The client is not a Snap
-      *           -> Grant access
-      *
-      * NOTE: This method only works if cupsd is running in a Snap
-      *       providing a slot for the client's "cups-control"
-      *       plug. Do not build CUPS with this method when intending
-      *       to use it unsnapped, for example in a Debian or RPM
-      *       package, or directly installed into your system. The
-      *       errors of snapctl missing or snapctl/the function
-      *       running without a Snap context will deny all
-      *       administrative accesses!
-      *
-      * When running inside a Snap this method is preferred, as it does not
-      * require full access to the snapd under which cupsd is running.
-      */
+    address[0] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[0]);
+    address[1] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[1]);
+    address[2] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[2]);
+    address[3] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[3]);
+  }
+  else
+#endif /* AF_INET6 */
+  if (con->http->hostaddr->addr.sa_family == AF_INET)
+  {
+   /*
+    * Copy IPv4 address...
+    */
 
-#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
+    address[0] = 0;
+    address[1] = 0;
+    address[2] = 0;
+    address[3] = ntohl(hostaddr->ipv4.sin_addr.s_addr);
+  }
+  else
+    memset(address, 0, sizeof(address));
 
-     /*
-      * Use the snapd_client_run_snapctl2_sync() function of libsnapd-glib
-      */
+  hostlen = strlen(hostname);
 
-      /* Insert client Snap context in snapctl arguments */
-      args[2] = context;
+  auth = cupsdCheckAccess(address, hostname, hostlen, best)
+             ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY;
 
-      /* Connect to snapd */
-      client = snapd_client_new();
-      if (!client)
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not connect to snapd, permission denied");
-       ret = 0;
-       goto snap_check_done;
-      }
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...", auth ? "DENY" : "ALLOW");
 
-      /* snapctl commands are sent over a this socket that is made
-        available within the snap sandbox */
-      snapd_client_set_socket_path(client, "/run/snapd-snap.socket");
+  if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL)
+    return (HTTP_FORBIDDEN);
 
-      /* Take cookie from the environment if available */
-      cookie = g_getenv("SNAP_COOKIE");
-      if (!cookie)
-      {
-        cookie = "";
-       cupsdLogMessage(CUPSD_LOG_WARN, "cupsdCheckAdminTask: No SNAP_COOKIE set in the Snap environment, client Snap context check may not work");
-      }
+#ifdef HAVE_TLS
+ /*
+  * See if encryption is required...
+  */
 
-      /* Do the client Snap context check */
-      if (!snapd_client_run_snapctl2_sync(client, cookie, args, NULL, NULL, &status, NULL, &error)) {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap context check error - %s", error->message);
-       ret = 0;
-       goto snap_check_done;
-      }
+  if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http->tls &&
+      _cups_strcasecmp(hostname, "localhost") &&
+      !httpAddrLocalhost(hostaddr) &&
+      best->satisfy == CUPSD_AUTH_SATISFY_ALL) &&
+      !(type == CUPSD_AUTH_NEGOTIATE ||
+        (type == CUPSD_AUTH_NONE &&
+         cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE)))
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                    "cupsdIsAuthorized: Need upgrade to TLS...");
+    return (HTTP_UPGRADE_REQUIRED);
+  }
+#endif /* HAVE_TLS */
 
-#    else
-#      ifdef HAVE_SNAPCTL_IS_CONNECTED
+ /*
+  * Now see what access level is required...
+  */
 
-     /*
-      * Call the snapctl executable using execv() in a fork
-      */
+  if (best->level == CUPSD_AUTH_ANON ||        /* Anonymous access - allow it */
+      (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0))
+    return (HTTP_OK);
 
-      /* Insert client Snap context in snapctl command line */
-      args[3] = context;
+  if (!con->username[0] && type == CUPSD_AUTH_NONE &&
+      best->limit == CUPSD_AUTH_LIMIT_IPP)
+  {
+   /*
+    * Check for unauthenticated username...
+    */
 
-      /* Create a pipe to catch stderr output from snapctl */
-      if (pipe(fds))
-      {
-       fds[0] = -1;
-       fds[1] = -1;
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to establish stderr pipe for snapctl call - %s", strerror(errno));
-       ret = 0;
-       goto snap_check_done;
-      }
+    ipp_attribute_t    *attr;          /* requesting-user-name attribute */
 
-      /* Set the "close on exec" flag on each end of the pipe... */
-      if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
-      {
-       close(fds[0]);
-       close(fds[1]);
-       fds[0] = -1;
-       fds[1] = -1;
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to set \"close on exec\" flag on read end of the stderr pipe for snapctl call - %s", strerror(errno));
-       ret = 0;
-       goto snap_check_done;
-      }
-      if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
-      {
-       close(fds[0]);
-       close(fds[1]);
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to set \"close on exec\" flag on write end of the stderr pipe for snapctl call - %s", strerror(errno));
-       ret = 0;
-       goto snap_check_done;
-      }
 
-      if ((pid = fork()) == 0)
-      {
-       /* Couple pipe with stderr of Ghostscript process */
-       if (fds[1] >= 0) {
-         if (fds[1] != 2) {
-           if (dup2(fds[1], 2) < 0) {
-             cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to couple pipe with stderr of snapctl process - %s", strerror(errno));
-             exit(100);
-           }
-           close(fds[1]);
-         }
-         close(fds[0]);
-       } else {
-         cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Invalid pipe file descriptor to couple with stderr of snapctl process - %s", strerror(errno));
-         exit(100);
-       }
+    attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
+    if (attr)
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "cupsdIsAuthorized: requesting-user-name=\"%s\"",
+                      attr->values[0].string.text);
+      strlcpy(username, attr->values[0].string.text, sizeof(username));
+    }
+    else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
+      return (HTTP_UNAUTHORIZED);      /* Non-anonymous needs user/pass */
+    else
+      return (HTTP_OK);                        /* unless overridden with Satisfy */
+  }
+  else
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"",
+                   con->username);
 
-       /* Send snapctl's stdout to the Nirwana, as snapctl is supposed to
-          not output anything here */
-       if ((nullfd = open("/dev/null", O_RDWR)) > 2)
-       {
-         dup2(nullfd, 1);
-         close(nullfd);
-       }
-       else
-         close(nullfd);
-       fcntl(1, F_SETFL, O_NDELAY);
+#ifdef HAVE_AUTHORIZATION_H
+    if (!con->username[0] && !con->authref)
+#else
+    if (!con->username[0])
+#endif /* HAVE_AUTHORIZATION_H */
+    {
+      if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
+       return (HTTP_UNAUTHORIZED);     /* Non-anonymous needs user/pass */
+      else
+       return (HTTP_OK);               /* unless overridden with Satisfy */
+    }
 
-       /* Execute snapctl command line ... */
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Running command: " SNAPCTL " is-connected --apparmor-label %s " CUPS_CONTROL_SLOT, context);
-       execv(SNAPCTL, args);
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to launch snapctl: %s", strerror(errno));
-       exit(100);
-      }
-      else if (pid < 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to fork for snapctl call - %s", strerror(errno));
-       ret = 0;
-       goto snap_check_done;
-      }
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Started snapctl (PID %d)", pid);
 
-      close(fds[1]);
+    if (con->type != type && type != CUPSD_AUTH_NONE &&
+#ifdef HAVE_GSSAPI
+        (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) &&
+#endif /* HAVE_GSSAPI */
+        con->type != CUPSD_AUTH_BASIC)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s.",
+                      types[con->type], types[type]);
 
-      /* Read out stderr from snapctl */
-      buf[0] = '\0';
-      snapctl_stderr = cupsFileOpenFd(fds[0], "r");
-      if (snapctl_stderr)
-      {
-       while (cupsFileGets(snapctl_stderr, buf, sizeof(buf)) && buf[0])
-       {
-         cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Error message from snapctl: %s", buf);
-         ret = 0;
-       }
-       cupsFileClose(snapctl_stderr);
-      }
-      close(fds[0]);
+      return (HTTP_UNAUTHORIZED);
+    }
 
-      /* Wait for snapctl to finish */
-    retry_wait:
-      if (waitpid (pid, &wstatus, 0) == -1)
-      {
-       if (errno == EINTR)
-         goto retry_wait;
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl (PID %d) stopped with an error - %s", pid, strerror(errno));
-       ret = 0;
-       goto snap_check_done;
-      }
-      if (ret == 0)
-       goto snap_check_done;
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: snapctl (PID %d) exited with no errors.", pid);
+    strlcpy(username, con->username, sizeof(username));
+  }
 
-      /* How did snapctl terminate */
-      if (WIFEXITED(wstatus))
-      {
-       /* Via regular exit */
-       status = WEXITSTATUS(wstatus);
-       if (status == 100)
-       {
-         cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl not executed");
-         ret = 0;
-         goto snap_check_done;
-       }
-      }
-      else if (WIFSIGNALED(wstatus))
-      {
-       /* Via signal */
-       cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl caught the signal %d", WTERMSIG(wstatus));
-       ret = 0;
-       goto snap_check_done;
-      }
+ /*
+  * OK, got a username.  See if we need normal user access, or group
+  * access...
+  */
 
-#      endif /* HAVE_SNAPCTL_IS_CONNECTED */
-#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
+ /*
+  * Strip any @domain or @KDC from the username and owner...
+  */
 
-      switch (status)
-      {
-        case  0 :
-           cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap connecting via \"cups-control\" interface, access granted");
-           break;
-        case  1 :
-           cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap does not connect via \"cups-control\" interface, permission denied");
-           ret = 0;
-           break;
-        case 10 :
-           cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap under classic confinement, access granted");
-           break;
-        case 11 :
-           cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client is not a Snap, access granted");
-           break;
-        default :
-           cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl exited with unknown status: %d", status);
-           ret = 0;
-           break;
-      }
+  if ((ptr = strchr(username, '@')) != NULL)
+    *ptr = '\0';
 
-    snap_check_done:
-      if (context)
-       free(context);
-#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
-      g_clear_object(&client);
-#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
+  if (owner)
+  {
+    strlcpy(ownername, owner, sizeof(ownername));
 
-#  else
-#    if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB)
+    if ((ptr = strchr(ownername, '@')) != NULL)
+      *ptr = '\0';
+  }
+  else
+    ownername[0] = '\0';
 
-     /*
-      * If the client is a Snap, extract the client Snap's name from
-      * the AppArmor context and then query snapd to find out about the
-      * Snap's confinement type and whether it plugs cups-control. Grant
-      * access if
-      *
-      *   - the client is not a Snap
-      *   - the client is a classic Snap
-      *   - the client is a confined Snap plugging "cups-control"
-      *
-      * We deny access if
-      *
-      *   - the client is a confined Snap not plugging "cups-control"
-      *   - an error occurs during the steps of this method
-      *
-      * NOTE: This method is only for use of cupsd when it is not
-      *       packaged in a Snap. In a Snap one would need to plug the
-      *       snapd-control interface, which gives full control on
-      *       snapd, a high security risk. Therefore one will not get
-      *       automatic connection of this interface granted in the
-      *       Snap Store. This is the reason why the snapctl method
-      *       (above) got created by the snapd developers.
-      *
-      * This is the preferred method to run CUPS unsnapped, as this is
-      * the only way to check Snap status on clients from an unsnapped
-      * cupsd.
-      */
+ /*
+  * Get the user info...
+  */
 
-      /* If the AppArmor context does not begin with "snap.", then this
-         is not a snap */
-      if (strncmp(context, "snap.", 5) != 0)
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor context not from a Snap");
-        goto snap_check_done;
-      }
+  if (username[0])
+  {
+    pw = getpwnam(username);
+    endpwent();
+  }
+  else
+    pw = NULL;
 
-      dot = strchr(context + 5, '.');
-      if (dot == NULL)
-      {
-        cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Malformed snapd AppArmor profile name: %s", context);
-        goto snap_check_done;
-      }
-      snap_name = strndup(context + 5, (size_t)(dot - context - 5));
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client is the Snap %s", snap_name);
+ /*
+  * For matching user and group memberships below we will first go
+  * through all names except @SYSTEM to authorize the task as
+  * non-administrative, like printing or deleting one's own job, if this
+  * fails we will check whether we can authorize via the special name
+  * @SYSTEM, as an administrative task, like creating a print queue or
+  * deleting someone else's job.
+  * Note that tasks are considered as administrative by the policies
+  * in cupsd.conf, when they require the user or group @SYSTEM.
+  * We do this separation because if the client is a Snap connecting via
+  * domain socket, we need to additionally check whether it plugs to us
+  * through the "cups-control" interface which allows administration and
+  * not through the "cups" interface which allows only printing.
+  */
 
-      /* Connect to snapd */
-      snapd = snapd_client_new();
-      if (!snapd)
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not connect to snapd, permission denied");
-       ret = 0;
-       goto snap_check_done;
-      }
+  if (best->level == CUPSD_AUTH_USER)
+  {
+   /*
+    * If there are no names associated with this location, then
+    * any valid user is OK...
+    */
 
-      /* Check whether the client Snap is under classic confinement */
-      snap = snapd_client_get_snap_sync(snapd, snap_name, NULL, &error);
-      if (!snap)
+    if (cupsArrayCount(best->names) == 0)
+      return (HTTP_OK);
+
+   /*
+    * Otherwise check the user list and return OK if this user is
+    * allowed...
+    */
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership...");
+
+#ifdef HAVE_AUTHORIZATION_H
+   /*
+    * If an authorization reference was supplied it must match a right name...
+    */
+
+    if (con->authref)
+    {
+      for (name = (char *)cupsArrayFirst(best->names);
+           name;
+          name = (char *)cupsArrayNext(best->names))
       {
-        cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not obtain client Snap data: \"%s\", permission denied", error->message);
-       ret = 0;
-       goto snap_check_done;
+       if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9))
+         return (HTTP_OK);
       }
 
-      /* Snaps using classic confinement are granted access */
-      if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC)
+      for (name = (char *)cupsArrayFirst(best->names);
+           name;
+          name = (char *)cupsArrayNext(best->names))
       {
-        cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap under classic confinement, access granted");
-        goto snap_check_done;
+       if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
+           check_authref(con, SystemGroupAuthKey))
+         return (HTTP_OK);
       }
 
-      /* Get list of interfaces to which the client Snap is plugging */
-      if (!snapd_client_get_connections2_sync(snapd, SNAPD_GET_CONNECTIONS_FLAGS_NONE, snap_name, "cups-control", NULL, NULL, &plugs, NULL, NULL, &error))
+      return (HTTP_FORBIDDEN);
+    }
+#endif /* HAVE_AUTHORIZATION_H */
+
+    for (name = (char *)cupsArrayFirst(best->names);
+        name;
+        name = (char *)cupsArrayNext(best->names))
+    {
+      if (!_cups_strcasecmp(name, "@OWNER") && owner &&
+          !_cups_strcasecmp(username, ownername))
+       return (HTTP_OK);
+      else if (!_cups_strcasecmp(name, "@SYSTEM"))
       {
-        cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not obtain the client Snap's interface connections: \"%s\", permission denied", error->message);
-       ret = 0;
-       goto snap_check_done;
+       /* Do @SYSTEM later, when every other entry fails */
+       continue;
       }
-
-      if (plugs->len <= 0)
+      else if (name[0] == '@')
       {
-       cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap does not connect via \"cups-control\" interface, permission denied");
-       ret = 0;
-       goto snap_check_done;
+        if (cupsdCheckGroup(username, pw, name + 1))
+          return (HTTP_OK);
       }
+      else if (!_cups_strcasecmp(username, name))
+        return (HTTP_OK);
+    }
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap connecting via \"cups-control\" interface, access granted");
-
-    snap_check_done:
-      if (context)
-       free(context);
-      if (snap_name)
-       free(snap_name);
-      g_clear_object(&snapd);
-      g_clear_object(&snap);
-      if (plugs)
-       g_ptr_array_unref(plugs);
+    for (name = (char *)cupsArrayFirst(best->names);
+        name;
+        name = (char *)cupsArrayNext(best->names))
+    {
+      if (!_cups_strcasecmp(name, "@SYSTEM"))
+      {
+        for (i = 0; i < NumSystemGroups; i ++)
+         if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_task(con))
+           return (HTTP_OK);
+      }
+    }
 
-#    endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */
-#  endif /* SUPPORT_SNAPPED_CUPSD */
+    return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
+  }
 
-#  ifndef CHECK_METHOD_FOUND
+ /*
+  * Check to see if this user is in any of the named groups...
+  */
 
-     /*
-      * Issue warning if requirements for building Snap-related access control
-      * not fulfilled
-      */
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership...");
 
-      cupsdLogMessage(CUPSD_LOG_WARN, "cupsdCheckAdminTask: Compiling problem: none of the three access control methods (libsnapd-glib snapd access, \"snapctl is-connected\", libsnapd-glib-based snapctl call) available, no Snap-related access control built!");
+ /*
+  * Check to see if this user is in any of the named groups...
+  */
 
-    snap_check_done:
-      if (context)
-       free(context);
+  for (name = (char *)cupsArrayFirst(best->names);
+       name;
+       name = (char *)cupsArrayNext(best->names))
+  {
+    if (!_cups_strcasecmp(name, "@SYSTEM"))
+    {
+      /* Do @SYSTEM later, when every other entry fails */
+      continue;
+    }
 
-#  endif /* !CHECK_METHOD_FOUND */
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
 
-    }
+    if (cupsdCheckGroup(username, pw, name))
+      return (HTTP_OK);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Access %s", ret == 1 ? "granted" : "denied");
+  for (name = (char *)cupsArrayFirst(best->names);
+       name;
+       name = (char *)cupsArrayNext(best->names))
+  {
+    if (!_cups_strcasecmp(name, "@SYSTEM"))
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
 
-#else
+      for (i = 0; i < NumSystemGroups; i ++)
+       if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_task(con))
+         return (HTTP_OK);
+    }
+  }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Access granted (no extra checking)");
+ /*
+  * The user isn't part of the specified group, so deny access...
+  */
 
-#endif /* AF_LOCAL && SUPPORT_SNAPPED_CLIENTS */
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s).");
 
-  return ret;
+  return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
 }
 
 
 /*
- * 'cupsdIsAuthorized()' - Check to see if the user is authorized...
+ * 'cupsdNewLocation()' - Create a new location for authorization.
+ *
+ * Note: Still need to call cupsdAddLocation() to add it to the list of global
+ * locations.
  */
 
-http_status_t                          /* O - HTTP_OK if authorized or error code */
-cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
-                  const char     *owner)/* I - Owner of object */
+cupsd_location_t *                     /* O - Pointer to new location record */
+cupsdNewLocation(const char *location) /* I - Location path */
 {
-  int                  i,              /* Looping vars */
-                       auth,           /* Authorization status */
-                       type;           /* Type of authentication */
-  http_addr_t          *hostaddr = httpGetAddress(con->http);
-                                       /* Client address */
-  const char           *hostname = httpGetHostname(con->http, NULL, 0);
-                                       /* Client hostname */
-  unsigned             address[4];     /* Authorization address */
-  cupsd_location_t     *best;          /* Best match for location so far */
-  size_t               hostlen;        /* Length of hostname */
-  char                 *name,          /* Current username */
-                       username[256],  /* Username to authorize */
-                       ownername[256], /* Owner name to authorize */
-                       *ptr;           /* Pointer into username */
-  struct passwd                *pw;            /* User password data */
-  static const char * const levels[] = /* Auth levels */
-               {
-                 "ANON",
-                 "USER",
-                 "GROUP"
-               };
-  static const char * const types[] =  /* Auth types */
-               {
-                 "None",
-                 "Basic",
-                 "Negotiate"
-               };
+  cupsd_location_t     *temp;          /* New location */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location ? con->best->location : "(null)" : "");
-  if (owner)
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner);
+ /*
+  * Try to allocate memory for the new location.
+  */
+
+  if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
+    return (NULL);
 
  /*
-  * If there is no "best" authentication rule for this request, then
-  * access is allowed from the local system and denied from other
-  * addresses...
+  * Initialize the record and copy the name over...
   */
 
-  if (!con->best)
+  if ((temp->location = _cupsStrAlloc(location)) == NULL)
   {
-    if (httpAddrLocalhost(httpGetAddress(con->http)) ||
-        !strcmp(hostname, ServerName) ||
-       cupsArrayFind(ServerAlias, (void *)hostname))
-      return (HTTP_OK);
-    else
-      return (HTTP_FORBIDDEN);
+    free(temp);
+    return (NULL);
   }
 
-  best = con->best;
+  temp->length = strlen(temp->location);
 
-  if ((type = best->type) == CUPSD_AUTH_DEFAULT)
-    type = cupsdDefaultAuthType();
+ /*
+  * Return the new record...
+  */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: level=CUPSD_AUTH_%s, type=%s, satisfy=CUPSD_AUTH_SATISFY_%s, num_names=%d", levels[best->level], types[type], best->satisfy ? "ANY" : "ALL", cupsArrayCount(best->names));
+  return (temp);
+}
+
+
+/*
+ * 'check_admin_task()' - Do additional checks on administrative tasks
+ */
+
+static int                                      /* O - 1 if admin task authorized */
+check_admin_task(cupsd_client_t *con) /* I - Connection */
+{
+  int ret = 1; /* Return value */
 
-  if (best->limit == CUPSD_AUTH_LIMIT_IPP)
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", best->op, ippOpString(best->op));
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Administrative task");
 
  /*
-  * Check host/ip-based accesses...
+  * If the client accesses locally via domain socket, find out whether it
+  * is a Snap. Grant access if it is not a Snap, if it is a classic Snap
+  * or if it is a confined Snap which plugs "cups-control", deny access
+  * if it is a confined Snap not plugging "cups-control" or if an error
+  * occurs in the process of finding this out.
   */
 
-#ifdef AF_INET6
-  if (httpAddrFamily(hostaddr) == AF_INET6)
-  {
-   /*
-    * Copy IPv6 address...
-    */
+#if defined(AF_LOCAL) && defined(SUPPORT_SNAPPED_CLIENTS)
 
-    address[0] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[0]);
-    address[1] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[1]);
-    address[2] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[2]);
-    address[3] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[3]);
-  }
-  else
-#endif /* AF_INET6 */
-  if (con->http->hostaddr->addr.sa_family == AF_INET)
+ /*
+  * Get the client's file descriptor and from this its AppArmor context
+  */
+
+  if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL)
   {
-   /*
-    * Copy IPv4 address...
-    */
+    int                 peerfd;         /* Peer's file descriptor */
 
-    address[0] = 0;
-    address[1] = 0;
-    address[2] = 0;
-    address[3] = ntohl(hostaddr->ipv4.sin_addr.s_addr);
-  }
-  else
-    memset(address, 0, sizeof(address));
+    peerfd = httpGetFd(con->http);
 
-  hostlen = strlen(hostname);
+    if (peerfd < 0)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to get peer file descriptor of client connecting via domain socket");
+    }
+    else
+    {
+      char *context = NULL;      /* AppArmor profile name of client */
+#  undef CHECK_METHOD_FOUND
+#  ifdef SUPPORT_SNAPPED_CUPSD
+      int status = 65535;        /* Status of client Snap context check */
+#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
+#      define CHECK_METHOD_FOUND 1
+      char *args[] = { "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl arguments */
+      SnapdClient *client = NULL; /* Data structure of snapd access */
+      const char *cookie;        /* snapd access cookie */
+      GError *error = NULL;      /* Glib error */
+#    else
+#      ifdef HAVE_SNAPCTL_IS_CONNECTED
+#        define CHECK_METHOD_FOUND 1
+      char *args[] = { SNAPCTL, "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl command line */
+      int fds[2],                /* Pipe file descriptors for stderr of
+                                   snapctl */
+         nullfd;                /* /dev/null file descriptor for stdout of
+                                   snapctl */
+      pid_t pid;                 /* PID of snapctl */
+      cups_file_t *snapctl_stderr; /* CUPS FP for stderr of snapctl */
+      char buf[1024];            /* Buffer for snapctl's stderr output */
+      int wstatus;               /* Wait result of forked snapctl process */
+#      endif /* HAVE_SNAPCTL_IS_CONNECTED */
+#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
+#  else
+#    if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB)
+#      define CHECK_METHOD_FOUND 1
+      char *snap_name = NULL;    /* Client Snap name */
+      char *dot;                 /* Pointer to dot in AppArmor profile name */
+      SnapdClient *snapd = NULL; /* Data structure of snapd access */
+      GError *error = NULL;      /* Glib error */
+      SnapdSnap *snap = NULL;    /* Data structure of client Snap */
+      GPtrArray *plugs = NULL;   /* Plug search result of client Snap */
+#    endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */
+#  endif /* SUPPORT_SNAPPED_CUPSD */
 
-  auth = cupsdCheckAccess(address, hostname, hostlen, best)
-             ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY;
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...", auth ? "DENY" : "ALLOW");
+#  ifndef SUPPORT_SNAPPED_CUPSD
 
-  if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL)
-    return (HTTP_FORBIDDEN);
+      /* If AppArmor is not enabled, then we can't identify the client */
+      /* With cupsd running in a Snap, the "mount-observe" interface
+         needs to be plugged, therefore we do this only if not snapped. */
+      if (!aa_is_enabled())
+      {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: No AppArmor in use");
+       goto snap_check_done;
+      }
 
-#ifdef HAVE_TLS
- /*
-  * See if encryption is required...
-  */
+#  endif /* !SUPPORT_SNAPPED_CUPSD */
+
+      if (aa_getpeercon(peerfd, &context, NULL) < 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor profile could not be retrieved for client process - Error: %s", strerror(errno));
+       goto snap_check_done;
+      } else
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor profile of client process: %s", context);
+
+#  ifdef OUR_SNAP_NAME
+      /* Is the client one of the utilities of our Snap? */
+      if (!strncmp(context, "snap." OUR_SNAP_NAME ".", strlen(OUR_SNAP_NAME) + 6))
+      {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap is the same Snap we are running in, access granted");
+       goto snap_check_done;
+      }
+#  endif /* OUR_SNAP_NAME */
+
+#  ifdef SUPPORT_SNAPPED_CUPSD
+
+     /*
+      * Run
+      *
+      * snapctl is-connected --apparmor-label AA_CONTEXT CUPS_CONTROL_SLOT
+      *
+      * or an equivalent library function call using the
+      * snapd_client_run_snapctl2_sync() function of libsnapd-glib.
+      *
+      * Here AA_CONTEXT is the AppArmor profile name of the client, or
+      * "unconfined" for an unconfined client and CUPS_CONTROL_SLOT
+      * the name of the slot of the CUPS Snap to which clients plug
+      * with their cups-control plug in order to do administrative
+      * CUPS tasks.
+      *
+      * The exit status of the command/function call tells which type
+      * of client we have to do with:
+      *
+      *    0: The client is a confined Snap and plugs cups-control
+      *           -> Grant access
+      *    1: The client is a confined Snap and does not plug cups-control
+      *           -> Deny access
+      *   10: The client is a classic Snap
+      *           -> Grant access
+      *   11: The client is not a Snap
+      *           -> Grant access
+      *
+      * NOTE: This method only works if cupsd is running in a Snap
+      *       providing a slot for the client's "cups-control"
+      *       plug. Do not build CUPS with this method when intending
+      *       to use it unsnapped, for example in a Debian or RPM
+      *       package, or directly installed into your system. The
+      *       errors of snapctl missing or snapctl/the function
+      *       running without a Snap context will deny all
+      *       administrative accesses!
+      *
+      * When running inside a Snap this method is preferred, as it does not
+      * require full access to the snapd under which cupsd is running.
+      */
 
-  if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http->tls &&
-      _cups_strcasecmp(hostname, "localhost") &&
-      !httpAddrLocalhost(hostaddr) &&
-      best->satisfy == CUPSD_AUTH_SATISFY_ALL) &&
-      !(type == CUPSD_AUTH_NEGOTIATE ||
-        (type == CUPSD_AUTH_NONE &&
-         cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE)))
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "cupsdIsAuthorized: Need upgrade to TLS...");
-    return (HTTP_UPGRADE_REQUIRED);
-  }
-#endif /* HAVE_TLS */
+#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
 
- /*
-  * Now see what access level is required...
-  */
    /*
+      * Use the snapd_client_run_snapctl2_sync() function of libsnapd-glib
+      */
 
-  if (best->level == CUPSD_AUTH_ANON ||        /* Anonymous access - allow it */
-      (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0))
-    return (HTTP_OK);
+      /* Insert client Snap context in snapctl arguments */
+      args[2] = context;
 
-  if (!con->username[0] && type == CUPSD_AUTH_NONE &&
-      best->limit == CUPSD_AUTH_LIMIT_IPP)
-  {
-   /*
-    * Check for unauthenticated username...
-    */
+      /* Connect to snapd */
+      client = snapd_client_new();
+      if (!client)
+      {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not connect to snapd, permission denied");
+       ret = 0;
+       goto snap_check_done;
+      }
 
-    ipp_attribute_t    *attr;          /* requesting-user-name attribute */
+      /* snapctl commands are sent over a this socket that is made
+        available within the snap sandbox */
+      snapd_client_set_socket_path(client, "/run/snapd-snap.socket");
 
+      /* Take cookie from the environment if available */
+      cookie = g_getenv("SNAP_COOKIE");
+      if (!cookie)
+      {
+        cookie = "";
+       cupsdLogMessage(CUPSD_LOG_WARN, "check_admin_task: No SNAP_COOKIE set in the Snap environment, client Snap context check may not work");
+      }
 
-    attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
-    if (attr)
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                      "cupsdIsAuthorized: requesting-user-name=\"%s\"",
-                      attr->values[0].string.text);
-      strlcpy(username, attr->values[0].string.text, sizeof(username));
-    }
-    else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
-      return (HTTP_UNAUTHORIZED);      /* Non-anonymous needs user/pass */
-    else
-      return (HTTP_OK);                        /* unless overridden with Satisfy */
-  }
-  else
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"",
-                   con->username);
+      /* Do the client Snap context check */
+      if (!snapd_client_run_snapctl2_sync(client, cookie, args, NULL, NULL, &status, NULL, &error)) {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap context check error - %s", error->message);
+       ret = 0;
+       goto snap_check_done;
+      }
 
-#ifdef HAVE_AUTHORIZATION_H
-    if (!con->username[0] && !con->authref)
-#else
-    if (!con->username[0])
-#endif /* HAVE_AUTHORIZATION_H */
-    {
-      if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
-       return (HTTP_UNAUTHORIZED);     /* Non-anonymous needs user/pass */
-      else
-       return (HTTP_OK);               /* unless overridden with Satisfy */
-    }
+#    else
+#      ifdef HAVE_SNAPCTL_IS_CONNECTED
 
+     /*
+      * Call the snapctl executable using execv() in a fork
+      */
 
-    if (con->type != type && type != CUPSD_AUTH_NONE &&
-#ifdef HAVE_GSSAPI
-        (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) &&
-#endif /* HAVE_GSSAPI */
-        con->type != CUPSD_AUTH_BASIC)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s.",
-                      types[con->type], types[type]);
+      /* Insert client Snap context in snapctl command line */
+      args[3] = context;
 
-      return (HTTP_UNAUTHORIZED);
-    }
+      /* Create a pipe to catch stderr output from snapctl */
+      if (pipe(fds))
+      {
+       fds[0] = -1;
+       fds[1] = -1;
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to establish stderr pipe for snapctl call - %s", strerror(errno));
+       ret = 0;
+       goto snap_check_done;
+      }
 
-    strlcpy(username, con->username, sizeof(username));
-  }
+      /* Set the "close on exec" flag on each end of the pipe... */
+      if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC))
+      {
+       close(fds[0]);
+       close(fds[1]);
+       fds[0] = -1;
+       fds[1] = -1;
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to set \"close on exec\" flag on read end of the stderr pipe for snapctl call - %s", strerror(errno));
+       ret = 0;
+       goto snap_check_done;
+      }
+      if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC))
+      {
+       close(fds[0]);
+       close(fds[1]);
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to set \"close on exec\" flag on write end of the stderr pipe for snapctl call - %s", strerror(errno));
+       ret = 0;
+       goto snap_check_done;
+      }
 
- /*
-  * OK, got a username.  See if we need normal user access, or group
-  * access...
-  */
+      if ((pid = fork()) == 0)
+      {
+       /* Couple pipe with stderr of Ghostscript process */
+       if (fds[1] >= 0) {
+         if (fds[1] != 2) {
+           if (dup2(fds[1], 2) < 0) {
+             cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to couple pipe with stderr of snapctl process - %s", strerror(errno));
+             exit(100);
+           }
+           close(fds[1]);
+         }
+         close(fds[0]);
+       } else {
+         cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Invalid pipe file descriptor to couple with stderr of snapctl process - %s", strerror(errno));
+         exit(100);
+       }
 
- /*
-  * Strip any @domain or @KDC from the username and owner...
-  */
+       /* Send snapctl's stdout to the Nirwana, as snapctl is supposed to
+          not output anything here */
+       if ((nullfd = open("/dev/null", O_RDWR)) > 2)
+       {
+         dup2(nullfd, 1);
+         close(nullfd);
+       }
+       else
+         close(nullfd);
+       fcntl(1, F_SETFL, O_NDELAY);
 
-  if ((ptr = strchr(username, '@')) != NULL)
-    *ptr = '\0';
+       /* Execute snapctl command line ... */
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Running command: " SNAPCTL " is-connected --apparmor-label %s " CUPS_CONTROL_SLOT, context);
+       execv(SNAPCTL, args);
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to launch snapctl: %s", strerror(errno));
+       exit(100);
+      }
+      else if (pid < 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to fork for snapctl call - %s", strerror(errno));
+       ret = 0;
+       goto snap_check_done;
+      }
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Started snapctl (PID %d)", pid);
 
-  if (owner)
-  {
-    strlcpy(ownername, owner, sizeof(ownername));
+      close(fds[1]);
 
-    if ((ptr = strchr(ownername, '@')) != NULL)
-      *ptr = '\0';
-  }
-  else
-    ownername[0] = '\0';
+      /* Read out stderr from snapctl */
+      buf[0] = '\0';
+      snapctl_stderr = cupsFileOpenFd(fds[0], "r");
+      if (snapctl_stderr)
+      {
+       while (cupsFileGets(snapctl_stderr, buf, sizeof(buf)) && buf[0])
+       {
+         cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Error message from snapctl: %s", buf);
+         ret = 0;
+       }
+       cupsFileClose(snapctl_stderr);
+      }
+      close(fds[0]);
 
- /*
-  * Get the user info...
-  */
+      /* Wait for snapctl to finish */
+    retry_wait:
+      if (waitpid (pid, &wstatus, 0) == -1)
+      {
+       if (errno == EINTR)
+         goto retry_wait;
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl (PID %d) stopped with an error - %s", pid, strerror(errno));
+       ret = 0;
+       goto snap_check_done;
+      }
+      if (ret == 0)
+       goto snap_check_done;
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: snapctl (PID %d) exited with no errors.", pid);
 
-  if (username[0])
-  {
-    pw = getpwnam(username);
-    endpwent();
-  }
-  else
-    pw = NULL;
+      /* How did snapctl terminate */
+      if (WIFEXITED(wstatus))
+      {
+       /* Via regular exit */
+       status = WEXITSTATUS(wstatus);
+       if (status == 100)
+       {
+         cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl not executed");
+         ret = 0;
+         goto snap_check_done;
+       }
+      }
+      else if (WIFSIGNALED(wstatus))
+      {
+       /* Via signal */
+       cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl caught the signal %d", WTERMSIG(wstatus));
+       ret = 0;
+       goto snap_check_done;
+      }
 
- /*
-  * For matching user and group memberships below we will first go
-  * through all names except @SYSTEM to authorize the task as
-  * non-administrative, like printing or deleting one's own job, if this
-  * fails we will check whether we can authorize via the special name
-  * @SYSTEM, as an administrative task, like creating a print queue or
-  * deleting someone else's job.
-  * Note that tasks are considered as administrative by the policies
-  * in cupsd.conf, when they require the user or group @SYSTEM.
-  * We do this separation because if the client is a Snap connecting via
-  * domain socket, we need to additionally check whether it plugs to us
-  * through the "cups-control" interface which allows administration and
-  * not through the "cups" interface which allows only printing.
-  */
+#      endif /* HAVE_SNAPCTL_IS_CONNECTED */
+#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
 
-  if (best->level == CUPSD_AUTH_USER)
-  {
-   /*
-    * If there are no names associated with this location, then
-    * any valid user is OK...
-    */
+      switch (status)
+      {
+        case  0 :
+           cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap connecting via \"cups-control\" interface, access granted");
+           break;
+        case  1 :
+           cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap does not connect via \"cups-control\" interface, permission denied");
+           ret = 0;
+           break;
+        case 10 :
+           cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap under classic confinement, access granted");
+           break;
+        case 11 :
+           cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client is not a Snap, access granted");
+           break;
+        default :
+           cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl exited with unknown status: %d", status);
+           ret = 0;
+           break;
+      }
 
-    if (cupsArrayCount(best->names) == 0)
-      return (HTTP_OK);
+    snap_check_done:
+      if (context)
+       free(context);
+#    if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
+      g_clear_object(&client);
+#    endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */
 
-   /*
-    * Otherwise check the user list and return OK if this user is
-    * allowed...
-    */
+#  else
+#    if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB)
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership...");
+     /*
+      * If the client is a Snap, extract the client Snap's name from
+      * the AppArmor context and then query snapd to find out about the
+      * Snap's confinement type and whether it plugs cups-control. Grant
+      * access if
+      *
+      *   - the client is not a Snap
+      *   - the client is a classic Snap
+      *   - the client is a confined Snap plugging "cups-control"
+      *
+      * We deny access if
+      *
+      *   - the client is a confined Snap not plugging "cups-control"
+      *   - an error occurs during the steps of this method
+      *
+      * NOTE: This method is only for use of cupsd when it is not
+      *       packaged in a Snap. In a Snap one would need to plug the
+      *       snapd-control interface, which gives full control on
+      *       snapd, a high security risk. Therefore one will not get
+      *       automatic connection of this interface granted in the
+      *       Snap Store. This is the reason why the snapctl method
+      *       (above) got created by the snapd developers.
+      *
+      * This is the preferred method to run CUPS unsnapped, as this is
+      * the only way to check Snap status on clients from an unsnapped
+      * cupsd.
+      */
 
-#ifdef HAVE_AUTHORIZATION_H
-   /*
-    * If an authorization reference was supplied it must match a right name...
-    */
+      /* If the AppArmor context does not begin with "snap.", then this
+         is not a snap */
+      if (strncmp(context, "snap.", 5) != 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor context not from a Snap");
+        goto snap_check_done;
+      }
 
-    if (con->authref)
-    {
-      for (name = (char *)cupsArrayFirst(best->names);
-           name;
-          name = (char *)cupsArrayNext(best->names))
+      dot = strchr(context + 5, '.');
+      if (dot == NULL)
       {
-       if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9))
-         return (HTTP_OK);
+        cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Malformed snapd AppArmor profile name: %s", context);
+        goto snap_check_done;
       }
+      snap_name = strndup(context + 5, (size_t)(dot - context - 5));
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client is the Snap %s", snap_name);
 
-      for (name = (char *)cupsArrayFirst(best->names);
-           name;
-          name = (char *)cupsArrayNext(best->names))
+      /* Connect to snapd */
+      snapd = snapd_client_new();
+      if (!snapd)
       {
-       if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
-           check_authref(con, SystemGroupAuthKey) &&
-           cupsdCheckAdminTask(con))
-         return (HTTP_OK);
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not connect to snapd, permission denied");
+       ret = 0;
+       goto snap_check_done;
       }
 
-      return (HTTP_FORBIDDEN);
-    }
-#endif /* HAVE_AUTHORIZATION_H */
+      /* Check whether the client Snap is under classic confinement */
+      snap = snapd_client_get_snap_sync(snapd, snap_name, NULL, &error);
+      if (!snap)
+      {
+        cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not obtain client Snap data: \"%s\", permission denied", error->message);
+       ret = 0;
+       goto snap_check_done;
+      }
 
-    for (name = (char *)cupsArrayFirst(best->names);
-        name;
-        name = (char *)cupsArrayNext(best->names))
-    {
-      if (!_cups_strcasecmp(name, "@OWNER") && owner &&
-          !_cups_strcasecmp(username, ownername))
-       return (HTTP_OK);
-      else if (!_cups_strcasecmp(name, "@SYSTEM"))
+      /* Snaps using classic confinement are granted access */
+      if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC)
       {
-       /* Do @SYSTEM later, when every other entry fails */
-       continue;
+        cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap under classic confinement, access granted");
+        goto snap_check_done;
       }
-      else if (name[0] == '@')
+
+      /* Get list of interfaces to which the client Snap is plugging */
+      if (!snapd_client_get_connections2_sync(snapd, SNAPD_GET_CONNECTIONS_FLAGS_NONE, snap_name, "cups-control", NULL, NULL, &plugs, NULL, NULL, &error))
       {
-        if (cupsdCheckGroup(username, pw, name + 1))
-          return (HTTP_OK);
+        cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not obtain the client Snap's interface connections: \"%s\", permission denied", error->message);
+       ret = 0;
+       goto snap_check_done;
       }
-      else if (!_cups_strcasecmp(username, name))
-        return (HTTP_OK);
-    }
 
-    for (name = (char *)cupsArrayFirst(best->names);
-        name;
-        name = (char *)cupsArrayNext(best->names))
-    {
-      if (!_cups_strcasecmp(name, "@SYSTEM"))
+      if (plugs->len <= 0)
       {
-        for (i = 0; i < NumSystemGroups; i ++)
-         if (cupsdCheckGroup(username, pw, SystemGroups[i]) &&
-             cupsdCheckAdminTask(con))
-           return (HTTP_OK);
+       cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap does not connect via \"cups-control\" interface, permission denied");
+       ret = 0;
+       goto snap_check_done;
       }
-    }
 
-    return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
-  }
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap connecting via \"cups-control\" interface, access granted");
 
- /*
-  * Check to see if this user is in any of the named groups...
-  */
+    snap_check_done:
+      if (context)
+       free(context);
+      if (snap_name)
+       free(snap_name);
+      g_clear_object(&snapd);
+      g_clear_object(&snap);
+      if (plugs)
+       g_ptr_array_unref(plugs);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership...");
+#    endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */
+#  endif /* SUPPORT_SNAPPED_CUPSD */
 
- /*
-  * Check to see if this user is in any of the named groups...
-  */
+#  ifndef CHECK_METHOD_FOUND
 
-  for (name = (char *)cupsArrayFirst(best->names);
-       name;
-       name = (char *)cupsArrayNext(best->names))
-  {
-    if (!_cups_strcasecmp(name, "@SYSTEM"))
-    {
-      /* Do @SYSTEM later, when every other entry fails */
-      continue;
-    }
+     /*
+      * Issue warning if requirements for building Snap-related access control
+      * not fulfilled
+      */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
+      cupsdLogMessage(CUPSD_LOG_WARN, "check_admin_task: Compiling problem: none of the three access control methods (libsnapd-glib snapd access, \"snapctl is-connected\", libsnapd-glib-based snapctl call) available, no Snap-related access control built!");
 
-    if (cupsdCheckGroup(username, pw, name))
-      return (HTTP_OK);
-  }
+    snap_check_done:
+      if (context)
+       free(context);
 
-  for (name = (char *)cupsArrayFirst(best->names);
-       name;
-       name = (char *)cupsArrayNext(best->names))
-  {
-    if (!_cups_strcasecmp(name, "@SYSTEM"))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
+#  endif /* !CHECK_METHOD_FOUND */
 
-      for (i = 0; i < NumSystemGroups; i ++)
-       if (cupsdCheckGroup(username, pw, SystemGroups[i]) &&
-           cupsdCheckAdminTask(con))
-         return (HTTP_OK);
     }
   }
 
- /*
-  * The user isn't part of the specified group, so deny access...
-  */
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s).");
-
-  return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED);
-}
-
-
-/*
- * 'cupsdNewLocation()' - Create a new location for authorization.
- *
- * Note: Still need to call cupsdAddLocation() to add it to the list of global
- * locations.
- */
-
-cupsd_location_t *                     /* O - Pointer to new location record */
-cupsdNewLocation(const char *location) /* I - Location path */
-{
-  cupsd_location_t     *temp;          /* New location */
-
-
- /*
-  * Try to allocate memory for the new location.
-  */
-
-  if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
-    return (NULL);
-
- /*
-  * Initialize the record and copy the name over...
-  */
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Access %s", ret == 1 ? "granted" : "denied");
 
-  if ((temp->location = _cupsStrAlloc(location)) == NULL)
-  {
-    free(temp);
-    return (NULL);
-  }
+#else
 
-  temp->length = strlen(temp->location);
+  (void)con;
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Access granted (no extra checking)");
 
- /*
-  * Return the new record...
-  */
+#endif /* AF_LOCAL && SUPPORT_SNAPPED_CLIENTS */
 
-  return (temp);
+  return ret;
 }
 
 
index 23993aaf9363d32b030c6c9c58be36f28d4952a9..d9192b424215ca0da1b6ab41b1742f09e927ca2b 100644 (file)
@@ -373,7 +373,6 @@ main(int  argc,                             /* I - Number of command-line args */
            default : /* Something we don't understand... */
                _cupsLangPrintf(stderr, _("%s: Error - unknown option \"-%c\"."), argv[0], *opt);
                usage(NULL);
-               break;
          }
        }
       }
index 189079f9201ad621427522b8400c5051ae189379..800ccff2317374d46d15969d1fa21fc7917ab366 100644 (file)
@@ -2207,7 +2207,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
             ipp_attribute_t *uri)      /* I - URI of printer */
 {
   http_status_t        status;                 /* Policy status */
-  int          i;                      /* Looping var */
+  int          i = 0;                  /* Looping var */
   char         scheme[HTTP_MAX_URI],   /* Method portion of URI */
                username[HTTP_MAX_URI], /* Username portion of URI */
                host[HTTP_MAX_URI],     /* Host portion of URI */
index 3b5b4c58c404dd5228eb65c5333ed4da15d293c9..4568fb94973d2af81c68133ec605b34f1bbae264 100644 (file)
@@ -151,7 +151,6 @@ main(int  argc,                             /* I - Number of command-line args */
 
            default :
                usage(opt);
-               break;
          }
       }
     }
index cdedc8899a564244d669c19e6f553cbc21e2f9d5..28631beecc88b9c94efe6a140ba19db81dacbe5f 100644 (file)
@@ -417,10 +417,8 @@ get_options(cups_option_t **options)       /* O - Options */
   * Load PPD file and the corresponding IPP <-> PPD cache data...
   */
 
-  if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL)
+  if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL && (ppd_cache = _ppdCacheCreateWithPPD(ppd)) != NULL)
   {
-    ppd_cache = _ppdCacheCreateWithPPD(ppd);
-
     /* TODO: Fix me - values are names, not numbers... Also need to support finishings-col */
     if ((value = getenv("IPP_FINISHINGS")) == NULL)
       value = getenv("IPP_FINISHINGS_DEFAULT");
index e89fb5bcad8f5bcd39ce0e90668abd09b9631a37..7c8bc6d96ef79c457404b9a3958155424f78d385 100644 (file)
@@ -1645,36 +1645,38 @@ do_test(_ipp_file_t    *f,              /* I - IPP data file */
         status_ok   = 1;
         repeat_test = 1;
       }
-
-      for (i = 0, status_ok = 0; i < data->num_statuses; i ++)
+      else
       {
-       if (data->statuses[i].if_defined &&
-           !_ippVarsGet(data->vars, data->statuses[i].if_defined))
-         continue;
-
-       if (data->statuses[i].if_not_defined &&
-           _ippVarsGet(data->vars, data->statuses[i].if_not_defined))
-         continue;
-
-       if (ippGetStatusCode(response) == data->statuses[i].status)
+       for (i = 0, status_ok = 0; i < data->num_statuses; i ++)
        {
-         status_ok = 1;
-
-         if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit)
-           repeat_test = 1;
+         if (data->statuses[i].if_defined &&
+             !_ippVarsGet(data->vars, data->statuses[i].if_defined))
+           continue;
 
-         if (data->statuses[i].define_match)
-           _ippVarsSet(data->vars, data->statuses[i].define_match, "1");
-       }
-       else
-       {
-         if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit)
-           repeat_test = 1;
+         if (data->statuses[i].if_not_defined &&
+             _ippVarsGet(data->vars, data->statuses[i].if_not_defined))
+           continue;
 
-         if (data->statuses[i].define_no_match)
+         if (ippGetStatusCode(response) == data->statuses[i].status)
          {
-           _ippVarsSet(data->vars, data->statuses[i].define_no_match, "1");
            status_ok = 1;
+
+           if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit)
+             repeat_test = 1;
+
+           if (data->statuses[i].define_match)
+             _ippVarsSet(data->vars, data->statuses[i].define_match, "1");
+         }
+         else
+         {
+           if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit)
+             repeat_test = 1;
+
+           if (data->statuses[i].define_no_match)
+           {
+             _ippVarsSet(data->vars, data->statuses[i].define_no_match, "1");
+             status_ok = 1;
+           }
          }
        }
       }
index 253289e8fe7180cf519b1b908b432b5bb00426cc..f4311664ae490da8dbbff31c82ceaaad96b8b45a 100644 (file)
                72D53A3015B4923F003F877F /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E5136B64AF00836530 /* CoreFoundation.framework */; };
                72D53A3115B4923F003F877F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E7136B64B000836530 /* Security.framework */; };
                72D53A3215B4923F003F877F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E8136B64B000836530 /* SystemConfiguration.framework */; };
-               72D53A3415B4925B003F877F /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3315B4925B003F877F /* ApplicationServices.framework */; };
+               72D53A3415B4925B003F877F /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3315B4925B003F877F /* ApplicationServices.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
                72D53A3515B49270003F877F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2C15B4913D003F877F /* IOKit.framework */; };
                72D53A3815B4929D003F877F /* colorman.c in Sources */ = {isa = PBXBuildFile; fileRef = 72D53A3615B4929D003F877F /* colorman.c */; };
                72D53A3A15B492FA003F877F /* libpam.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3915B492FA003F877F /* libpam.dylib */; };
-               72D53A3B15B4930A003F877F /* GSS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2915B49110003F877F /* GSS.framework */; };
-               72D53A3C15B4930A003F877F /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E6136B64B000836530 /* Kerberos.framework */; };
+               72D53A3B15B4930A003F877F /* GSS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2915B49110003F877F /* GSS.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+               72D53A3C15B4930A003F877F /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E6136B64B000836530 /* Kerberos.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
                72F75A5C1336F988004BB496 /* cupstestppd.c in Sources */ = {isa = PBXBuildFile; fileRef = 72F75A5B1336F988004BB496 /* cupstestppd.c */; };
                72F75A671336FA38004BB496 /* libcups.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72220EAE1333047D00FCA411 /* libcups.dylib */; };
 /* End PBXBuildFile section */