/*
* "$Id: printers.c 7968 2008-09-19 23:03:01Z mike $"
*
- * Printer routines for the Common UNIX Printing System (CUPS).
+ * Printer routines for the CUPS scheduler.
*
* Copyright 2007-2010 by Apple Inc.
* Copyright 1997-2007 by Easy Software Products, all rights reserved.
#include "cupsd.h"
#include <cups/dir.h>
+#ifdef HAVE_APPLICATIONSERVICES_H
+# include <ApplicationServices/ApplicationServices.h>
+#endif /* HAVE_APPLICATIONSERVICES_H */
+#ifdef HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+#endif /* HAVE_SYS_MOUNT_H */
+#ifdef HAVE_SYS_STATFS_H
+# include <sys/statfs.h>
+#endif /* HAVE_SYS_STATFS_H */
+#ifdef HAVE_SYS_STATVFS_H
+# include <sys/statvfs.h>
+#endif /* HAVE_SYS_STATVFS_H */
+#ifdef HAVE_SYS_VFS_H
+# include <sys/vfs.h>
+#endif /* HAVE_SYS_VFS_H */
/*
cupsdAddPrinter(const char *name) /* I - Name of printer */
{
cupsd_printer_t *p; /* New printer */
+ char uri[1024]; /* Printer URI */
/*
cupsdSetString(&p->info, name);
cupsdSetString(&p->hostname, ServerName);
- cupsdSetStringf(&p->uri, "ipp://%s:%d/printers/%s", ServerName, RemotePort,
- name);
+ httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ ServerName, RemotePort, "/printers/%s", name);
+ cupsdSetString(&p->uri, uri);
cupsdSetDeviceURI(p, "file:///dev/null");
p->state = IPP_PRINTER_STOPPED;
char filename[1024], /* Filename */
*notifier; /* Current notifier */
cupsd_policy_t *p; /* Current policy */
+ int k_supported; /* Maximum file size supported */
+#ifdef HAVE_STATFS
+ struct statfs spoolinfo; /* FS info for spool directory */
+ double spoolsize; /* FS size */
+#elif defined(HAVE_STATVFS)
+ struct statvfs spoolinfo; /* FS info for spool directory */
+ double spoolsize; /* FS size */
+#endif /* HAVE_STATFS */
static const int nups[] = /* number-up-supported values */
{ 1, 2, 4, 6, 9, 16 };
static const int orients[4] =/* orientation-requested-supported values */
IPP_GET_PRINTER_ATTRIBUTES,
IPP_HOLD_JOB,
IPP_RELEASE_JOB,
+ IPP_RESTART_JOB,
IPP_PAUSE_PRINTER,
IPP_RESUME_PRINTER,
IPP_PURGE_JOBS,
{ /* job-creation-attributes-supported */
"copies",
"finishings",
+ "ipp-attribute-fidelity",
"job-hold-until",
"job-name",
"job-priority",
"multiple-document-handling",
"number-up",
"output-bin",
+ "output-mode",
"orientation-requested",
"page-ranges",
"print-quality",
"multiple-document-handling",
"number-up",
"output-bin",
+ "output-mode",
"orientation-requested",
"page-ranges",
"print-quality",
"printer-location"
};
static const char * const which_jobs[] =
- { /* which-jobs-supported values */
- "completed",
- "not-completed",
- "aborted",
- "all",
- "canceled",
- "pending",
- "pending-held",
- "processing",
- "processing-stopped"
- };
+ { /* which-jobs-supported values */
+ "completed",
+ "not-completed",
+ "aborted",
+ "all",
+ "canceled",
+ "pending",
+ "pending-held",
+ "processing",
+ "processing-stopped"
+ };
if (CommonData)
CommonData = ippNew();
+ /*
+ * Get the maximum spool size based on the size of the filesystem used for
+ * the RequestRoot directory. If the host OS doesn't support the statfs call
+ * or the filesystem is larger than 2TiB, always report INT_MAX.
+ */
+
+#ifdef HAVE_STATFS
+ if (statfs(RequestRoot, &spoolinfo))
+ k_supported = INT_MAX;
+ else if ((spoolsize = (double)spoolinfo.f_bsize * spoolinfo.f_blocks / 1024) >
+ INT_MAX)
+ k_supported = INT_MAX;
+ else
+ k_supported = (int)spoolsize;
+
+#elif defined(HAVE_STATVFS)
+ if (statvfs(RequestRoot, &spoolinfo))
+ k_supported = INT_MAX;
+ else if ((spoolsize = (double)spoolinfo.f_frsize * spoolinfo.f_blocks / 1024) >
+ INT_MAX)
+ k_supported = INT_MAX;
+ else
+ k_supported = (int)spoolsize;
+
+#else
+ k_supported = INT_MAX;
+#endif /* HAVE_STATFS */
+
/*
* This list of attributes is sorted to improve performance when the
* client provides a requested-attributes attribute...
"ipp-versions-supported", sizeof(versions) / sizeof(versions[0]),
NULL, versions);
+ /* ippget-event-life */
+ ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "ippget-event-life", 15);
+
/* job-creation-attributes-supported */
ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
"job-creation-attributes-supported",
/* job-ids-supported */
ippAddBoolean(CommonData, IPP_TAG_PRINTER, "job-ids-supported", 1);
+ /* job-k-octets-supported */
+ ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+ "job-k-octets-supported", k_supported);
+
/* job-priority-supported */
ippAddInteger(CommonData, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
"job-priority-supported", 100);
/* page-ranges-supported */
ippAddBoolean(CommonData, IPP_TAG_PRINTER, "page-ranges-supported", 1);
- /* pdf-override-supported */
+ /* pdl-override-supported */
ippAddString(CommonData, IPP_TAG_PRINTER, IPP_TAG_KEYWORD | IPP_TAG_COPY,
- "pdl-override-supported", NULL, "not-attempted");
+ "pdl-override-supported", NULL, "attempted");
/* printer-op-policy-supported */
attr = ippAddStrings(CommonData, IPP_TAG_PRINTER, IPP_TAG_NAME | IPP_TAG_COPY,
*/
fchown(cupsFileNumber(fp), getuid(), Group);
- fchmod(cupsFileNumber(fp), 0600);
+ fchmod(cupsFileNumber(fp), ConfigFilePerm & 0600);
/*
* Write a small header to the file...
int /* O - 1 if something changed, 0 otherwise */
cupsdSetPrinterReasons(
- cupsd_printer_t *p, /* I - Printer */
- const char *s) /* I - Reasons strings */
+ cupsd_printer_t *p, /* I - Printer */
+ const char *s) /* I - Reasons strings */
{
int i, /* Looping var */
changed = 0; /* Did something change? */
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-hold-until-default"));
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-priority-default"));
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("job-sheets-default"));
- cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-default"));
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("media-col-default"));
cupsArrayAdd(CommonDefaults, _cupsStrAlloc("number-up-default"));
cupsArrayAdd(CommonDefaults,
_cupsStrAlloc("orientation-requested-default"));
- cupsArrayAdd(CommonDefaults, _cupsStrAlloc("sides-default"));
}
/*
if (!RunUser)
{
/*
- * Only use filters that are owned by root and do not have group or world
- * write permissions.
+ * Only use filters that are owned by root and do not have world write
+ * permissions.
*/
- if (fileinfo.st_uid ||
- (fileinfo.st_mode & (S_ISUID | S_IWGRP | S_IWOTH)) != 0)
+ if (fileinfo.st_uid || (fileinfo.st_mode & (S_ISUID | S_IWOTH)) != 0)
{
if (fileinfo.st_uid)
snprintf(p->state_message, sizeof(p->state_message),
if (!stat(filename, &fileinfo) &&
(fileinfo.st_uid ||
- (fileinfo.st_mode & (S_ISUID | S_IWGRP | S_IWOTH)) != 0))
+ (fileinfo.st_mode & (S_ISUID | S_IWOTH)) != 0))
{
if (fileinfo.st_uid)
snprintf(p->state_message, sizeof(p->state_message),
* Check to see if the cache is up-to-date...
*/
- snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp2", CacheDir, p->name);
+ snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp4", CacheDir, p->name);
if (stat(cache_name, &cache_info))
cache_info.st_mtime = 0;
- snprintf(pwg_name, sizeof(pwg_name), "%s/%s.pwg", CacheDir, p->name);
+ snprintf(pwg_name, sizeof(pwg_name), "%s/%s.pwg3", CacheDir, p->name);
if (stat(pwg_name, &pwg_info))
pwg_info.st_mtime = 0;
media_type ?
_pwgGetType(p->pwg,
media_type->choice) :
- NULL);
+ NULL);
ippAddCollection(p->ppd_attrs, IPP_TAG_PRINTER, "media-col-default",
col);
* Output bin...
*/
- if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
+ if (p->pwg && p->pwg->num_bins > 0)
{
attr = ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
- "output-bin-supported", output_bin->num_choices,
+ "output-bin-supported", p->pwg->num_bins,
NULL, NULL);
if (attr != NULL)
{
for (i = 0, val = attr->values;
- i < output_bin->num_choices;
+ i < p->pwg->num_bins;
i ++, val ++)
- val->string.text = _cupsStrAlloc(output_bin->choices[i].choice);
+ val->string.text = _cupsStrAlloc(p->pwg->bins[i].pwg);
}
+ if ((output_bin = ppdFindOption(ppd, "OutputBin")) != NULL)
+ {
+ for (i = 0; i < p->pwg->num_bins; i ++)
+ if (!strcmp(p->pwg->bins[i].ppd, output_bin->defchoice))
+ break;
+
+ if (i >= p->pwg->num_bins)
+ i = 0;
+
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-default", NULL, p->pwg->bins[i].pwg);
+ }
+ else
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-default", NULL, p->pwg->bins[0].pwg);
+ }
+ else if (((ppd_attr = ppdFindAttr(ppd, "DefaultOutputOrder",
+ NULL)) != NULL &&
+ !strcasecmp(ppd_attr->value, "Reverse")) ||
+ (!ppd_attr && ppd->manufacturer && /* "Compatibility heuristic" */
+ (!strcasecmp(ppd->manufacturer, "epson") ||
+ !strcasecmp(ppd->manufacturer, "lexmark"))))
+ {
+ /*
+ * Report that this printer has a single output bin that leaves pages face
+ * up.
+ */
+
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-supported", NULL, "face-up");
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-default", NULL, "face-up");
+ }
+ else
+ {
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-supported", NULL, "face-down");
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-bin-default", NULL, "face-down");
+ }
+
+ /*
+ * output-mode...
+ */
+
+ if (ppd->color_device)
+ {
+ static const char * const output_modes[] =
+ {
+ "monochrome",
+ "color"
+ };
+
+ ippAddStrings(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-mode-supported", 2, NULL, output_modes);
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
- "output-bin-default", NULL, output_bin->defchoice);
+ "output-mode-default", NULL, "color");
+ }
+ else
+ {
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-mode-supported", NULL, "monochrome");
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "output-mode-default", NULL, "monochrome");
}
/*
cupsdLogMessage(CUPSD_LOG_WARN,
"Bad resolution \"%s\" for printer %s.",
choice->choice, p->name);
- xdpi = ydpi = 72;
+ xdpi = ydpi = 300;
}
attr->values[i].resolution.xres = xdpi;
* Just the DefaultResolution to report...
*/
- xdpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10);
+ xdpi = ydpi = (int)strtol(ppd_attr->value, (char **)&resptr, 10);
if (resptr > ppd_attr->value && xdpi > 0)
{
if (*resptr == 'x')
cupsdLogMessage(CUPSD_LOG_WARN,
"Bad default resolution \"%s\" for printer %s.",
ppd_attr->value, p->name);
- xdpi = ydpi = 72;
+ xdpi = ydpi = 300;
}
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
"printer-resolution-default", IPP_RES_PER_INCH,
- 72, 72);
+ 300, 300);
ippAddResolution(p->ppd_attrs, IPP_TAG_PRINTER,
"printer-resolution-supported", IPP_RES_PER_INCH,
- 72, 72);
+ 300, 300);
}
/*
* Duplexing, etc...
*/
+ ppdMarkDefaults(ppd);
+
if ((duplex = ppdFindOption(ppd, "Duplex")) == NULL)
if ((duplex = ppdFindOption(ppd, "EFDuplex")) == NULL)
if ((duplex = ppdFindOption(ppd, "EFDuplexing")) == NULL)
ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
"sides-default", NULL, "one-sided");
}
+ else
+ {
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "sides-supported", NULL, "one-sided");
+ ippAddString(p->ppd_attrs, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+ "sides-default", NULL, "one-sided");
+ }
if (ppdFindOption(ppd, "Collate") != NULL)
p->type |= CUPS_PRINTER_COLLATE;
if (ppdFindAttr(ppd, "APRemoteQueueID", NULL))
p->type |= CUPS_PRINTER_REMOTE;
+#ifdef HAVE_APPLICATIONSERVICES_H
+ /*
+ * Convert the file referenced in APPrinterIconPath to a 128x128 PNG
+ * and save it as cacheDir/printername.png
+ */
+
+ if ((ppd_attr = ppdFindAttr(ppd, "APPrinterIconPath", NULL)) != NULL &&
+ ppd_attr->value)
+ {
+ CGImageRef imageRef = NULL;/* Current icon image */
+ CGImageRef biggestIconRef = NULL;
+ /* Biggest icon image */
+ CGImageRef closestTo128IconRef = NULL;
+ /* Icon image closest to and >= 128 */
+ CGImageSourceRef sourceRef; /* The file's image source */
+ char outPath[HTTP_MAX_URI];
+ /* The path to the PNG file */
+ CFURLRef outUrl; /* The URL made from the outPath */
+ CFURLRef icnsFileUrl; /* The URL of the original ICNS icon file */
+ CGImageDestinationRef destRef; /* The image destination to write */
+ size_t bytesPerRow; /* The bytes per row used for resizing */
+ CGContextRef context; /* The CG context used for resizing */
+
+ snprintf(outPath, sizeof(outPath), "%s/%s.png", CacheDir, p->name);
+ outUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8 *)outPath,
+ strlen(outPath),
+ FALSE);
+ icnsFileUrl = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8 *)ppd_attr->value,
+ strlen(ppd_attr->value),
+ FALSE);
+ if (outUrl && icnsFileUrl)
+ {
+ sourceRef = CGImageSourceCreateWithURL(icnsFileUrl, NULL);
+ if (sourceRef)
+ {
+ for (i = 0; i < CGImageSourceGetCount(sourceRef); i ++)
+ {
+ imageRef = CGImageSourceCreateImageAtIndex(sourceRef, i, NULL);
+ if (imageRef &&
+ CGImageGetWidth(imageRef) == CGImageGetHeight(imageRef))
+ {
+ /*
+ * Loop through remembering the icon closest to 128 but >= 128
+ * and then remember the largest icon.
+ */
+
+ if (CGImageGetWidth(imageRef) >= 128 &&
+ (!closestTo128IconRef ||
+ CGImageGetWidth(imageRef) <
+ CGImageGetWidth(closestTo128IconRef)))
+ {
+ CGImageRelease(closestTo128IconRef);
+ CGImageRetain(imageRef);
+ closestTo128IconRef = imageRef;
+ }
+
+ if (!biggestIconRef ||
+ CGImageGetWidth(imageRef) > CGImageGetWidth(biggestIconRef))
+ {
+ CGImageRelease(biggestIconRef);
+ CGImageRetain(imageRef);
+ biggestIconRef = imageRef;
+ }
+
+ CGImageRelease(imageRef);
+ }
+ }
+
+ if (biggestIconRef)
+ {
+ /*
+ * If biggestIconRef is NULL, we found no icons. Otherwise we first
+ * want the closest to 128, but if none are larger than 128, we want
+ * the largest icon available.
+ */
+
+ imageRef = closestTo128IconRef ? closestTo128IconRef :
+ biggestIconRef;
+ CGImageRetain(imageRef);
+ CGImageRelease(biggestIconRef);
+ CGImageRelease(closestTo128IconRef);
+ destRef = CGImageDestinationCreateWithURL(outUrl, kUTTypePNG, 1,
+ NULL);
+ if (destRef)
+ {
+ if (CGImageGetWidth(imageRef) != 128)
+ {
+ bytesPerRow = CGImageGetBytesPerRow(imageRef) /
+ CGImageGetWidth(imageRef) * 128;
+ context = CGBitmapContextCreate(NULL, 128, 128,
+ CGImageGetBitsPerComponent(imageRef),
+ bytesPerRow,
+ CGImageGetColorSpace(imageRef),
+ kCGImageAlphaPremultipliedFirst);
+ if (context)
+ {
+ CGContextDrawImage(context, CGRectMake(0, 0, 128, 128),
+ imageRef);
+ CGImageRelease(imageRef);
+ imageRef = CGBitmapContextCreateImage(context);
+ CGContextRelease(context);
+ }
+ }
+
+ CGImageDestinationAddImage(destRef, imageRef, NULL);
+ CGImageDestinationFinalize(destRef);
+ CFRelease(destRef);
+ }
+
+ CGImageRelease(imageRef);
+ }
+
+ CFRelease(sourceRef);
+ CFRelease(icnsFileUrl);
+ }
+
+ CFRelease(outUrl);
+ }
+ }
+#endif /* HAVE_APPLICATIONSERVICES_H */
+
/*
* Close the PPD and set the type...
*/