[Pkg-cups-devel] r440 - in cupsys/branches/cups-1.2-ubuntu/debian:
. patches
Martin Pitt
mpitt at alioth.debian.org
Wed Mar 14 09:25:29 CET 2007
Author: mpitt
Date: Wed Mar 14 08:25:28 2007
New Revision: 440
Added:
cupsys/branches/cups-1.2-ubuntu/debian/patches/98_search_mime_files_in_usr_share.dpatch
Modified:
cupsys/branches/cups-1.2-ubuntu/debian/changelog
cupsys/branches/cups-1.2-ubuntu/debian/cupsys.postinst
cupsys/branches/cups-1.2-ubuntu/debian/patches/00list
cupsys/branches/cups-1.2-ubuntu/debian/patches/96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch
Log:
* debian/cupsys.postinst: Assure that special permissions of lpd backend
are always correctly set for the link in /usr/lib/cups/backend (closes:
LP#91382).
* 98_search_mime_files_in_usr_share.dpatch: Let CUPS also search for
*.types and *.convs files in /usr/share/cups/mime. This way packages can
provide *.types and *.convs files which do not get considered as conffiles
(All files in /etc get considered as conffiles, closes: LP#36532).
* 96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch: Keep up with upstream
bug fixes:
o cupsd crash in get_jobs: In get_jobs when my-jobs is set, we compare
the job username before it is loaded (upstream STR #2288).
o Fixed messed-up french translation (upstream STR #2287).
o The scheduler's openssl certificate generation code was
broken on some platforms (upstream STR #2282).
o The scheduler's log rotation check for devices was
broken (upstream STR #2278).
o The LPD mini-daemon did not handle the document-format
option correctly (upstream STR #2266).
Modified: cupsys/branches/cups-1.2-ubuntu/debian/changelog
==============================================================================
--- cupsys/branches/cups-1.2-ubuntu/debian/changelog (original)
+++ cupsys/branches/cups-1.2-ubuntu/debian/changelog Wed Mar 14 08:25:28 2007
@@ -1,3 +1,26 @@
+cupsys (1.2.8-0ubuntu5) feisty; urgency=low
+
+ * debian/cupsys.postinst: Assure that special permissions of lpd backend
+ are always correctly set for the link in /usr/lib/cups/backend (closes:
+ LP#91382).
+ * 98_search_mime_files_in_usr_share.dpatch: Let CUPS also search for
+ *.types and *.convs files in /usr/share/cups/mime. This way packages can
+ provide *.types and *.convs files which do not get considered as conffiles
+ (All files in /etc get considered as conffiles, closes: LP#36532).
+ * 96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch: Keep up with upstream
+ bug fixes:
+ o cupsd crash in get_jobs: In get_jobs when my-jobs is set, we compare
+ the job username before it is loaded (upstream STR #2288).
+ o Fixed messed-up french translation (upstream STR #2287).
+ o The scheduler's openssl certificate generation code was
+ broken on some platforms (upstream STR #2282).
+ o The scheduler's log rotation check for devices was
+ broken (upstream STR #2278).
+ o The LPD mini-daemon did not handle the document-format
+ option correctly (upstream STR #2266).
+
+ -- Till Kamppeter <till.kamppeter at gmail.com> Mon, 12 Mar 2007 15:22:06 +0000
+
cupsys (1.2.8-0ubuntu4) feisty; urgency=low
* debian/cupsys.postinst: (De)activate backends also if the user initially
Modified: cupsys/branches/cups-1.2-ubuntu/debian/cupsys.postinst
==============================================================================
--- cupsys/branches/cups-1.2-ubuntu/debian/cupsys.postinst (original)
+++ cupsys/branches/cups-1.2-ubuntu/debian/cupsys.postinst Wed Mar 14 08:25:28 2007
@@ -140,6 +140,14 @@
IFS=$save_IFS
db_fset cupsys/backend changed false
fi
+
+ # Assure that the special permissions of the lpd backend are also set
+ # for the link in /usr/lib/cups/backend/
+ if [ -e /usr/lib/cups/backend/lpd ]; then
+ chown root:lp /usr/lib/cups/backend/lpd
+ chmod 4754 /usr/lib/cups/backend/lpd
+ fi
+
# In cupsys 1.2.8-0ubuntu3 the defaults for the active backends
# changed. snmp, scsi, and serial got added. So activate these
# backends.
Modified: cupsys/branches/cups-1.2-ubuntu/debian/patches/00list
==============================================================================
--- cupsys/branches/cups-1.2-ubuntu/debian/patches/00list (original)
+++ cupsys/branches/cups-1.2-ubuntu/debian/patches/00list Wed Mar 14 08:25:28 2007
@@ -37,6 +37,7 @@
92_texttops-prettyprint-crash.dpatch
94_doc-port-in-client-conf.dpatch
96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch
+98_search_mime_files_in_usr_share.dpatch
ubuntu-disable-browsing.dpatch
ubuntu-external-pam-helper.dpatch
ubuntu-default-error-policy-retry-job.dpatch
Modified: cupsys/branches/cups-1.2-ubuntu/debian/patches/96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch
==============================================================================
--- cupsys/branches/cups-1.2-ubuntu/debian/patches/96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch (original)
+++ cupsys/branches/cups-1.2-ubuntu/debian/patches/96_more-bug-fixes-between-cups-1.2.8-1.2.9.dpatch Wed Mar 14 08:25:28 2007
@@ -5,9 +5,26 @@
## DP: No description.
@DPATCH@
+diff -urNad cupsys-1.2.8~/config-scripts/cups-largefile.m4 cupsys-1.2.8/config-scripts/cups-largefile.m4
+--- cupsys-1.2.8~/config-scripts/cups-largefile.m4 2005-10-01 00:23:25.000000000 +0100
++++ cupsys-1.2.8/config-scripts/cups-largefile.m4 2007-03-13 15:02:07.000000000 +0000
+@@ -30,11 +30,11 @@
+ if test x$enable_largefile != xno; then
+ LARGEFILE="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE"
+
+- if test $ac_cv_sys_large_files = 1; then
++ if test x$ac_cv_sys_large_files = x1; then
+ LARGEFILE="$LARGEFILE -D_LARGE_FILES"
+ fi
+
+- if test $ac_cv_sys_file_offset_bits = 64; then
++ if test x$ac_cv_sys_file_offset_bits = x64; then
+ LARGEFILE="$LARGEFILE -D_FILE_OFFSET_BITS=64"
+ fi
+ fi
diff -urNad cupsys-1.2.8~/cups/file.c cupsys-1.2.8/cups/file.c
--- cupsys-1.2.8~/cups/file.c 2007-01-10 19:27:04.000000000 +0000
-+++ cupsys-1.2.8/cups/file.c 2007-03-11 17:45:03.000000000 +0000
++++ cupsys-1.2.8/cups/file.c 2007-03-13 15:02:07.000000000 +0000
@@ -508,7 +508,7 @@
*/
@@ -50,7 +67,7 @@
/*
diff -urNad cupsys-1.2.8~/cups/options.c cupsys-1.2.8/cups/options.c
--- cupsys-1.2.8~/cups/options.c 2006-02-22 22:43:17.000000000 +0000
-+++ cupsys-1.2.8/cups/options.c 2007-03-11 17:45:03.000000000 +0000
++++ cupsys-1.2.8/cups/options.c 2007-03-13 15:02:07.000000000 +0000
@@ -3,7 +3,7 @@
*
* Option routines for the Common UNIX Printing System (CUPS).
@@ -80,7 +97,7 @@
/*
diff -urNad cupsys-1.2.8~/filter/pstops.c cupsys-1.2.8/filter/pstops.c
--- cupsys-1.2.8~/filter/pstops.c 2007-02-07 20:54:37.000000000 +0000
-+++ cupsys-1.2.8/filter/pstops.c 2007-03-11 17:45:03.000000000 +0000
++++ cupsys-1.2.8/filter/pstops.c 2007-03-13 15:02:07.000000000 +0000
@@ -937,14 +937,21 @@
ppdEmitJCL(ppd, stdout, doc->job_id, doc->user, doc->title);
@@ -106,118 +123,12835 @@
* Then copy all of the pages...
*/
-diff -urNad cupsys-1.2.8~/pdftops/pdftops.cxx cupsys-1.2.8/pdftops/pdftops.cxx
---- cupsys-1.2.8~/pdftops/pdftops.cxx 2006-01-10 20:53:28.000000000 +0000
-+++ cupsys-1.2.8/pdftops/pdftops.cxx 2007-03-11 17:45:03.000000000 +0000
-@@ -279,9 +279,16 @@
-
- globalParams = new GlobalParams(buffer);
-
-- globalParams->setPSPaperWidth(width);
-- globalParams->setPSPaperHeight(length);
-- globalParams->setPSImageableArea(left, bottom, right, top);
-+ if (fit || globalParams->getPSPaperWidth() > 0)
-+ {
-+ // Only set paper size and area if we are fitting to the job's
-+ // page size or the pdftops.conf file does not contain
-+ // "psPaperSize match"...
-+ globalParams->setPSPaperWidth(width);
-+ globalParams->setPSPaperHeight(length);
-+ globalParams->setPSImageableArea(left, bottom, right, top);
-+ }
+diff -urNad cupsys-1.2.8~/locale/cups_fr.po cupsys-1.2.8/locale/cups_fr.po
+--- cupsys-1.2.8~/locale/cups_fr.po 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/locale/cups_fr.po 2007-03-13 15:02:07.000000000 +0000
+@@ -2843,2848 +2843,3 @@
+ #
+ # End of "$Id$".
+ #
+-#
+-# "$Id$"
+-#
+-# Message catalog template for the Common UNIX Printing System (CUPS).
+-#
+-# Copyright 2005-2006 by Easy Software Products.
+-#
+-# These coded instructions, statements, and computer programs are the
+-# property of Easy Software Products and are protected by Federal
+-# copyright law. Distribution and use rights are outlined in the file
+-# "LICENSE.txt" which should have been included with this file. If this
+-# file is missing or damaged please contact Easy Software Products
+-# at:
+-#
+-# Attn: CUPS Licensing Information
+-# Easy Software Products
+-# 44141 Airport View Drive, Suite 204
+-# Hollywood, Maryland 20636 USA
+-#
+-# Voice: (301) 373-9600
+-# EMail: cups-info at cups.org
+-# WWW: http://www.cups.org
+-#
+-#
+-# NDT: I did not translate the messages used in conformance, DSC and PPD tests,
+-# because they are destined to developers only, as far as I understand.
+-#
+-
+-msgid ""
+-msgstr ""
+-"Project-Id-Version: CUPS 1.2\n"
+-"Report-Msgid-Bugs-To: http://www.cups.org/str.php\n"
+-"POT-Creation-Date: 2006-05-21 10:08-0400\n"
+-"PO-Revision-Date: 2007-01-25 19:55+0200\n"
+-"Last-Translator: Philippe Combes <Philippe.Combes at ens-lyon.fr>\n"
+-"Language-Team: Français\n"
+-"Content-Type: text/plain; charset=utf-8\n"
+-"Content-Transfer-Encoding: 8bit\n"
+-
+-msgid "Options Installed"
+-msgstr "Options Installées"
+-
+-msgid "Class"
+-msgstr "Classe"
+-
+-msgid "Printer"
+-msgstr "Imprimante"
+-
+-msgid "Extra"
+-msgstr "Supplémentaire"
+-
+-msgid "General"
+-msgstr "Généralités"
+-
+-msgid "Media Size"
+-msgstr "Taille du support"
+-
+-msgid "Media Type"
+-msgstr "Type de support"
+-
+-msgid "Media Source"
+-msgstr "Source du support"
+-
+-msgid "Output Mode"
+-msgstr "Mode de sortie"
+-
+-msgid "Resolution"
+-msgstr "Résolution"
+-
+-msgid "Variable"
+-msgstr "Variable"
+-
+-msgid "Yes"
+-msgstr "Oui"
+-
+-msgid "No"
+-msgstr "Non"
+-
+-msgid "Auto"
+-msgstr "Auto"
+-
+-msgid "Enter your username and password or the root username and password to access this page."
+-msgstr "Entrez votre nom d'utilisateur et votre mot de passe UNIX ou bien identifiez-vous en tant que « root » pour accéder à cette page."
+-
+-msgid "You must use a https: URL to access this page."
+-msgstr "Vous devez utiliser une URL https: pour accéder à cette page."
+-
+-#, c-format
+-msgid "Bad request version number %d.%d!"
+-msgstr "La requête a un numéro de version erroné : %d.%d !"
+-
+-msgid "No attributes in request!"
+-msgstr "Aucun attribut dans la requête !"
+-
+-#, c-format
+-msgid "Attribute groups are out of order (%x < %x)!"
+-msgstr "Les groupes d'attributs ne sont pas dans le bon ordre (%x < %x) !"
+-
+-msgid "Missing required attributes!"
+-msgstr "Il manque des attributs indispensables !"
+-
+-#, c-format
+-msgid "%s not supported!"
+-msgstr "%s: opération non gérée !"
+-
+-msgid "The printer or class was not found."
+-msgstr "L'imprimante ou la classe n'a pas été trouvée."
+-
+-msgid "The printer-uri must be of the form \"ipp://HOSTNAME/classes/CLASSNAME\"."
+-msgstr "L'URI de l'imprimante doit suivre le format « ipp://NOM_MACHINE/classes/NOM_CLASSE »."
+-
+-#, c-format
+-msgid "The printer-uri \"%s\" contains invalid characters."
+-msgstr "L'URI de l'imprimante « %s » contient des caractères ."
+-
+-#, c-format
+-msgid "A printer named \"%s\" already exists!"
+-msgstr "Il existe déjà une imprimante appelée « %s » !"
+-
+-#, c-format
+-msgid "Attempt to set %s printer-state to bad value %d!"
+-msgstr "Tentative de donner une valeur erronée au paramètre « printer-state » : %d !"
+-
+-#, c-format
+-msgid "add_class: Unknown printer-op-policy \"%s\"."
+-msgstr "add_class: L'attribut « printer-op-policy » a une valeur non comprise : « %s »."
+-
+-#, c-format
+-msgid "add_class: Unknown printer-error-policy \"%s\"."
+-msgstr "add_class: L'attribut « printer-error-policy » a une valeur non comprise : « %s »."
+-
+-msgid "Unable to allocate memory for file types!"
+-msgstr "Impossible d'allouer de la mémoire pour les types de fichiers !"
+-
+-#, c-format
+-msgid "Character set \"%s\" not supported!"
+-msgstr "Jeu de caractères « %s » non géré !"
+-
+-#, c-format
+-msgid "Language \"%s\" not supported!"
+-msgstr "Langue « %s » non gérée !"
+-
+-#, c-format
+-msgid "The notify-user-data value is too large (%d > 63 octets)!"
+-msgstr "La valeur de l'attribut « notify-user-data » est trop grande ( %d > 63 octets ) !"
+-
+-msgid "The notify-lease-duration attribute cannot be used with job subscriptions."
+-msgstr "L'attribut « notify-lease-duration » ne peut pas être utilisé dans une souscription de tâche."
+-
+-msgid "The printer-uri must be of the form \"ipp://HOSTNAME/printers/PRINTERNAME\"."
+-msgstr "L'URI de l'imprimante doit suivre le format « ipp://NOM_MACHINE/printers/NOM_CLASSE »."
+-
+-#, c-format
+-msgid "A class named \"%s\" already exists!"
+-msgstr ""Il existe déjà une classe appelée « %s » !"
+-
+-#, c-format
+-msgid "File device URIs have been disabled! To enable, see the FileDevice directive in \"%s/cupsd.conf\"."
+-msgstr "L'impression dans un fichier a été désactivée ! Pour l'activer, cf. la directive « FileDevice » dans « %s/cupsd.conf »."
+-
+-#, c-format
+-msgid "Bad device-uri \"%s\"!"
+-msgstr "Valeur erronée pour le paramètre « device-uri » : « %s » !"
+-
+-#, c-format
+-msgid "Bad port-monitor \"%s\"!"
+-msgstr "Valeur erronée pour le paramètre « port-monitor » : « %s » !"
+-
+-#, c-format
+-msgid "Bad printer-state value %d!"
+-msgstr "Valeur erronée pour le paramètre « printer-state » : « %s » !"
+-
+-#, c-format
+-msgid "Unknown printer-op-policy \"%s\"."
+-msgstr "L'attribut « printer-op-policy » a une valeur non comprise : « %s »."
+-
+-#, c-format
+-msgid "Unknown printer-error-policy \"%s\"."
+-msgstr "L'attribut « printer-error-policy » a une valeur non comprise : « %s »."
+-
+-#, c-format
+-msgid "Unable to copy interface script - %s!"
+-msgstr "Impossible de copier le « script » d'interface - « %s » !"
+-
+-#, c-format
+-msgid "Unable to copy PPD file - %s!"
+-msgstr "Impossible de copier le fichier PPD - « %s » !"
+-
+-msgid "Unable to copy PPD file!"
+-msgstr "Impossible de copier le fichier PPD !"
+-
+-msgid "Got a printer-uri attribute but no job-id!"
+-msgstr "Attribut « printer-uri » trouvé mais pas de « job-id »"
+-
+-#, c-format
+-msgid "Bad job-uri attribute \"%s\"!"
+-msgstr "Valeur erronée pour l'attribut « job-uri » : « %s » !"
+-
+-#, c-format
+-msgid "Job #%d doesn't exist!"
+-msgstr "La tâche n°%d n'existe pas !"
+-
+-#, c-format
+-msgid "Job #%d is not held for authentication!"
+-msgstr "La tâche n°%d n'est pas en attente d'authentification !"
+-
+-#, c-format
+-msgid "You are not authorized to authenticate job #%d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation d'authentifier la tâche n°%d appartenant à « %s » !"
+-
+-msgid "The printer-uri attribute is required!"
+-msgstr "L'attribut « printer-uri » est indispensable !"
+-
+-msgid "Missing requesting-user-name attribute!"
+-msgstr "Il manque l'attribut « requesting-user-name » !"
+-
+-#, c-format
+-msgid "The printer-uri \"%s\" is not valid."
+-msgstr "L'attribut « printer-uri » est incorrect : « %s »."
+-
+-#, c-format
+-msgid "No active jobs on %s!"
+-msgstr "Il n'y a pas de tâche en cours sur « %s » !"
+-
+-#, c-format
+-msgid "You are not authorized to delete job #%d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de supprimer la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "Job #%d is already %s - can't cancel."
+-msgstr "La tâche n°%d est déjà « %s » - impossible de l'annuler."
+-
+-msgid "The printer or class is not shared!"
+-msgstr "L'imprimante ou la classe n'est pas partagée !"
+-
+-#, c-format
+-msgid "Destination \"%s\" is not accepting jobs."
+-msgstr "La destination « %s » n'accepte aucune tâche."
+-
+-#, c-format
+-msgid "Bad copies value %d."
+-msgstr "Nombre de copies erroné : %d."
+-
+-#, c-format
+-msgid "Bad page-ranges values %d-%d."
+-msgstr "Intervalle de pages erroné : %d-%d."
+-
+-msgid "Too many active jobs."
+-msgstr "Trop de tâches en cours."
+-
+-msgid "Quota limit reached."
+-msgstr "Quota atteint."
+-
+-#, c-format
+-msgid "Unable to add job for destination \"%s\"!"
+-msgstr "Impossible d'ajouter une tâche pour la destination « %s » !"
+-
+-msgid "No subscription attributes in request!"
+-msgstr "Pas d'attribut de souscription dans la requête !"
+-
+-msgid "notify-events not specified!"
+-msgstr "Attribut « notify-events » non renseigné !"
+-
+-#, c-format
+-msgid "Job %d not found!"
+-msgstr "La tâche n°%d n'a pas été trouvée !"
+-
+-msgid "No default printer"
+-msgstr "Pas d'imprimante par défaut"
+-
+-msgid "cups-deviced failed to execute."
+-msgstr "L'exécution de « cups-deviced » a échouée."
+-
+-msgid "cups-driverd failed to execute."
+-msgstr "L'exécution de « cups-driverd » a échouée."
+-
+-msgid "No destinations added."
+-msgstr "Aucune destination ajoutée."
+-
+-#, c-format
+-msgid "notify-subscription-id %d no good!"
+-msgstr "Valeur erronée pour l'attribut « notify-subscription-id » : %d !"
+-
+-#, c-format
+-msgid "Job #%s does not exist!"
+-msgstr "La tâche n°%s n'existe pas !"
+-
+-#, c-format
+-msgid "Job #%d does not exist!"
+-msgstr "La tâche n°%d n'existe pas !"
+-
+-msgid "No subscriptions found."
+-msgstr "Aucune souscription trouvée."
+-
+-#, c-format
+-msgid "Not authorized to hold job #%d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de retenir la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "Job #%d is finished and cannot be altered!"
+-msgstr "La tâche n°%d est terminée et ne peut plus être modifiée !"
+-
+-#, c-format
+-msgid "You are not authorized to move job #%d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de transférer la tâche n°%d appartenant à « %s » !"
+-
+-msgid "job-printer-uri attribute missing!"
+-msgstr "Il manque l'attribut « job-printer-uri » !"
+-
+-#, c-format
+-msgid "Unsupported compression \"%s\"!"
+-msgstr "La compression « %s » n'est pas gérée !"
+-
+-msgid "No file!?!"
+-msgstr "Pas de fichier !?!"
+-
+-#, c-format
+-msgid "Could not scan type \"%s\"!"
+-msgstr "Impossible de comprendre le format « %s » !"
+-
+-#, c-format
+-msgid "Unsupported format '%s/%s'!"
+-msgstr "Le format « %s » n'est pas géré !"
+-
+-msgid "Printer not shared!"
+-msgstr "L'imprimante n'est pas partagée !"
+-
+-#, c-format
+-msgid "Too many jobs - %d jobs, max jobs is %d."
+-msgstr "Trop de tâches - %d tâches pour un maximum de %d."
+-
+-#, c-format
+-msgid "Job #%d is not held!"
+-msgstr "La tâche n°%d n'est pas retenue !"
+-
+-#, c-format
+-msgid "You are not authorized to release job id %d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de libérer la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "Job #%d is not complete!"
+-msgstr "La tâche n°%d n'est pas terminée !"
+-
+-#, c-format
+-msgid "Job #%d cannot be restarted - no files!"
+-msgstr "La tâche n°%d ne peut être redémarrée - aucun fichier !"
+-
+-#, c-format
+-msgid "You are not authorized to restart job id %d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de redémarrer la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "You are not authorized to send document for job #%d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation d'envoyer un document pour la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "Bad document-format \"%s\"!"
+-msgstr "Format de document erroné : « %s » !"
+-
+-#, c-format
+-msgid "You are not authorized to alter job id %d owned by \"%s\"!"
+-msgstr "Vous n'avez pas l'autorisation de modifier la tâche n°%d appartenant à « %s » !"
+-
+-#, c-format
+-msgid "%s cannot be changed."
+-msgstr "Impossible de modifier « %s »"
+-
+-msgid "Bad job-priority value!"
+-msgstr "Valeur erronée pour l'attribut « job-priority » !"
+-
+-msgid "Job is completed and cannot be changed."
+-msgstr "La tâche est terminée et ne peut être modifiée."
+-
+-msgid "Bad job-state value!"
+-msgstr "Valeur erronée pour l'attribut « job-state » !"
+-
+-msgid "Job state cannot be changed."
+-msgstr "L'état de la tâche ne peut être modifié."
+-
+-#, c-format
+-msgid "Unsupported compression attribute %s!"
+-msgstr "L'attribute de compression %s n'est pas géré !"
+-
+-#, c-format
+-msgid "Unsupported format \"%s\"!"
+-msgstr "Le format « %s » n'est pas géré !"
+-
+-#, c-format
+-msgid "%s is not implemented by the CUPS version of lpc.\n"
+-msgstr "« %s » n'est pas implanté dans la version CUPS de lpc.\n"
+-
+-msgid ""
+-"Commands may be abbreviated. Commands are:\n"
+-"\n"
+-"exit help quit status ?\n"
+-msgstr ""
+-"Les commandes peuvent être abrégée. Elles sont:\n"
+-"\n"
+-"exit help quit status ?\n"
+-
+-msgid "help\t\tget help on commands\n"
+-msgstr "help\t\tobtenir de l'aide sur les commandes\n"
+-
+-msgid "status\t\tshow status of daemon and queue\n"
+-msgstr "status\t\taffiche l'état du démon et de la file\n"
+-
+-msgid "?Invalid help command unknown\n"
+-msgstr "?Commande d'aide inconnue\n"
+-
+-#, c-format
+-msgid "\tprinter is on device '%s' speed -1\n"
+-msgstr "\tl'imprimante est sur le périphérique '%s', vitesse -1\n"
+-
+-msgid "\tqueuing is enabled\n"
+-msgstr "\tla mise en file d'attente est permise\n"
+-
+-msgid "\tqueuing is disabled\n"
+-msgstr "\tla mise en file d'attente est refusée\n"
+-
+-msgid "\tprinting is enabled\n"
+-msgstr "\tl'impression est permise\n"
+-
+-msgid "\tprinting is disabled\n"
+-msgstr "\tl'impression est refusée\n"
+-
+-msgid "\tno entries\n"
+-msgstr "\taucune entrée\n"
+-
+-#, c-format
+-msgid "\t%d entries\n"
+-msgstr "\t%d entrées\n"
+-
+-msgid "\tdaemon present\n"
+-msgstr "\tdémon présent\n"
+-
+-msgid "lpq: Unable to contact server!\n"
+-msgstr "lpq: Impossible de contacter le serveur !\n"
+-
+-#, c-format
+-msgid "%s: Sorry, no encryption support compiled in!\n"
+-msgstr "%s: Désolé, la gestion du cryptage n'a pas été compilée !\n"
+-
+-#, c-format
+-msgid "lpq: Unknown destination \"%s/%s\"!\n"
+-msgstr lpq: Destination « %s/%s » inconnue !\n""
+-
+-#, c-format
+-msgid "lpq: Unknown destination \"%s\"!\n"
+-msgstr "lpq: Destination « %s » inconnue !\n"
+-
+-#, c-format
+-msgid "lp: error - %s environment variable names non-existent destination \"%s\"!\n"
+-msgstr "lp: erreur - la variable d'environnement %s désigne une destination inexistente « %s » !\n"
+-
+-msgid "lpq: error - no default destination available.\n"
+-msgstr "lpq: erreur - aucune destination par défaut n'est disponible.\n"
+-
+-#, c-format
+-msgid "lpq: get-jobs failed: %s\n"
+-msgstr "lpq: « get-jobs » a échoué: %s\n"
+-
+-msgid "Rank Owner Pri Job Files Total Size\n"
+-msgstr "Rang Propr. Prio Tâche Fichiers Taille totale\n"
+-
+-msgid "Rank Owner Job File(s) Total Size\n"
+-msgstr "Rang Propr. Tâche Fichiers Taille totale\n"
+-
+-#, c-format
+-msgid "%s: %-33.33s [job %d localhost]\n"
+-msgstr "%s: %-33.33s [tâche %d localhost]\n"
+-
+-#, c-format
+-msgid " %-39.39s %.0f bytes\n"
+-msgstr " %-39.39s %.0f octets\n"
+-
+-#, c-format
+-msgid "%-6s %-10.10s %-4d %-10d %-27.27s %.0f bytes\n"
+-msgstr "%-6s %-10.10s %-4d %-10d %-27.27s %.0f octets\n"
+-
+-#, c-format
+-msgid "%-7s %-7.7s %-7d %-31.31s %.0f bytes\n"
+-msgstr "%-7s %-7.7s %-7d %-31.31s %.0f octets\n"
+-
+-msgid "no entries\n"
+-msgstr "aucune entrée\n"
+-
+-#, c-format
+-msgid "lpq: get-printer-attributes failed: %s\n"
+-msgstr "lpq: get-printer-attributes a échoué : %s\n"
+-
+-#, c-format
+-msgid "%s is ready\n"
+-msgstr "%s est prête\n"
+-
+-#, c-format
+-msgid "%s is ready and printing\n"
+-msgstr "%s est prête et en cours d'impression\n"
+-
+-#, c-format
+-msgid "%s is not ready\n"
+-msgstr "%s n'est pas prête\n"
+-
+-msgid "Usage: lpq [-P dest] [-l] [+interval]\n"
+-msgstr "Utilisation : lpq [-P dest] [-l] [+intervalle]\n"
+-
+-#, c-format
+-msgid "lpr: error - expected value after -%c option!\n"
+-msgstr "lpr: erreur - il faut une valeur après l'option '-%c' !\n"
+-
+-#, c-format
+-msgid "lpr: warning - '%c' format modifier not supported - output may not be correct!\n"
+-msgstr "lpr: attention - le modificateur de format '%c' n'est pas géré - l'affichage pourrait être incorrect !\n"
+-
+-msgid "lpr: error - expected option=value after -o option!\n"
+-msgstr "lpr: erreur - il faut un argument du type option=valeur après l'option '-o' !\n"
+-
+-msgid "lpr: warning - email notification is not currently supported!\n"
+-msgstr "lpr: attention - la notification par courriel n'est pas encore gérée !\n"
+-
+-msgid "lpr: error - expected destination after -P option!\n"
+-msgstr "lpr: erreur - il faut une destination après l'option '-P' !\n"
+-
+-msgid "lpr: error - expected copy count after -# option!\n"
+-msgstr "lpr: erreur - il faut un nombre de copies après l'option '-#' !\n"
+-
+-#, c-format
+-msgid "lpr: error - expected name after -%c option!\n"
+-msgstr "lpr: erreur - il faut un nom après l'option '-%c' !\n"
+-
+-msgid "lpr: error - expected username after -U option!\n"
+-msgstr "lpr: erreur - il faut un nom d'utilisateur après l'option '-U' !\n"
+-
+-#, c-format
+-msgid "lpr: error - unknown option '%c'!\n"
+-msgstr "lpr: erreur - option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "lpr: error - unable to access \"%s\" - %s\n"
+-msgstr "lpr: erreur - impossible d'accéder à « %s » - %s\n"
+-
+-#, c-format
+-msgid "lpr: error - too many files - \"%s\"\n"
+-msgstr "lpr: erreur - trop de fichiers - « %s »\n"
+-
+-#, c-format
+-msgid "lpr: error - %s environment variable names non-existent destination \"%s\"!\n"
+-msgstr "lpr: erreur - la variable d'environnement %s désigne une destination inexistente « %s » !\n"
+-
+-msgid "lpr: error - no default destination available.\n"
+-msgstr "lpr: erreur - aucune destination par défaut n'est disponible.\n"
+-
+-msgid "lpr: error - scheduler not responding!\n"
+-msgstr "lpr: erreur - l'ordonnanceur ne répond pas !\n"
+-
+-#, c-format
+-msgid "lpr: error - unable to create temporary file \"%s\" - %s\n"
+-msgstr "lpr: erreur - impossible de créer le fichier temporaire « %s » - %s\n"
+-
+-#, c-format
+-msgid "lpr: error - unable to write to temporary file \"%s\" - %s\n"
+-msgstr "lpr: erreur - impossible d'écrire dans le fichier temporaire « %s » - %s\n"
+-
+-msgid "lpr: error - stdin is empty, so no job has been sent.\n"
+-msgstr "lpr: erreur - stdin est vide, donc aucune tâche n'a été envoyée.\n"
+-
+-#, c-format
+-msgid "lpr: error - unable to print file: %s\n"
+-msgstr "lpr: erreur - impossible d'imprimer le fichier : %s\n"
+-
+-msgid "lprm: Unable to contact server!\n"
+-msgstr "lprm: Impossible de contacter le serveur !\n"
+-
+-#, c-format
+-msgid "lprm: Unknown destination \"%s\"!\n"
+-msgstr "lprm: Destination « %s » inconnue !\n"
+-
+-#, c-format
+-msgid "lprm: Unknown option '%c'!\n"
+-msgstr "lprm: Option '%c' inconnue !\n"
+-
+-msgid "lprm: Job or printer not found!\n"
+-msgstr "lprm: La tâche ou l'imprimante n'a pas été trouvée !\n"
+-
+-msgid "lprm: Not authorized to lprm job(s)!\n"
+-msgstr "lprm: Vous n'avez pas l'autorisation de supprimer cette(ces) tâche(s) !\n"
+-
+-#, c-format
+-msgid "lprm: You don't own job ID %d!\n"
+-msgstr lprm: La tâche n°%d ne vous appartient pas !\n""
+-
+-msgid "lprm: Unable to lprm job(s)!\n"
+-msgstr "lprm: Impossible de supprimer cette(ces) tâche(s) !\n"
+-
+-msgid "lprm: Unable to cancel job(s)!\n"
+-msgstr "lprm: Impossible d'annuler cette(ces) tâche(s) !\n"
+-
+-#, c-format
+-msgid "%s: Don't know what to do!\n"
+-msgstr "%s: Je ne sais que faire !"
+-
+-#, c-format
+-msgid "%s: Expected server name after -h!\n"
+-msgstr "%s: Il faut un nom de serveur après l'option '-h' !\n"
+-
+-#, c-format
+-msgid "%s: Expected reason text after -r!\n"
+-msgstr "%s: Il faut le texte de la raison après l'option '-r' !\n"
+-
+-#, c-format
+-msgid "%s: Unknown option '%c'!\n"
+-msgstr "%s: Option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "%s: Unable to connect to server: %s\n"
+-msgstr "%s: Impossible de se connecter au serveur : %s\n"
+-
+-#, c-format
+-msgid "%s: Operation failed: %s\n"
+-msgstr "%s: L'opération a échoué : %s\n"
+-
+-msgid "cancel: Error - expected hostname after '-h' option!\n"
+-msgstr "annulation: erreur - il faut un nom de machine après l'option '-h' !\n"
+-
+-msgid "cancel: Error - expected username after '-u' option!\n"
+-msgstr "annulation: erreur - il faut un nom d'utilisateur après l'option '-u' !\n"
+-
+-#, c-format
+-msgid "cancel: Unknown option '%c'!\n"
+-msgstr "annulation: Option '%c' inconnue !"
+-
+-#, c-format
+-msgid "cancel: Unknown destination \"%s\"!\n"
+-msgstr "annulation: Destination inconnue « %s » !"
+-
+-msgid "cancel: Unable to contact server!\n"
+-msgstr "annulation: Impossible de contacter le serveur !\n"
+-
+-#, c-format
+-msgid "cancel: %s failed: %s\n"
+-msgstr "annulation: %s a échoué :%s\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Missing value on line %d!\n"
+-msgstr "cupsaddsmb: Il manque une valeur à la ligne n°%d !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Missing double quote on line %d!\n"
+-msgstr "cupsaddsmb: Il manque un \" à la ligne n°%d !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Bad option + choice on line %d!\n"
+-msgstr "cupsaddsmb: Option et choix erronés à la ligne n°%d !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to connect to server \"%s\" for %s - %s\n"
+-msgstr "cupsaddsmb: Impossible de se connecter au serveur « %s » pour %s - %s\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: No PPD file for printer \"%s\" - skipping!\n"
+-msgstr "cupsaddsmb: Aucun fichier PPD pour l'imprimante « %s » - ignorée !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: get-printer-attributes failed for \"%s\": %s\n"
+-msgstr "cupsaddsmb: get-printer-attributes a échoué pour « %s » : %s\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to convert PPD file for %s - %s\n"
+-msgstr "cupsaddsmb: Impossible de convertir le fichier PPD pour %s - %s\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to copy Windows 2000 printer driver files (%d)!\n"
+-msgstr "cupsaddsmb: Impossible de copier les fichiers du pilote d'impression pour Windows 2000 (%d) !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to copy CUPS printer driver files (%d)!\n"
+-msgstr "cupsaddsmb: Impossible de copier les fichiers du pilote d'impression pour CUPS (%d) !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to install Windows 2000 printer driver files (%d)!\n"
+-msgstr "cupsaddsmb: Impossible d'installer les fichiers du pilote d'impression pour Windows 2000 (%d) !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to copy Windows 9x printer driver files (%d)!\n"
+-msgstr "cupsaddsmb: Impossible de copier les fichiers du pilote d'impression pour Windows 9x (%d) !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to install Windows 9x printer driver files (%d)!\n"
+-msgstr "cupsaddsmb: Impossible d'installer les fichiers du pilote d'impression pour Windows 9x (%d) !\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to set Windows printer driver (%d)!\n"
+-msgstr "cupsaddsmb: Impossible de paramétrer le pilote d'impression pour Windows (%d) !\n"
+-
+-msgid ""
+-"Usage: cupsaddsmb [options] printer1 ... printerN\n"
+-" cupsaddsmb [options] -a\n"
+-"\n"
+-"Options:\n"
+-" -H samba-server Use the named SAMBA server\n"
+-" -U samba-user Authenticate using the named SAMBA user\n"
+-" -a Export all printers\n"
+-" -h cups-server Use the named CUPS server\n"
+-" -v Be verbose (show commands)\n"
+-msgstr ""
+-"Utilisation : cupsaddsmb [options] imprimante1 ... imprimanteN\n"
+-" cupsaddsmb [options] -a\n"
+-"\n"
+-"Options:\n"
+-" -H serveur-samba Utiliser le serveur SAMBA désigné\n"
+-" -U utilisateur-samba S'authentifier avec l'utilisateur SAMBA désigné\n"
+-" -a Exporter toutes les imprimantes\n"
+-" -h serveur-CUPS Utiliser le serveur CUPS désigné\n"
+-" -v Être locace ( afficher les commandes )\n"
+-
+-msgid "cupstestppd: The -q option is incompatible with the -v option.\n"
+-msgstr "cupstestppd: L'option est -q incomptaible avec l'option -v.\n"
+-
+-msgid "cupstestppd: The -v option is incompatible with the -q option.\n"
+-msgstr "cupstestppd: L'option est -v incomptaible avec l'option -q.\n"
+-
+-#, c-format
+-msgid ""
+-" FAIL\n"
+-" **FAIL** Unable to open PPD file - %s\n"
+-msgstr ""
+-" FAIL\n"
+-" **FAIL** Unable to open PPD file - %s\n"
+-
+-#, c-format
+-msgid ""
+-" FAIL\n"
+-" **FAIL** Unable to open PPD file - %s on line %d.\n"
+-msgstr ""
+-" FAIL\n"
+-" **FAIL** Unable to open PPD file - %s on line %d.\n"
+-
+-msgid " REF: Page 42, section 5.2.\n"
+-msgstr " REF: Page 42, section 5.2.\n"
+-
+-msgid " REF: Page 20, section 3.4.\n"
+-msgstr " REF: Page 20, section 3.4.\n"
+-
+-msgid " REF: Pages 45-46, section 5.2.\n"
+-msgstr " REF: Pages 45-46, section 5.2.\n"
+-
+-msgid " REF: Pages 42-45, section 5.2.\n"
+-msgstr " REF: Pages 42-45, section 5.2.\n"
+-
+-msgid " REF: Pages 48-49, section 5.2.\n"
+-msgstr " REF: Pages 48-49, section 5.2.\n"
+-
+-msgid " REF: Pages 52-54, section 5.2.\n"
+-msgstr " REF: Pages 52-54, section 5.2.\n"
+-
+-msgid " REF: Page 15, section 3.2.\n"
+-msgstr " REF: Page 15, section 3.2.\n"
+-
+-msgid " REF: Page 15, section 3.1.\n"
+-msgstr " REF: Page 15, section 3.1.\n"
+-
+-msgid " REF: Pages 16-17, section 3.2.\n"
+-msgstr " REF: Pages 16-17, section 3.2.\n"
+-
+-msgid " REF: Page 19, section 3.3.\n"
+-msgstr " REF: Page 19, section 3.3.\n"
+-
+-msgid " REF: Page 27, section 3.5.\n"
+-msgstr " REF: Page 27, section 3.5.\n"
+-
+-msgid ""
+-"\n"
+-" DETAILED CONFORMANCE TEST RESULTS\n"
+-msgstr ""
+-"\n"
+-" DETAILED CONFORMANCE TEST RESULTS\n"
+-
+-#, c-format
+-msgid " WARN %s has no corresponding options!\n"
+-msgstr " WARN %s has no corresponding options!\n"
+-
+-msgid " FAIL\n"
+-msgstr " FAIL\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED DefaultImageableArea\n"
+-" REF: Page 102, section 5.15.\n"
+-msgstr ""
+-" **FAIL** REQUIRED DefaultImageableArea\n"
+-" REF: Page 102, section 5.15.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** BAD DefaultImageableArea %s!\n"
+-" REF: Page 102, section 5.15.\n"
+-msgstr ""
+-" **FAIL** BAD DefaultImageableArea %s!\n"
+-" REF: Page 102, section 5.15.\n"
+-
+-msgid " PASS DefaultImageableArea\n"
+-msgstr " PASS DefaultImageableArea\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED DefaultPaperDimension\n"
+-" REF: Page 103, section 5.15.\n"
+-msgstr ""
+-" **FAIL** REQUIRED DefaultPaperDimension\n"
+-" REF: Page 103, section 5.15.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** BAD DefaultPaperDimension %s!\n"
+-" REF: Page 103, section 5.15.\n"
+-msgstr ""
+-" **FAIL** BAD DefaultPaperDimension %s!\n"
+-" REF: Page 103, section 5.15.\n"
+-
+-msgid " PASS DefaultPaperDimension\n"
+-msgstr " PASS DefaultPaperDimension\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** BAD Default%s %s\n"
+-" REF: Page 40, section 4.5.\n"
+-msgstr ""
+-" **FAIL** BAD Default%s %s\n"
+-" REF: Page 40, section 4.5.\n"
+-
+-#, c-format
+-msgid " PASS Default%s\n"
+-msgstr " PASS Default%s\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** REQUIRED Default%s\n"
+-" REF: Page 40, section 4.5.\n"
+-msgstr ""
+-" **FAIL** REQUIRED Default%s\n"
+-" REF: Page 40, section 4.5.\n"
+-
+-msgid " PASS FileVersion\n"
+-msgstr " PASS FileVersion\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED FileVersion\n"
+-" REF: Page 56, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED FileVersion\n"
+-" REF: Page 56, section 5.3.\n"
+-
+-msgid " PASS FormatVersion\n"
+-msgstr " PASS FormatVersion\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED FormatVersion\n"
+-" REF: Page 56, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED FormatVersion\n"
+-" REF: Page 56, section 5.3.\n"
+-
+-msgid " PASS LanguageEncoding\n"
+-msgstr " PASS LanguageEncoding\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED LanguageEncoding\n"
+-" REF: Pages 56-57, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED LanguageEncoding\n"
+-" REF: Pages 56-57, section 5.3.\n"
+-
+-msgid " PASS LanguageVersion\n"
+-msgstr " PASS LanguageVersion\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED LanguageVersion\n"
+-" REF: Pages 57-58, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED LanguageVersion\n"
+-" REF: Pages 57-58, section 5.3.\n"
+-
+-msgid ""
+-" **FAIL** BAD Manufacturer (should be \"HP\")\n"
+-" REF: Page 211, table D.1.\n"
+-msgstr ""
+-" **FAIL** BAD Manufacturer (should be \"HP\")\n"
+-" REF: Page 211, table D.1.\n"
+-
+-msgid " PASS Manufacturer\n"
+-msgstr " PASS Manufacturer\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED Manufacturer\n"
+-" REF: Pages 58-59, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED Manufacturer\n"
+-" REF: Pages 58-59, section 5.3.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** BAD ModelName - \"%c\" not allowed in string.\n"
+-" REF: Pages 59-60, section 5.3.\n"
+-msgstr ""
+-" **FAIL** BAD ModelName - \"%c\" not allowed in string.\n"
+-" REF: Pages 59-60, section 5.3.\n"
+-
+-msgid " PASS ModelName\n"
+-msgstr " PASS ModelName\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED ModelName\n"
+-" REF: Pages 59-60, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED ModelName\n"
+-" REF: Pages 59-60, section 5.3.\n"
+-
+-msgid " PASS NickName\n"
+-msgstr " PASS NickName\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED NickName\n"
+-" REF: Page 60, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED NickName\n"
+-" REF: Page 60, section 5.3.\n"
+-
+-msgid " PASS PageSize\n"
+-msgstr " PASS PageSize\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED PageSize\n"
+-" REF: Pages 99-100, section 5.14.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PageSize\n"
+-" REF: Pages 99-100, section 5.14.\n"
+-
+-msgid " PASS PageRegion\n"
+-msgstr " PASS PageRegion\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED PageRegion\n"
+-" REF: Page 100, section 5.14.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PageRegion\n"
+-" REF: Page 100, section 5.14.\n"
+-
+-msgid " PASS PCFileName\n"
+-msgstr " PASS PCFileName\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED PCFileName\n"
+-" REF: Pages 61-62, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PCFileName\n"
+-" REF: Pages 61-62, section 5.3.\n"
+-
+-msgid ""
+-" **FAIL** BAD Product - not \"(string)\".\n"
+-" REF: Page 62, section 5.3.\n"
+-msgstr ""
+-" **FAIL** BAD Product - not \"(string)\".\n"
+-" REF: Page 62, section 5.3.\n"
+-
+-msgid " PASS Product\n"
+-msgstr " PASS Product\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED Product\n"
+-" REF: Page 62, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED Product\n"
+-" REF: Page 62, section 5.3.\n"
+-
+-msgid ""
+-" **FAIL** BAD PSVersion - not \"(string) int\".\n"
+-" REF: Pages 62-64, section 5.3.\n"
+-msgstr ""
+-" **FAIL** BAD PSVersion - not \"(string) int\".\n"
+-" REF: Pages 62-64, section 5.3.\n"
+-
+-msgid " PASS PSVersion\n"
+-msgstr " PASS PSVersion\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED PSVersion\n"
+-" REF: Pages 62-64, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PSVersion\n"
+-" REF: Pages 62-64, section 5.3.\n"
+-
+-msgid ""
+-" **FAIL** BAD ShortNickName - longer than 31 chars.\n"
+-" REF: Pages 64-65, section 5.3.\n"
+-msgstr ""
+-" **FAIL** BAD ShortNickName - longer than 31 chars.\n"
+-" REF: Pages 64-65, section 5.3.\n"
+-
+-msgid " PASS ShortNickName\n"
+-msgstr " PASS ShortNickName\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED ShortNickName\n"
+-" REF: Page 64-65, section 5.3.\n"
+-msgstr ""
+-" **FAIL** REQUIRED ShortNickName\n"
+-" REF: Page 64-65, section 5.3.\n"
+-
+-msgid ""
+-" **FAIL** BAD JobPatchFile attribute in file\n"
+-" REF: Page 24, section 3.4.\n"
+-msgstr ""
+-" **FAIL** BAD JobPatchFile attribute in file\n"
+-" REF: Page 24, section 3.4.\n"
+-
+-msgid ""
+-" **FAIL** REQUIRED PageSize\n"
+-" REF: Page 41, section 5.\n"
+-" REF: Page 99, section 5.14.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PageSize\n"
+-" REF: Page 41, section 5.\n"\n"
+-" REF: Page 99, section 5.14.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** REQUIRED ImageableArea for PageSize %s\n"
+-" REF: Page 41, section 5.\n"
+-" REF: Page 102, section 5.15.\n"
+-msgstr ""
+-" **FAIL** REQUIRED ImageableArea for PageSize %s\n"
+-" REF: Page 41, section 5.\n"
+-" REF: Page 102, section 5.15.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** REQUIRED PaperDimension for PageSize %s\n"
+-" REF: Page 41, section 5.\n"
+-" REF: Page 103, section 5.15.\n"
+-msgstr ""
+-" **FAIL** REQUIRED PaperDimension for PageSize %s\n"
+-" REF: Page 41, section 5.\n"
+-" REF: Page 103, section 5.15.\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** Bad %s choice %s!\n"
+-" REF: Page 84, section 5.9\n"
+-msgstr ""
+-" **FAIL** Bad %s choice %s!\n"
+-" REF: Page 84, section 5.9\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** REQUIRED %s does not define choice None!\n"
+-" REF: Page 122, section 5.17\n"
+-msgstr ""
+-" **FAIL** REQUIRED %s does not define choice None!\n"
+-" REF: Page 122, section 5.17\n"
+-
+-#, c-format
+-msgid ""
+-" **FAIL** Bad %s choice %s!\n"
+-" REF: Page 122, section 5.17\n"
+-msgstr ""
+-" **FAIL** Bad %s choice %s!\n"
+-" REF: Page 122, section 5.17\n"
+-
+-msgid " PASS\n"
+-msgstr " PASS\n"
+-
+-#, c-format
+-msgid ""
+-" WARN Duplex option keyword %s should be named Duplex or JCLDuplex!\n"
+-" REF: Page 122, section 5.17\n"
+-msgstr ""
+-" WARN Duplex option keyword %s should be named Duplex or JCLDuplex!\n"
+-" REF: Page 122, section 5.17\n"
+-
+-msgid " WARN Default choices conflicting!\n"
+-msgstr ""
+-
+-#, c-format
+-msgid ""
+-" WARN Obsolete PPD version %.1f!\n"
+-" REF: Page 42, section 5.2.\n"
+-msgstr ""
+-" WARN Obsolete PPD version %.1f!\n"
+-" REF: Page 42, section 5.2.\n"
+-
+-msgid ""
+-" WARN LanguageEncoding required by PPD 4.3 spec.\n"
+-" REF: Pages 56-57, section 5.3.\n"
+-msgstr ""
+-" WARN LanguageEncoding required by PPD 4.3 spec.\n"
+-" REF: Pages 56-57, section 5.3.\n"
+-
+-msgid ""
+-" WARN Manufacturer required by PPD 4.3 spec.\n"
+-" REF: Pages 58-59, section 5.3.\n"
+-msgstr ""
+-" WARN Manufacturer required by PPD 4.3 spec.\n"
+-" REF: Pages 58-59, section 5.3.\n"
+-
+-msgid ""
+-" WARN PCFileName longer than 8.3 in violation of PPD spec.\n"
+-" REF: Pages 61-62, section 5.3.\n"
+-msgstr ""
+-" WARN PCFileName longer than 8.3 in violation of PPD spec.\n"
+-" REF: Pages 61-62, section 5.3.\n"
+-
+-msgid ""
+-" WARN ShortNickName required by PPD 4.3 spec.\n"
+-" REF: Pages 64-65, section 5.3.\n"
+-msgstr ""
+-" WARN ShortNickName required by PPD 4.3 spec.\n"
+-" REF: Pages 64-65, section 5.3.\n"
+-
+-msgid ""
+-" WARN Protocols contains both PJL and BCP; expected TBCP.\n"
+-" REF: Pages 78-79, section 5.7.\n"
+-msgstr ""
+-" WARN Protocols contains both PJL and BCP; expected TBCP.\n"
+-" REF: Pages 78-79, section 5.7.\n"
+-
+-msgid ""
+-" WARN Protocols contains PJL but JCL attributes are not set.\n"
+-" REF: Pages 78-79, section 5.7.\n"
+-msgstr ""
+-" WARN Protocols contains PJL but JCL attributes are not set.\n"
+-" REF: Pages 78-79, section 5.7.\n"
+-
+-#, c-format
+-msgid ""
+-" WARN %s shares a common prefix with %s\n"
+-" REF: Page 15, section 3.2.\n"
+-msgstr ""
+-" WARN %s shares a common prefix with %s\n"
+-" REF: Page 15, section 3.2.\n"
+-
+-#, c-format
+-msgid " %d ERROR%s FOUND\n"
+-msgstr " %d ERROR%s FOUND\n"
+-
+-msgid " NO ERRORS FOUND\n"
+-msgstr " NO ERRORS FOUND\n"
+-
+-#, c-format
+-msgid ""
+-" WARN \"%s %s\" conflicts with \"%s %s\"\n"
+-" (constraint=\"%s %s %s %s\")\n"
+-msgstr ""
+-" WARN \"%s %s\" conflicts with \"%s %s\"\n"
+-" (constraint=\"%s %s %s %s\")\n"
+-msgid ""
+-"Usage: cupstestppd [-q] [-r] [-v[v]] filename1.ppd[.gz] [... filenameN.ppd[.gz]]\n"
+-" program | cupstestppd [-q] [-r] [-v[v]] -\n"
+-msgstr ""
+-"Utilisation : cupstestppd [-q] [-r] [-v[v]] fichier1.ppd[.gz] [... fichierN.ppd[.gz]]\n"
+-" programme | cupstestppd [-q] [-r] [-v[v]] -\n"
+-
+-msgid "lpstat: Need \"completed\" or \"not-completed\" after -W!\n"
+-msgstr "lpstat: Il faut « completed » ou « not-completed » après l'option -W !\n"
+-
+-msgid "lpstat: The -b option requires a destination argument.\n"
+-msgstr "lpstat: Il faut une destination après l'option '-b' !\n"
+-
+-msgid "Error: need hostname after '-h' option!\n"
+-msgstr "Erreur : Il faut un nom d'hôte après l'option '-h' !\n"
+-
+-#, c-format
+-msgid "lpstat: Unknown option '%c'!\n"
+-msgstr "lpstat: Option '%c inconnue !\n"
+-
+-#, c-format
+-msgid "lpstat: Invalid destination name in list \"%s\"!\n"
+-msgstr "lpstat: Nom de destination incorrect dans la liste « %s » !\n"
+-
+-#, c-format
+-msgid "lpstat: Unknown destination \"%s\"!\n"
+-msgstr "lpstat: Destination « %s » inconnue !\n"
+-
+-#, c-format
+-msgid "lpstat: Unable to connect to server %s on port %d: %s\n"
+-msgstr "lpstat: Impossible de se connecter au serveur « %s » sur le port %d : %s\n"
+-
+-#, c-format
+-msgid "lpstat: get-printers failed: %s\n"
+-msgstr "lpstat: get-printers a échoué: %s\n"
+-
+-#, c-format
+-msgid "%s accepting requests since Jan 01 00:00\n"
+-msgstr "%s accepte les requêtes depuis le Jan 01 00:00\n"
+-
+-#, c-format
+-msgid ""
+-"%s not accepting requests since Jan 01 00:00 -\n"
+-"\t%s\n"
+-msgstr ""
+-"%s n'accepte pas les requêtes depuis le Jan 01 00:00 -\n"
+-"\t%s\n"
+-
+-#, c-format
+-msgid "%s/%s accepting requests since Jan 01 00:00\n"
+-msgstr "%s/%s accepte les requêtes depuis le Jan 01 00:00\n"
+-
+-#, c-format
+-msgid ""
+-"%s/%s not accepting requests since Jan 01 00:00 -\n"
+-"\t%s\n"
+-msgstr ""
+-"%s/%s n'accepte pas les requêtes depuis le Jan 01 00:00 -\n"
+-"\t%s\n"
+-
+-#, c-format
+-msgid "lpstat: get-classes failed: %s\n"
+-msgstr "lpstat: get-classes a échoué : %s !\n"
+-
+-#, c-format
+-msgid "members of class %s:\n"
+-msgstr "membres de la classe %s :\n"
+-
+-#, c-format
+-msgid "system default destination: %s/%s\n"
+-msgstr "destination par défaut du système : %s/%s\n"
+-
+-#, c-format
+-msgid "system default destination: %s\n"
+-msgstr "destination par défaut du système : %s\n"
+-
+-#, c-format
+-msgid "lpstat: error - %s environment variable names non-existent destination \"%s\"!\n"
+-msgstr ""
+-"lpstat: erreur - la variable d'environnement désigne une destination\n"
+-" inexistente « %s » !\n"
+-
+-msgid "no system default destination\n"
+-msgstr "le système n'a pas de destination par défaut\n"
+-
+-#, c-format
+-msgid "Output for printer %s is sent to remote printer %s on %s\n"
+-msgstr ""
+-"Ce qui est envoyé à l'imprimante %s est redirigé vers l'imprimante\n"
+-"distante %s sur %s\n"
+-
+-#, c-format
+-msgid "Output for printer %s is sent to %s\n"
+-msgstr "Ce qui est envoyé à l'imprimante %s est redirigé vers %s\n"
+-
+-#, c-format
+-msgid "Output for printer %s/%s is sent to remote printer %s on %s\n"
+-msgstr ""
+-"Ce qui est envoyé à l'imprimante %s/%s est redirigé vers l'imprimante\n"
+-"distante %s sur %s\n"
+-
+-#, c-format
+-msgid "Output for printer %s/%s is sent to %s\n"
+-msgstr "Ce qui est envoyé à l'imprimante %s/%s est redirigé vers %s\n"
+-
+-#, c-format
+-msgid "device for %s: %s\n"
+-msgstr "périphérique pour %s : %s\n"
+-
+-#, c-format
+-msgid "device for %s/%s: %s\n"
+-msgstr "périphérique pour %s/%s : %s \n"
+-
+-#, c-format
+-msgid "lpstat: get-jobs failed: %s\n"
+-msgstr "lpstat: get-jobs a échoué : %s !\n"
+-
+-#, c-format
+-msgid "\tqueued for %s\n"
+-msgstr "\tmis en file d'attente pour %s\n"
+-
+-#, c-format
+-msgid "printer %s is idle. enabled since %s\n"
+-msgstr "l'imprimante %s ne fait rien ; elle est activée depuis le %s\n"
+-
+-#, c-format
+-msgid "printer %s now printing %s-%d. enabled since %s\n"
+-msgstr "l'imprimante %s est en train d'imprimer %s-%d ; elle est activée depuis le %s\n""
+-
+-#, c-format
+-msgid "printer %s disabled since %s -\n"
+-msgstr "l'imprimante %s est désactivée depuis le %s -\n"
+-
+-msgid "\treason unknown\n"
+-msgstr "\traison inconnue\n"
+-
+-msgid ""
+-"\tForm mounted:\n"
+-"\tContent types: any\n"
+-"\tPrinter types: unknown\n"
+-msgstr ""
+-"\tFormat monté :\n"
+-"\tTypes de contenu : any\n"
+-"\tTypes d'imprimante : unknown\n"
+-
+-#, c-format
+-msgid "\tDescription: %s\n"
+-msgstr "\tDescription : %s\n"
+-
+-msgid "\tAlerts:"
+-msgstr "\tAlertes :"
+-
+-#, c-format
+-msgid "\tLocation: %s\n"
+-msgstr "\tLieu : %s\n"
+-
+-msgid "\tConnection: remote\n"
+-msgstr "\tConnexion : distante\n"
+-
+-#, c-format
+-msgid "\tInterface: %s.ppd\n"
+-msgstr "\tInterface : %s.ppd\n"
+-
+-msgid "\tConnection: direct\n"
+-msgstr "\tConnexion : directe\n"
+-
+-#, c-format
+-msgid "\tInterface: %s/interfaces/%s\n"
+-msgstr "\tInterface : %s/interfaces/%s\n"
+-
+-#, c-format
+-msgid "\tInterface: %s/ppd/%s.ppd\n"
+-msgstr "\tInterface : %s/ppd/%s.ppd\n"
+-
+-msgid "\tOn fault: no alert\n"
+-msgstr "\tEn cas d'erreur : pas d'alerte\n"
+-
+-msgid "\tAfter fault: continue\n"
+-msgstr "\tAprès une erreur : poursuivre\n"
+-
+-msgid "\tUsers allowed:\n"
+-msgstr "\tUtilisateurs autorisés :\n"
+-
+-msgid "\tUsers denied:\n"
+-msgstr "\tUtilisateurs refusés :\n"
+-
+-msgid "\t\t(all)\n"
+-msgstr "\t\t( tous )\n"
+-
+-msgid "\tForms allowed:\n"
+-msgstr "\tFormats autorisés :\n"
+-
+-msgid "\t\t(none)\n"
+-msgstr "\t\t( aucun )\n"
+-
+-msgid "\tBanner required\n"
+-msgstr "\tBannière requise\n"
+-
+-msgid "\tCharset sets:\n"
+-msgstr "\tJeux de caractères :\n"
+-
+-msgid "\tDefault pitch:\n"
+-msgstr "\t« pitch » par défaut :\n"
+-
+-msgid "\tDefault page size:\n"
+-msgstr "\tTaille de papier par défaut :\n"
+-
+-msgid "\tDefault port settings:\n"
+-msgstr "\tParamètres par défaut du port :\n"
+-
+-#, c-format
+-msgid "printer %s/%s is idle. enabled since %s\n"
+-msgstr "l'imprimante %s/%s ne fait rien ; elle est activée depuis le %s\n"
+-
+-#, c-format
+-msgid "printer %s/%s now printing %s-%d. enabled since %s\n"
+-msgstr "l'imprimante %s/%s est en train d'imprimer %s-%d ; elle est activée depuis le %s\n""
+-
+-#, c-format
+-msgid "printer %s/%s disabled since %s -\n"
+-msgstr "l'imprimante %s/%s est désactivée depuis le %s -\n"
+-
+-msgid "scheduler is running\n"
+-msgstr "l'ordonnanceur tourne\n"
+-
+-msgid "scheduler is not running\n"
+-msgstr "l'ordonnanceur est arrêté\n"
+-
+-#, c-format
+-msgid "lpadmin: Unable to connect to server: %s\n"
+-msgstr "lpadmin: Impossible de se connecter au serveur « %s » !\n"
+-
+-msgid ""
+-"lpadmin: Unable to add a printer to the class:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible d'ajouter une imprimante à la classe :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected class name after '-c' option!\n"
+-msgstr "lpadmin: Il faut un nom de classe après l'option '-c' !\n"
+-
+-msgid "lpadmin: Class name can only contain printable characters!\n"
+-msgstr "lpadmin: Un nom de classe ne peut comporter que des caractères imprimables !\n"
+-
+-msgid "lpadmin: Expected printer name after '-d' option!\n"
+-msgstr "lpadmin: Il faut un nom d'imprimante après l'option '-d' !\n"
+-
+-msgid "lpadmin: Printer name can only contain printable characters!\n"
+-msgstr "lpadmin: Un nom d'imprimante ne peut comporter que des caractères imprimables !\n"
+-
+-msgid "lpadmin: Expected hostname after '-h' option!\n"
+-msgstr "lpadmin: Il faut un nom d'hôte après l'option 'h' !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the interface script:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir le script d'interface :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected interface after '-i' option!\n"
+-msgstr "lpadmin: Il faut une interface après l'option '-i' !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the interface script or PPD file:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir le script d'interface ou le fichier PPD :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected model after '-m' option!\n"
+-msgstr "lpadmin: Il faut un modèle après l'option '-m' !\n"
+-
+-msgid "lpadmin: Expected name=value after '-o' option!\n"
+-msgstr "lpadmin: Il faut un argument du type option=valeur après l'option '-o' !\n"
+-
+-msgid "lpadmin: Expected printer after '-p' option!\n"
+-msgstr "lpadmin: Il faut une imprimante après l'option '-p' !\n"
+-
+-msgid ""
+-"lpadmin: Unable to remove a printer from the class:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de supprimer une imprimante de la classe :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected class after '-r' option!\n"
+-msgstr "lpadmin: Il faut une classe après l'option '-r' !\n"
+-
+-msgid "lpadmin: Expected allow/deny:userlist after '-u' option!\n"
+-msgstr ""
+-"lpadmin: Il faut un argument du type allow/deny:liste_utilisateurs\n"
+-" après l'option '-u' !\n"
+-
+-#, c-format
+-msgid "lpadmin: Unknown allow/deny option \"%s\"!\n"
+-msgstr "lpadmin: Option allow/deny « %s » inconnue !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the device URI:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir l'URI du périphérique :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected device URI after '-v' option!\n"
+-msgstr "lpadmin: Il faut un URI de périphérique après l'option '-v' !\n"
+-
+-msgid "lpadmin: Expected printer or class after '-x' option!\n"
+-msgstr "lpadmin: Il faut une imprimante ou une classe après l'option '-x' !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the printer description:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir la description de l'imprimante :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected description after '-D' option!\n"
+-msgstr "lpadmin: Il faut une description après l'option '-D' !\n"
+-
+-msgid "lpadmin: Expected file type(s) after '-I' option!\n"
+-msgstr "lpadmin: Il faut un ou plusieurs types de fichier après l'option '-I' !\n"
+-
+-msgid "lpadmin: Warning - content type list ignored!\n"
+-msgstr "lpadmin: Attention - les types de contenu sont ignorés !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the printer location:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir le lieu de l'imprimante :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected location after '-L' option!\n"
+-msgstr "lpadmin: Il faut un lieu après l'option '-L' !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the PPD file:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir le fichier PPD :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid "lpadmin: Expected PPD after '-P' option!\n"
+-msgstr "lpadmin: Il faut un fichier PPD après l'option '-P' !\n"
+-
+-#, c-format
+-msgid "lpadmin: Unknown option '%c'!\n"
+-msgstr "lpadmin: Option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "lpadmin: Unknown argument '%s'!\n"
+-msgstr "lpadmin: Argument « %s » inconnu !\n"
+-
+-msgid ""
+-"lpadmin: Unable to set the printer options:\n"
+-" You must specify a printer name first!\n"
+-msgstr ""
+-"lpadmin: Impossible de définir les options de l'imprimante :\n"
+-" Vous devez d'abord préciser un nom d'imprimante !\n"
+-
+-msgid ""
+-"Usage:\n"
+-"\n"
+-" lpadmin [-h server] -d destination\n"
+-" lpadmin [-h server] -x destination\n"
+-" lpadmin [-h server] -p printer [-c add-class] [-i interface] [-m model]\n"
+-" [-r remove-class] [-v device] [-D description]\n"
+-" [-P ppd-file] [-o name=value]\n"
+-" [-u allow:user,user] [-u deny:user,user]\n"
+-"\n"
+-msgstr ""
+-"Utilisation :\n"
+-"\n"
+-" lpadmin [-h serveur] -d destination\n"
+-" lpadmin [-h serveur] -x destination\n"
+-" lpadmin [-h serveur] -p imprimante [-c classe] [-i interface] [-m modèle]\n"
+-" [-r classe] [-v URI] [-D description]\n"
+-" [-P fichier-ppd] [-o option=valeur]\n"
+-" [-u allow:util.,util.] [-u deny:util.,util.]\n"
+-"\n"
+-
+-#, c-format
+-msgid "lpadmin: Unable to create temporary file: %s\n"
+-msgstr "lpadmin: Impossible de créer un fichier temporaire : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: Unable to open file \"%s\": %s\n"
+-msgstr "lpadmin: Impossible d'ouvrir le fichier « %s » : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: add-printer (set model) failed: %s\n"
+-msgstr "lpadmin: add-printer ( définition du modèle ) a échoué : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: add-printer (set description) failed: %s\n"
+-msgstr "lpadmin: add-printer ( définition de la description ) a échoué : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: add-printer (set location) failed: %s\n"
+-msgstr "lpadmin: add-printer ( définition du lieu ) a échoué : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: Unable to create temporary file - %s\n"
+-msgstr "lpadmin: Impossible de créer un fichier temporaire : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: Unable to open PPD file \"%s\" - %s\n"
+-msgstr "lpadmin: Impossible d'ouvrir le fichier PPD « %s » : %s\n"
+-
+-#, c-format
+-msgid "lpadmin: %s failed: %s\n"
+-msgstr "lpadmin: %s a échoué : %s\n"
+-
+-msgid "lp: Expected destination after -d option!\n"
+-msgstr "lp: Il faut une destination après l'option '-d' !\n"
+-
+-msgid "lp: Expected form after -f option!\n"
+-msgstr "lp: Il faut un format après l'option '-f' !\n"
+-
+-msgid "lp: Expected hostname after -h option!\n"
+-msgstr "lp: Il faut un nom d'hôte après l'option '-h' !\n"
+-
+-msgid "lp: Expected job ID after -i option!\n"
+-msgstr "lp: Il faut un numéro de tâche après l'option '-i' !\n"
+-
+-msgid "lp: Error - cannot print files and alter jobs simultaneously!\n"
+-msgstr "lp: Erreur - impossible d'imprimer et modifier les tâches en même temps !\n"
+-
+-msgid "lp: Error - bad job ID!\n"
+-msgstr "lp: Erreur - numéro de tâche incorrect !\n"
+-
+-msgid "lp: Expected copies after -n option!\n"
+-msgstr "lp: Il faut un nombre de copies après l'option '-n' !\n"
+-
+-msgid "lp: Expected option string after -o option!\n"
+-msgstr "lp: Il faut une chaîne d'option \"nom=valeur\" après l'option '-o' !\n"
+-
+-#, c-format
+-msgid "lp: Expected priority after -%c option!\n"
+-msgstr "lp: Il faut une priorité après l'option '-%c' !\n"
+-
+-msgid "lp: Priority must be between 1 and 100.\n"
+-msgstr "lp: La priorité doit être comprise entre 1 et 100.\n"
+-
+-msgid "lp: Expected title after -t option!\n"
+-msgstr "lp: Il faut un titre après l'option '-t' !\n"
+-
+-msgid "lp: Expected mode list after -y option!\n"
+-msgstr "lp: Il faut une liste de modes après l'option '-y' !\n"
+-
+-msgid "lp: Warning - mode option ignored!\n"
+-msgstr "lp: Attention - l'option « mode » est ignorée !\n"
+-
+-msgid "lp: Expected hold name after -H option!\n"
+-msgstr "lp: Il faut un code de retenue après l'option '-H' !\n"
+-
+-msgid "lp: Need job ID (-i) before \"-H restart\"!\n"
+-msgstr "lp: Il faut un numéro de tâche ( '-i' ) avant '-H restart' !\n"
+-
+-msgid "lp: Expected page list after -P option!\n"
+-msgstr "lp: Il faut une liste de pages après l'option '-P' !\n"
+-
+-msgid "lp: Expected character set after -S option!\n"
+-msgstr "lp: Il faut un jeu de caractères après l'option '-S' !\n"
+-
+-msgid "lp: Warning - character set option ignored!\n"
+-msgstr "lp: Attention - l'option « jeu de caractères » est ignorée !\n"
+-
+-msgid "lp: Expected content type after -T option!\n"
+-msgstr "lp: Il faut un type de contenu après l'option '-T' !\n"
+-
+-msgid "lp: Warning - content type option ignored!\n"
+-msgstr "lp: Attention - l'option « type de contenu » est ignorée !\n"
+-
+-#, c-format
+-msgid "lp: Unknown option '%c'!\n"
+-msgstr "lp: Option '%c' inconnue !\n"
+-
+-msgid "lp: Error - cannot print from stdin if files or a job ID are provided!\n"
+-msgstr ""
+-"lp: Erreur - impossible d'imprimer depuis l'entrée standard si un fichier\n"
+-" ou un numéro de tâche est spécifié !\n"
+-
+-#, c-format
+-msgid "lp: Unable to access \"%s\" - %s\n"
+-msgstr "lp: Impossible d'accéder à « %s » - %s\n"
+-
+-#, c-format
+-msgid "lp: Too many files - \"%s\"\n"
+-msgstr "lp: Trop de fichiers - « %s »\n"
+-
+-msgid "lp: error - no default destination available.\n"
+-msgstr "lp: Erreur - aucune destination par défaut n'est disponible.\n"
+-
+-msgid "lp: error - scheduler not responding!\n"
+-msgstr "lp: Erreur - l'odonnanceur ne répond pas !\n"
+-
+-#, c-format
+-msgid "lp: Error - unable to create temporary file \"%s\" - %s\n"
+-msgstr "lp: Erreur - impossible de créer le fichier temporaire « %s » - %s\n"
+-
+-#, c-format
+-msgid "lp: error - unable to write to temporary file \"%s\" - %s\n"
+-msgstr "lp: Erreur - impossible d'écrire dans le fichier temporaire « %s » - %s\n"
+-
+-msgid "lp: stdin is empty, so no job has been sent.\n"
+-msgstr "lp: l'entrée standard est vide, donc aucune tâche n'a été envoyée.\n"
+-
+-#, c-format
+-msgid "lp: unable to print file: %s\n"
+-msgstr "lp: impossible d'imprimer le fichier - %s\n"
+-
+-#, c-format
+-msgid "request id is %s-%d (%d file(s))\n"
+-msgstr "l'identifiant de requête est %s-%d ( %d fichier(s) )\n"
+-
+-#, c-format
+-msgid "lp: restart-job failed: %s\n"
+-msgstr "lp: restart-job a échoué : %s\n"
+-
+-#, c-format
+-msgid "lp: set-job-attributes failed: %s\n"
+-msgstr "lp: set-job-attributes a échoué : %s\n"
+-
+-#, c-format
+-msgid "lpinfo: Unable to connect to server: %s\n"
+-msgstr "lpinfo: Impossible de se connecter au serveur : %s\n"
+-
+-#, c-format
+-msgid "lpinfo: Unknown option '%c'!\n"
+-msgstr "lpinfo: Option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "lpinfo: Unknown argument '%s'!\n"
+-msgstr "lpinfo: Argument '%s' inconnu !\n"
+-
+-#, c-format
+-msgid "lpinfo: cups-get-devices failed: %s\n"
+-msgstr "lpinfo: cups-get-devices a échoué : %s\n"
+-
+-#, c-format
+-msgid ""
+-"Device: uri = %s\n"
+-" class = %s\n"
+-" info = %s\n"
+-" make-and-model = %s\n"
+-msgstr ""
+-"Matériel : URI = %s\n"
+-" classe = %s\n"
+-" info = %s\n"
+-" marque/modèle = %s\n"
+-
+-#, c-format
+-msgid "lpinfo: cups-get-ppds failed: %s\n"
+-msgstr "lpinfo: cups-get-ppds a échoué : %s\n"
+-
+-#, c-format
+-msgid ""
+-"Model: name = %s\n"
+-" natural_language = %s\n"
+-" make-and-model = %s\n"
+-msgstr ""
+-"Modèle : nom = %s\n"
+-" langue naturelle = %s\n"
+-" marque/modèle = %s\n"
+-
+-#, c-format
+-msgid "lpmove: Unknown option '%c'!\n"
+-msgstr "lpmove: Option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "lpmove: Unknown argument '%s'!\n"
+-msgstr "lpmove: Argument '%s' inconnu !\n"
+-
+-msgid "Usage: lpmove job dest\n"
+-msgstr "Utilisation : lpmove tâche destination\n"
+-
+-#, c-format
+-msgid "lpmove: Unable to connect to server: %s\n"
+-msgstr "lpmove: Impossible de se connecter au serveur : %s\n"
+-
+-#, c-format
+-msgid "lpmove: move-job failed: %s\n"
+-msgstr "lpmove: move-job a échoué : %s\n""
+-
+-msgid "lpoptions: Unknown printer or class!\n"
+-msgstr "lpoptions: Imprimante ou classe inconnue !\n"
+-
+-msgid "lpoptions: No printers!?!\n"
+-msgstr "lpoptions: Pas d'imprimante !?!\n"
+-
+-#, c-format
+-msgid "lpoptions: Unable to add printer or instance: %s\n"
+-msgstr "lpoptions: Impossible d'ajouter l'imprimante ou l'instance : %s\n"
+-
+-#, c-format
+-msgid "lpoptions: Destination %s has no PPD file!\n"
+-msgstr "lpoptions: La destination %s n'a pas de fichier PPD !\n"
+-
+-#, c-format
+-msgid "lpoptions: Unable to open PPD file for %s!\n"
+-msgstr "lpoptions: Impossible d'ouvrir le fichier PPD pour %s !\n"
+-
+-msgid ""
+-"Usage: lpoptions [-h server] [-E] -d printer\n"
+-" lpoptions [-h server] [-E] [-p printer] -l\n"
+-" lpoptions [-h server] [-E] -p printer -o option[=value] ...\n"
+-" lpoptions [-h server] [-E] -x printer\n"
+-msgstr ""
+-"Utilisation : lpoptions [-h serveur] [-E] -d imprimante\n"
+-" lpoptions [-h serveur] [-E] [-p imprimante] -l\n"
+-" lpoptions [-h serveur] [-E] -p imprimante -o option[=valeur] ...\n"
+-" lpoptions [-h serveur] [-E] -x imprimante\n"
+-
+-msgid "lppasswd: Only root can add or delete passwords!\n"
+-msgstr "lppasswd: Seul l'utilisateur « root » peut ajouter ou supprimer des mots de passe !\n"
+-
+-msgid "Enter old password:"
+-msgstr "Ancien mot de passe :"
+-
+-#, c-format
+-msgid "lppasswd: Unable to copy password string: %s\n"
+-msgstr "lppasswd: Impossible de copier le mot de passe : %s\n"
+-
+-msgid "Enter password:"
+-msgstr "Entrez le nouveau mot de passe :"
+-
+-msgid "Enter password again:"
+-msgstr "Confirmez le nouveau mot de passe :"
+-
+-msgid "lppasswd: Sorry, passwords don't match!\n"
+-msgstr "lppasswd: Désolé, les mots de passe sont différents !\n"
+-
+-msgid ""
+-"lppasswd: Sorry, password rejected.\n"
+-"Your password must be at least 6 characters long, cannot contain\n"
+-"your username, and must contain at least one letter and number.\n"
+-msgstr ""
+-"lppasswd: Désolé, mot de passe refusé.\n"
+-"Votre mot de passe doit comporter au moins 6 caractères, au moins une lettre\n"
+-"et un nombre, et ne peut contenir votre nom d'utilisateur.\n"
+-
+-msgid "lppasswd: Password file busy!\n"
+-msgstr "lppasswd: Le fichier des mots de passe exidte déjà !\n"
+-
+-#, c-format
+-msgid "lppasswd: Unable to open password file: %s\n"
+-msgstr "lppasswd: Impossible d'ouvrir le fichier des mots de passe: %s\n"
+-
+-#, c-format
+-msgid "lppasswd: Unable to write to password file: %s\n"
+-msgstr "lppasswd: Impossible d'écrire dans le fichier des mots de passe : %s\n"
+-
+-#, c-format
+-msgid "lppasswd: user \"%s\" and group \"%s\" do not exist.\n"
+-msgstr "lppasswd: l'utilisateur « %s » et/ou le groupe « %s » n'existe(nt) pas !\n"
+-
+-msgid "lppasswd: Sorry, password doesn't match!\n"
+-msgstr "lppasswd: Désolé, le mot de passe est erroné !\n"
+-
+-msgid "lppasswd: Password file not updated!\n"
+-msgstr "lppasswd: Le fichier des mots de passe n'a pas été mis-à-jour !\n"
+-
+-#, c-format
+-msgid "lppasswd: failed to backup old password file: %s\n"
+-msgstr "lppasswd: impossible de sauvegarder l'ancien fichier des mots de passe : %s\n"
+-
+-#, c-format
+-msgid "lppasswd: failed to rename password file: %s\n"
+-msgstr "lppasswd: impossible de renommer le fichier des mots de passe : %s\n"
+-
+-msgid "Usage: lppasswd [-g groupname]\n"
+-msgstr "Utilisation : lppasswd [-g nom_groupe]\n"
+-
+-msgid ""
+-"Usage: lppasswd [-g groupname] [username]\n"
+-" lppasswd [-g groupname] -a [username]\n"
+-" lppasswd [-g groupname] -x [username]\n"
+-msgstr ""
+-"Utilisation : lppasswd [-g nom_groupe] [nom_utilisateur]\n"
+-" lppasswd [-g nom_groupe] -a [nom_utilisateur]\n"
+-" lppasswd [-g nom_groupe] -x [nom_utilisateur]\n"
+-
+-msgid "Start Printer"
+-msgstr "Démarrer l'imprimante"
+-
+-msgid "Stop Printer"
+-msgstr "Arrêter l'imprimante"
+-
+-msgid "Start Class"
+-msgstr "Démarrer la classe"
+-
+-msgid "Stop Class"
+-msgstr "Arrêter la classe"
+-
+-msgid "Accept Jobs"
+-msgstr "Accepter les tâches"
+-
+-msgid "Reject Jobs"
+-msgstr "Refuser les tâches"
+-
+-msgid "Purge Jobs"
+-msgstr "Effacer les tâches"
+-
+-msgid "Set As Default"
+-msgstr "Choisir par défaut"
+-
+-msgid "Administration"
+-msgstr "Administration"
+-
+-msgid "Modify Class"
+-msgstr "Modifier la classe"
+-
+-msgid "Add Class"
+-msgstr "Ajouter une classe"
+-
+-msgid "The class name may only contain up to 127 printable characters and may not contain spaces, slashes (/), or the pound sign (#)."
+-msgstr "Le nom de classe doit comporter au plus 127 caractères, tous imprimables, sans espace, '/' ni '#'."
+-
+-msgid "Unable to modify class:"
+-msgstr "Impossible de modifier la classe :"
+-
+-msgid "Unable to add class:"
+-msgstr "Impossible d'ajouter la classe :"
+-
+-msgid "Modify Printer"
+-msgstr "Modifier l'imprimante"
+-
+-msgid "Add Printer"
+-msgstr "Ajouter une imprimante"
+-
+-msgid "The printer name may only contain up to 127 printable characters and may not contain spaces, slashes (/), or the pound sign (#)."
+-msgstr "Le nom d'imprimante doit comporter au plus 127 caractères, tous imprimables, sans espace, '/' ni '#'."
+-
+-msgid "Unable to get list of printer drivers:"
+-msgstr "Impossible d'obtenir la liste des pilotes pour l'imprimante:"
+-
+-msgid "Unable to modify printer:"
+-msgstr "Impossible de modifier l'imprimante :"
+-
+-msgid "Unable to add printer:"
+-msgstr "Impossible d'ajouter l'imprimante :"
+-
+-msgid "Set Printer Options"
+-msgstr "Définir les options de l'imprimante"
+-
+-msgid "Missing form variable!"
+-msgstr "Un champ du formulaire HTML n'a pas été rempli !"
+-
+-msgid "Unable to get PPD file!"
+-msgstr "Impossible de trouver le fichier PPD !"
+-
+-msgid "Unable to open PPD file:"
+-msgstr "Impossible d'ouvrir le fichier PPD :"
+-
+-msgid "Banners"
+-msgstr "Bannières"
+-
+-msgid "Starting Banner"
+-msgstr "Début de la bannière"
+-
+-msgid "Ending Banner"
+-msgstr "Fin de la bannière"
+-
+-msgid "Policies"
+-msgstr "Politiques"
+-
+-msgid "Error Policy"
+-msgstr "Politique d'erreur"
+-
+-msgid "Operation Policy"
+-msgstr "Politique des opérations"
+-
+-msgid "PS Binary Protocol"
+-msgstr "Protocole binaire PS"
+-
+-msgid "None"
+-msgstr "Auncun(e)"
+-
+-msgid "Unable to set options:"
+-msgstr "Impossible de définir les options :"
+-
+-msgid "Change Settings"
+-msgstr "Modifier les paramètres"
+-
+-msgid "Unable to change server settings:"
+-msgstr "Impossible de modifier les paramètres du serveur :"
+-
+-msgid "Unable to upload cupsd.conf file:"
+-msgstr "Impossible de transmettre le fichier cupsd.conf :"
+-
+-msgid "Edit Configuration File"
+-msgstr "Éditer le fichier de configuration"
+-
+-msgid "Unable to create temporary file:"
+-msgstr "Impossible de créer le fichier temporaire :"
+-
+-msgid "Unable to access cupsd.conf file:"
+-msgstr "Impossible d'accéder au fichier cupsd.conf :"
+-
+-msgid "Unable to edit cupsd.conf files larger than 1MB!"
+-msgstr "Impossible d'éditer des fichiers cupsd.conf de taille supérieure à 1Mo !"
+-
+-msgid "Delete Class"
+-msgstr "Supprimer la classe"
+-
+-msgid "Unable to delete class:"
+-msgstr "Impossible de supprimer la classe :"
+-
+-msgid "Delete Printer"
+-msgstr "Supprimer l'imprimante"
+-
+-msgid "Unable to delete printer:"
+-msgstr "Impossible de supprimer l'imprimante :"
+-
+-msgid "Export Printers to Samba"
+-msgstr "Exporter les imprimantes vers SAMBA"
+-
+-msgid "Unable to fork process!"
+-msgstr "Impossible de faire un « fork » !"
+-
+-msgid "Unable to connect to server!"
+-msgstr "Impossible de se connecter au serveur !"
+-
+-msgid "Unable to get printer attributes!"
+-msgstr "Impossible de récupérer les attributs de l'imprimante !"
+-
+-msgid "Unable to convert PPD file!"
+-msgstr "Impossible de convertir le fichier PPD !"
+-
+-msgid "Unable to copy Windows 2000 printer driver files!"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 2000 !"
+-
+-msgid "Unable to install Windows 2000 printer driver files!"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 2000 !"
+-
+-msgid "Unable to copy Windows 9x printer driver files!"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 9x !"
+-
+-msgid "Unable to install Windows 9x printer driver files!"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 9x !"
+-
+-msgid "Unable to set Windows printer driver!"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows !"
+-
+-msgid "No printer drivers found!"
+-msgstr "Aucun pilote trouvé pour l'imprimante !"
+-
+-msgid "Unable to execute cupsaddsmb command!"
+-msgstr "Impossible d'exécuter la commande cupsaddsmb !"
+-
+-#, c-format
+-msgid "cupsaddsmb failed with status %d"
+-msgstr "cupsaddsmb a échoué avec le statut %d"
+-
+-#, c-format
+-msgid "cupsaddsmb crashed on signal %d"
+-msgstr "cupsaddsmb s'est terminé inopinément sur réception du signal %d"
+-
+-msgid "A Samba username is required to export printer drivers!"
+-msgstr "Il faut un nom d'utilisateur SAMBA pour exporter les pilotes d'imprimante !"
+-
+-msgid "A Samba password is required to export printer drivers!"
+-msgstr "Il faut un mot de passe SAMBA pour exporter les pilotes d'imprimante !"
+-
+-msgid "Unable to open cupsd.conf file:"
+-msgstr "Impossible d'ouvrir le fichier cupsd.conf :"
+-
+-msgid "Unable to change printer:"
+-msgstr "Impossible de modifier l'imprimante :"
+-
+-msgid "Set Allowed Users"
+-msgstr "Définir les autorisations"
+-
+-msgid "Unable to get printer attributes:"
+-msgstr "Impossible de récupérer les attributs de l'imprimante :"
+-
+-msgid "Set Publishing"
+-msgstr "Publier"
+-
+-msgid "Unable to change printer-is-shared attribute:"
+-msgstr "Impossible de modifier l'attribut « printer-is-shared » :"
+-
+-msgid "Classes"
+-msgstr "Classes"
+-
+-msgid "Unable to get class list:"
+-msgstr "Impossible d'obtenir la liste des classes :"
+-
+-msgid "Unable to get class status:"
+-msgstr "Impossible d'obtenir l'état de la classe :"
+-
+-msgid "Move Job"
+-msgstr "Transférer la tâche"
+-
+-msgid "Unable to find destination for job!"
+-msgstr "Impossible de trouver la destination de la tâche !"
+-
+-msgid "Move All Jobs"
+-msgstr "Transférer toutes les tâches"
+-
+-msgid "Unable to move job"
+-msgstr "Impossible de transférer la tâche !"
+-
+-msgid "Unable to move jobs"
+-msgstr "Impossible de transférer les tâches !"
+-
+-msgid "Print Test Page"
+-msgstr "Imprimer la page de test"
+-
+-msgid "Unable to print test page:"
+-msgstr "Impossible d'imprimer la page de test :"
+-
+-msgid "Jobs"
+-msgstr "Tâches"
+-
+-msgid "Job operation failed:"
+-msgstr "L'opération sur la tâche a échoué :"
+-
+-msgid "Printers"
+-msgstr "Imprimantes"
+-
+-msgid "Unable to get printer list:"
+-msgstr "Impossible d'obtenir la liste des imprimantes :"
+-
+-msgid "Unable to get printer status:"
+-msgstr "Impossible d'obtenir l'état de l'imprimante :"
+-
+-msgid "OK"
+-msgstr "OK"
+-
+-msgid "Unable to open PPD file"
+-msgstr "Impossible d'ouvrir le fichier PPD !"
+-
+-msgid "NULL PPD file pointer"
+-msgstr "Pointeur de fichier PPD NUL !"
+-
+-msgid "Memory allocation error"
+-msgstr "Problème d'allocation de mémoire"
+-
+-msgid "Missing PPD-Adobe-4.x header"
+-msgstr "Il manque l'entête PPD-Adobe-4.x"
+-
+-msgid "Missing value string"
+-msgstr "Il manque la valeur"
+-
+-msgid "Internal error"
+-msgstr "Erreur interne"
+-
+-msgid "Bad OpenGroup"
+-msgstr "OpenGroup erroné"
+-
+-msgid "OpenGroup without a CloseGroup first"
+-msgstr "OpenGroup sans de CloseGroup d'abord"
+-
+-msgid "Bad OpenUI/JCLOpenUI"
+-msgstr "OpenUI/JCLOpenUI erroné"
+-
+-msgid "OpenUI/JCLOpenUI without a CloseUI/JCLCloseUI first"
+-msgstr "OpenUI/JCLOpenUI sans de CloseUI/JCLCloseUI d'abord"
+-
+-msgid "Bad OrderDependency"
+-msgstr "OrderDependency erroné"
+-
+-msgid "Bad UIConstraints"
+-msgstr "UIConstraints erroné"
+-
+-msgid "Missing asterisk in column 1"
+-msgstr "Il manque un astérisque à la colonne 1"
+-
+-msgid "Line longer than the maximum allowed (255 characters)"
+-msgstr "Ligne plus longue que les 255 caractères autorisés"
+-
+-msgid "Illegal control character"
+-msgstr "Caractère de contrôle incorrect"
+-
+-msgid "Illegal main keyword string"
+-msgstr "Mot-clé essentiel incorrect"
+-
+-msgid "Illegal option keyword string"
+-msgstr "Mot-clé d'option incorrect"
+-
+-msgid "Illegal translation string"
+-msgstr "Traduction incorrecte"
+-
+-msgid "Illegal whitespace character"
+-msgstr "Caractère « espace blanc » incorrect"
+-
+-msgid "Bad custom parameter"
+-msgstr "Paramètre de personnalisation incorrect"
+-
+-msgid "Unknown"
+-msgstr "Inconnu(e)"
+-
+-msgid "Custom"
+-msgstr "Personnalisation"
+-
+-msgid "JCL"
+-msgstr "JCL ( Langage de contrôle de tâche )"
+-
+-msgid "No authentication information provided!"
+-msgstr "Pas d'information d'authentification !"
+-
+-#, c-format
+-msgid "Password for %s required to access %s via SAMBA: "
+-msgstr "Il faut un mot de passe à %s pour accéder à %s via SAMBA : "
+-
+-#, c-format
+-msgid "Running command: %s %s -N -U '%s%%%s' -c '%s'\n"
+-msgstr "Exécute la commande : %s %s -N -U '%s%%%s' -c '%s'\n"
+-
+-#, c-format
+-msgid "cupsaddsmb: Unable to run \"%s\": %s\n"
+-msgstr "cupsaddsmb: Impossible d'exécuter « %s » : %s\n"
+-
+-msgid "cupsaddsmb: No Windows printer drivers are installed!\n"
+-msgstr "cupsaddsmb: Aucun pilote d'impression pour Windows n'est installé !\n"
+-
+-msgid "cupsaddsmb: Warning, no Windows 2000 printer drivers are installed!\n"
+-msgstr "cupsaddsmb: Attention, aucun pilote d'impression pour Windows 2000 n'est installé !\n"
+-
+-#, c-format
+-msgid "lpadmin: Printer %s is already a member of class %s.\n"
+-msgstr "lpadmin: L'imprimante %s est déjà membre de la classe %s.\n"
+-
+-msgid "lpadmin: No member names were seen!\n"
+-msgstr "lpadmin: Aucun nom de membre trouvé !\n"
+-
+-#, c-format
+-msgid "lpadmin: Printer %s is not a member of class %s.\n"
+-msgstr "lpadmin: L'imprimante %s n'est pas membre de la classe %s.\n"
+-
+-#, c-format
+-msgid ""
+-"Device: uri = %s\n"
+-" class = %s\n"
+-" info = %s\n"
+-" make-and-model = %s\n"
+-" device-id = %s\n"
+-msgstr ""
+-"Matériel : URI = %s\n"
+-" classe = %s\n"
+-" info = %s\n"
+-" marque/modèle = %s\n"
+-" ID matériel = %s\n"
+-
+-#, c-format
+-msgid ""
+-"Model: name = %s\n"
+-" natural_language = %s\n"
+-" make-and-model = %s\n"
+-" device-id = %s\n"
+-msgstr ""
+-"Modèle : nom = %s\n"
+-" langue naturelle = %s\n"
+-" marque/modèle = %s\n"
+-" ID matériel = %s\n"
+-
+-
+-msgid "Usage: lpmove job/src dest\n"
+-msgstr "Utilisation : lpmove tâche/source destination\n"
+-
+-msgid "lpstat: Need \"completed\", \"not-completed\", or \"all\" after -W!\n"
+-msgstr "lpstat: Il faut « completed » ou « not-completed » après l'option -W !\n"
+-
+-#, c-format
+-msgid "%s accepting requests since %s\n"
+-msgstr "%s accepte les requêtes depuis le %s\n"
+-
+-#, c-format
+-msgid ""
+-"%s not accepting requests since %s -\n"
+-"\t%s\n"
+-msgstr ""
+-"%s n'accepte pas les requêtes depuis le %s -\n"
+-"\t%s\n"
+-
+-#, c-format
+-msgid "%s/%s accepting requests since %s\n"
+-msgstr "%s/%s accepte les requêtes depuis le %s\n"
+-
+-#, c-format
+-msgid ""
+-"%s/%s not accepting requests since %s -\n"
+-"\t%s\n"
+-msgstr ""
+-"%s/%s n'accepte pas les requêtes depuis le %s -\n"
+-"\t%s\n"
+-
+-msgid "lpc> "
+-msgstr "lpc> "
+-
+-#, c-format
+-msgid "%s: Unable to contact server!\n"
+-msgstr "%s: Impossible de contacter le serveur !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected username after '-U' option!\n"
+-msgstr "%s: Erreur - il faut un nom d'utilisateur après l'option '-U' !\n"
+-
+-#, c-format
+-msgid "%s: Error - unknown destination \"%s/%s\"!\n"
+-msgstr "%s: Erreur - destination « %s/%s » inconnue !\n"
+-
+-#, c-format
+-msgid "%s: Unknown destination \"%s\"!\n"
+-msgstr "%s: Destination « %s » inconnue !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected hostname after '-h' option!\n"
+-msgstr "%s: Erreur - il faut un nom de machine après l'option '-h' !\n"
+-
+-#, c-format
+-msgid "%s: error - %s environment variable names non-existent destination \"%s\"!\n"
+-msgstr "%s: Erreur - la variable d'environnement %s désigne une destination inexistente « %s » !\n"
+-
+-#, c-format
+-msgid "%s: error - no default destination available.\n"
+-msgstr "%s: Erreur - aucune destination par défaut n'est disponible.\n"
+-
+-msgid "Usage: lpq [-P dest] [-U username] [-h hostname[:port]] [-l] [+interval]\n"
+-msgstr "Utilisation : lpq [-P dest] [-U nom_utilisateur] [-h nom_machine[:port]] [-l] [+intervalle]\n"
+-
+-#, c-format
+-msgid "%s: Error - expected hostname after '-H' option!\n"
+-msgstr "%s: Erreur - il faut un nom de machine après l'option '-H' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected value after '-%c' option!\n"
+-msgstr "%s: Erreur - il faut une valeur après l'option '-%c' !\n"
+-
+-#, c-format
+-msgid "%s: Warning - '%c' format modifier not supported - output may not be correct!\n"
+-msgstr "%s: Attention - le modificateur de format '%c' n'est pas géré - l'affichage pourrait être incorrect !\n"
+-
+-#, c-format
+-msgid "%s: error - expected option=value after '-o' option!\n"
+-msgstr "%s: Erreur - il faut un argument du type option=valeur après l'option '-o' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected destination after '-P' option!\n"
+-msgstr "%s: Erreur - il faut une destination après l'option '-P' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected copy count after '-#' option!\n"
+-msgstr "%s: Erreur - il faut un nombre de copies après l'option '-#' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected name after '-%c' option!\n"
+-msgstr "%s: Erreur - il faut un nom après l'option '-%c' !\n"
+-
+-#, c-format
+-msgid "%s: Error - unknown option '%c'!\n"
+-msgstr "%s: Erreur - option '%c' inconnue !\n"
+-
+-#, c-format
+-msgid "%s: Error - unable to access \"%s\" - %s\n"
+-msgstr "%s: Erreur - impossible d'accéder à « %s » - %s\n"
+-
+-#, c-format
+-msgid "%s: Error - too many files - \"%s\"\n"
+-msgstr "%s: Erreur - trop de fichiers - « %s »\n"
+-
+-#, c-format
+-msgid "%s: Error - %s environment variable names non-existent destination \"%s\"!\n"
+-msgstr "%s: Erreur - la variable d'environnement %s désigne une destination inexistente « %s » !\n"
+-
+-#, c-format
+-msgid "%s: Error - no default destination available.\n"
+-msgstr "%s: Erreur - aucune destination par défaut n'est disponible.\n"
+-
+-#, c-format
+-msgid "%s: Error - scheduler not responding!\n"
+-msgstr "%s: Erreur - l'ordonnanceur ne répond pas !\n"
+-
+-#, c-format
+-msgid "%s: Error - unable to create temporary file \"%s\" - %s\n"
+-msgstr "%s: Erreur - impossible de créer le fichier temporaire « %s » - %s\n"
+-
+-#, c-format
+-msgid "%s: Error - unable to write to temporary file \"%s\" - %s\n"
+-msgstr "%s: Erreur - impossible d'écrire dans le fichier temporaire « %s » - %s\n"
+-
+-#, c-format
+-msgid "%s: Error - stdin is empty, so no job has been sent.\n"
+-msgstr "%s: Erreur - stdin est vide, donc aucune tâche n'a été envoyée.\n"
+-
+-#, c-format
+-msgid "%s: Error - unknown destination \"%s\"!\n"
+-msgstr "%s: Erreur - destination « %s » inconnue !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected reason text after '-r' option!\n"
+-msgstr "%s: Erreur - il faut le texte de la raison après l'option '-r' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected username after '-u' option!\n"
+-msgstr "%s: Erreur - il faut un nom d'utilisateur après l'option '-u' !\n"
+-
+-#, c-format
+-msgid "%s: %s failed: %s\n"
+-msgstr ""%s: %s a échoué : %s"
+-
+-#, c-format
+-msgid "%s: Error - expected destination after '-d' option!\n"
+-msgstr "%s: Erreur - il faut une destination après l'option '-d' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected form after '-f' option!\n"
+-msgstr "%s: Erreur - il faut un format après l'option '-f' !\n"
+-
+-#, c-format
+-msgid "%s: Warning - form option ignored!\n"
+-msgstr "%s: Attention - l'option « format » est ignorée !\n"
+-
+-#, c-format
+-msgid "%s: Expected job ID after '-i' option!\n"
+-msgstr "%s: Il faut un numéro de tâche après l'option '-i' !\n"
+-
+-#, c-format
+-msgid "%s: Error - cannot print files and alter jobs simultaneously!\n"
+-msgstr "%s: Erreur - impossible d'imprimer et modifier les tâches en même temps !\n"
+-
+-#, c-format
+-msgid "%s: Error - bad job ID!\n"
+-msgstr "%s: Erreur - numéro de tâche incorrect !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected copies after '-n' option!\n"
+-msgstr "%s: Erreur - il faut un nombre de copies après l'option '-n' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected option string after '-o' option!\n"
+-msgstr "%s: Erreur - il faut une chaîne d'option \"nom=valeur\" après l'option '-o' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected priority after '-%c' option!\n"
+-msgstr "%s: Erreur - il faut une priorité après l'option '-%c' !\n"
+-
+-#, c-format
+-msgid "%s: Error - priority must be between 1 and 100.\n"
+-msgstr "%s: Erreur - la priorité doit être comprise entre 1 et 100.\n"
+-
+-#, c-format
+-msgid "%s: Error - expected title after '-t' option!\n"
+-msgstr "%s: Erreur - il faut un titre après l'option '-t' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected mode list after '-y' option!\n"
+-msgstr "%s: Erreur - il faut une liste de modes après l'option '-y' !\n"
+-
+-#, c-format
+-msgid "%s: Warning - mode option ignored!\n"
+-msgstr "%s: Attention - l'option « mode » est ignorée !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected hold name after '-H' option!\n"
+-msgstr "%s: Erreur - il faut un code de retenue après l'option '-H' !\n"
+-
+-#, c-format
+-msgid "%s: Need job ID ('-i jobid') before '-H restart'!\n"
+-msgstr "%s: Il faut un numéro de tâche ( '-i' ) avant '-H restart' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected page list after '-P' option!\n"
+-msgstr "%s: Erreur - il faut une liste de pages après l'option '-P' !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected character set after '-S' option!\n"
+-msgstr "%s: Erreur - il faut un jeu de caractères après l'option '-S' !\n"
+-
+-#, c-format
+-msgid "%s: Warning - character set option ignored!\n"
+-msgstr "%s: Attention - l'option « jeu de caractères » est ignorée !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected content type after '-T' option!\n"
+-msgstr "%s: Erreur - il faut un type de contenu après l'option '-T' !\n"
+-
+-#, c-format
+-msgid "%s: Warning - content type option ignored!\n"
+-msgstr "%s: Attention - l'option « type de contenu » est ignorée !\n"
+-
+-#, c-format
+-msgid "%s: Error - cannot print from stdin if files or a job ID are provided!\n"
+-msgstr ""
+-"%s: Erreur - impossible d'imprimer depuis l'entrée standard si un fichier\n"
+-" ou un numéro de tâche est spécifié !\n"
+-
+-#, c-format
+-msgid "%s: Error - need \"completed\", \"not-completed\", or \"all\" after '-W' option!\n"
+-msgstr "%s: Erreur - il faut « completed », « not-completed », ou « all » après l'option -W !\n"
+-
+-#, c-format
+-msgid "%s: Error - expected destination after '-b' option!\n"
+-msgstr "%s: Erreur - il faut une destination après l'option '-b' !\n"
+-
+-#, c-format
+-msgid "%s: Invalid destination name in list \"%s\"!\n"
+-msgstr "%s: Erreur - nom de destination incorrect dans la liste « %s » !\n"
+-
+-#, c-format
+-msgid "%s: Unable to connect to server\n"
+-msgstr "%s: Impossible de se connecter au serveur !\n"
+-
+-msgid "Print Job:"
+-msgstr "Imprimer la tâche :"
+-
+-msgid "pending"
+-msgstr "en attente"
+-
+-msgid "held"
+-msgstr "retenue"
+-
+-msgid "processing"
+-msgstr "en cours"
+-
+-msgid "stopped"
+-msgstr "arrêtée"
+-
+-msgid "canceled"
+-msgstr "annulée"
+-
+-msgid "aborted"
+-msgstr "abandonnée"
+-
+-msgid "completed"
+-msgstr "terminée"
+-
+-msgid "unknown"
+-msgstr "inconnue"
+-
+-msgid "untitled"
+-msgstr "sans titre"
+-
+-msgid "Printer:"
+-msgstr "Imprimante :"
+-
+-msgid "idle"
+-msgstr "inactive"
+-
+-msgid "Missing notify-subscription-ids attribute!"
+-msgstr "Il manque l'attibut notify-subscription-ids"
+-
+-msgid "Job subscriptions cannot be renewed!"
+-msgstr "Les souscriptions de tâche ne peuvent être renouvelées !"
+-
+-
+-I AM HERE
+-
+-
+-msgid "cupsd: Expected config filename after \"-c\" option!\n"
+-msgstr "cupsd: Il faut un nom de fichier de configuration après l'option '-c' !\n"
+-
+-msgid "cupsd: launchd(8) support not compiled in, running in normal mode.\n"
+-msgstr "cupsd: la gestion de launchd(8) n'a pas été compilée - exécution en mode normal.\n"
+-
+-#, c-format
+-msgid "cupsd: Unknown option \"%c\" - aborting!\n"
+-msgstr "cupsd: Option « %c » inconnue - abandon !\n"
+-
+-#, c-format
+-msgid "cupsd: Unknown argument \"%s\" - aborting!\n"
+-msgstr "cupsd: Argument « %s » inconnu - abandon !\n"
+-
+-msgid ""
+-"Usage: cupsd [-c config-file] [-f] [-F] [-h] [-l]\n"
+-"\n"
+-"-c config-file Load alternate configuration file\n"
+-"-f Run in the foreground\n"
+-"-F Run in the foreground but detach\n"
+-"-h Show this usage message\n"
+-"-l Run cupsd from launchd(8)\n"
+-msgstr ""
+-"Utilisation : cupsd [-c fichier-config] [-f] [-F] [-h] [-l]\n"
+-"\n"
+-"-c fichier-config Charge un autre fichier de configuration\n"
+-"-f Exécution au premier plan\n"
+-"-F Exécution au premier plan mais détaché\n"
+-"-h Affiche the message d'aide\n"
+-"-l Exécute cupsd depuis launchd(8)\n"
+-
+-#, c-format
+-msgid " WARN Line %d only contains whitespace!\n"
+-msgstr " WARN Line %d only contains whitespace!\n"
+-
+-msgid " WARN File contains a mix of CR, LF, and CR LF line endings!\n"
+-msgstr " WARN File contains a mix of CR, LF, and CR LF line endings!\n"
+-
+-msgid " WARN Non-Windows PPD files should use lines ending with only LF, not CR LF!\n"
+-msgstr " WARN Non-Windows PPD files should use lines ending with only LF, not CR LF!\n"
+-
+-msgid "Printer Maintenance"
+-msgstr "Maintenance de l'imprimante"
+-
+-msgid "Unable to send maintenance job:"
+-msgstr "Impossible d'envoyer la tâche de maintenance :"
+-
+-#, c-format
+-msgid "cupsaddsmb: No PPD file for printer \"%s\" - %s\n"
+-msgstr "cupsaddsmb: Pas de fichier PPD pour l'imprimante « %s » - %s\n"
+-
+-#, c-format
+-msgid " **FAIL** %s %s does not exist!\n"
+-msgstr " **FAIL** %s %s does not exist!\n"
+-
+-#, c-format
+-msgid " **FAIL** Bad language \"%s\"!\n"
+-msgstr " **FAIL** Bad language \"%s\"!\n"
+-
+-#, c-format
+-msgid " **FAIL** Missing \"%s\" translation string for option %s!\n"
+-msgstr " **FAIL** Missing \"%s\" translation string for option %s!\n"
+-
+-#, c-format
+-msgid " **FAIL** Default translation string for option %s contains 8-bit characters!\n"
+-msgstr " **FAIL** Default translation string for option %s contains 8-bit characters!\n"
+-
+-#, c-format
+-msgid " **FAIL** Missing \"%s\" translation string for option %s, choice %s!\n"
+-msgstr " **FAIL** Missing \"%s\" translation string for option %s, choice %s!\n"
+-
+-#, c-format
+-msgid " **FAIL** Default translation string for option %s choice %s contains 8-bit characters!\n"
+-msgstr " **FAIL** Default translation string for option %s choice %s contains 8-bit characters!\n"
+-
+-#, c-format
+-msgid " **FAIL** Bad cupsFilter value \"%s\"!\n"
+-msgstr " **FAIL** Bad cupsFilter value \"%s\"!\n"
+-
+-msgid "Help"
+-msgstr "Aide"
+-
+-#, c-format
+-msgid "Missing value on line %d!\n"
+-msgstr "Il manque une valeur à la ligne %d !\n"
+-
+-#, c-format
+-msgid "Missing double quote on line %d!\n"
+-msgstr "Il manque un \" à la ligne %d !\n"
+-
+-#, c-format
+-msgid "Bad option + choice on line %d!\n"
+-msgstr "Couple option + choix incorrect à la ligne %d !\n"
+-
+-#, c-format
+-msgid "Unable to copy Windows 2000 printer driver files (%d)!\n"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 2000 ( %d ) !\n"
+-
+-#, c-format
+-msgid "Unable to copy CUPS printer driver files (%d)!\n"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour CUPS ( %d ) !\n"
+-
+-#, c-format
+-msgid "Unable to install Windows 2000 printer driver files (%d)!\n"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 2000 ( %d ) !\n"
+-
+-#, c-format
+-msgid "Unable to copy Windows 9x printer driver files (%d)!\n"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 9x ( %d ) !\n"
+-
+-#, c-format
+-msgid "Unable to install Windows 9x printer driver files (%d)!\n"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 9x ( %d ) !\n"
+-
+-msgid "No Windows printer drivers are installed!\n"
+-msgstr "Aucun pilote d'impression pour Windows n'est installé !\n"
+-
+-msgid "Warning, no Windows 2000 printer drivers are installed!\n"
+-msgstr "Attention, aucun pilote d'impression pour Windows 2000 n'est installé !\n"
+-
+-#, c-format
+-msgid "Unable to set Windows printer driver (%d)!\n"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows ( %d ) !\n""
+-
+-msgid ""
+-"Usage: cupsaddsmb [options] printer1 ... printerN\n"
+-" cupsaddsmb [options] -a\n"
+-"\n"
+-"Options:\n"
+-" -E Encrypt the connection to the server\n"
+-" -H samba-server Use the named SAMBA server\n"
+-" -U samba-user Authenticate using the named SAMBA user\n"
+-" -a Export all printers\n"
+-" -h cups-server Use the named CUPS server\n"
+-" -v Be verbose (show commands)\n"
+-msgstr ""
+-"Utilisation : cupsaddsmb [options] imprimante1 ... imprimanteN\n"
+-" cupsaddsmb [options] -a\n"
+-"\n"
+-"Options:\n"
+-" -E Crypter la connexion au serveur\n"
+-" -H serveur-samba Utiliser le serveur SAMBA désigné\n"
+-" -U utilisateur-samba S'authentifier avec l'utilisateur SAMBA désigné\n"
+-" -a Exporter toutes les imprimantes\n"
+-" -h serveur-CUPS Utiliser le serveur CUPS désigné\n"
+-" -v Être locace ( afficher les commandes )\n"
+-
+-#, c-format
+-msgid "Unable to copy Windows 2000 printer driver files (%d)!"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 2000 ( %d ) !"
+-
+-#, c-format
+-msgid "Unable to copy CUPS printer driver files (%d)!"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour CUPS ( %d ) !"
+-
+-#, c-format
+-msgid "Unable to install Windows 2000 printer driver files (%d)!"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 2000 ( %d ) !"
+-
+-#, c-format
+-msgid "Unable to copy Windows 9x printer driver files (%d)!"
+-msgstr "Impossible de copier les fichiers de pilote d'impression pour Windows 9x ( %d ) !"
+-
+-#, c-format
+-msgid "Unable to install Windows 9x printer driver files (%d)!"
+-msgstr "Impossible d'installer les fichiers de pilote d'impression pour Windows 9x ( %d ) !"
+-
+-msgid "No Windows printer drivers are installed!"
+-msgstr "Aucun pilote d'impression pour Windows n'est installé !"
+-
+-msgid "Warning, no Windows 2000 printer drivers are installed!"
+-msgstr "Attention, aucun pilote d'impression pour Windows 2000 n'est installé !"
+-
+-#, c-format
+-msgid "open of %s failed: %s"
+-msgstr "l'ouverture de %s a échoué : %s"
+-
+-#, c-format
+-msgid "Running command: %s %s -N -A %s -c '%s'\n"
+-msgstr "Exécution de la commande : %s %s -N -A %s -c '%s'\n"
+-
+-#, c-format
+-msgid "stat of %s failed: %s"
+-msgstr "stat sur %s a échoué : %s"
+-
+-#, c-format
+-msgid "Job #%d is already cancelled - can't cancel."
+-msgstr "La tâche n°%d est déjà annulée - impossible de l'annuler."
+-
+-#, c-format
+-msgid "Job #%d is already aborted - can't cancel."
+-msgstr "La tâche n°%d est déjà abandonnée - impossible de l'annuler."
+-
+-#, c-format
+-msgid "Job #%d is already completed - can't cancel."
+-msgstr "La tâche n°%d est déjà terminée - impossible de l'annuler."
+-
+-#, c-format
+-msgid "You must access this page using the URL <A HREF=\"https://%s:%d%s\">https://%s:%d%s</A>."
+-msgstr "Vous devez accéder à cette page avec l'URL <A HREF=\"https://%s:%d%s\">https://%s:%d%s</A>."
+-
+-#, c-format
+-msgid "Unsupported format '%s'!"
+-msgstr "Format non géré '%s' !"
+-
+-msgid "FAIL\n"
+-msgstr "ÉCHEC"
+-
+-#, c-format
+-msgid ""
+-" Line %d is longer than 255 characters (%d)!\n"
+-" REF: Page 25, Line Length\n"
+-msgstr ""
+-" Line %d is longer than 255 characters (%d)!\n"
+-" REF: Page 25, Line Length\n"
+-
+-msgid ""
+-" Missing %!PS-Adobe-3.0 on first line!\n"
+-" REF: Page 17, 3.1 Conforming Documents\n"
+-msgstr ""
+-" Missing %!PS-Adobe-3.0 on first line!\n"
+-" REF: Page 17, 3.1 Conforming Documents\n"
+-
+-#, c-format
+-msgid ""
+-" Bad %%%%Pages: on line %d!\n"
+-" REF: Page 43, %%%%Pages:\n"
+-msgstr ""
+-" Bad %%%%Pages: on line %d!\n"
+-" REF: Page 43, %%%%Pages:\n"
+-
+-#, c-format
+-msgid ""
+-" Bad %%%%BoundingBox: on line %d!\n"
+-" REF: Page 39, %%%%BoundingBox:\n"
+-msgstr ""
+-" Bad %%%%BoundingBox: on line %d!\n"
+-" REF: Page 39, %%%%BoundingBox:\n"
+-
+-#, c-format
+-msgid ""
+-" Bad %%%%Page: on line %d!\n"
+-" REF: Page 53, %%%%Page:\n"
+-msgstr ""
+-" Bad %%%%Page: on line %d!\n"
+-" REF: Page 53, %%%%Page:\n"
+-
+-#, c-format
+-msgid ""
+-" Missing or bad %%BoundingBox: comment!\n"
+-" REF: Page 39, %%BoundingBox:\n"
+-msgstr ""
+-" Missing or bad %%BoundingBox: comment!\n"
+-" REF: Page 39, %%BoundingBox:\n"
+-
+-#, c-format
+-msgid ""
+-" Missing or bad %%Pages: comment!\n"
+-" REF: Page 43, %%Pages:\n"
+-msgstr ""
+-" Missing or bad %%Pages: comment!\n"
+-" REF: Page 43, %%Pages:\n"
+-
+-#, c-format
+-msgid ""
+-" Missing %%EndComments comment!\n"
+-" REF: Page 41, %%EndComments\n"
+-msgstr ""
+-" Missing %%EndComments comment!\n"
+-" REF: Page 41, %%EndComments\n"
+-
+-#, c-format
+-msgid ""
+-" Missing or bad %%Page: comments!\n"
+-" REF: Page 53, %%Page:\n"
+-msgstr ""
+-" Missing or bad %%Page: comments!\n"
+-" REF: Page 53, %%Page:\n"
+-
+-#, c-format
+-msgid " Too many %%EndDocument comments!\n"
+-msgstr " Too many %%EndDocument comments!\n"
+-
+-#, c-format
+-msgid " Too many %%BeginDocument comments!\n"
+-msgstr " Too many %%BeginDocument comments!\n"
+-
+-#, c-format
+-msgid " Saw %d lines that exceeded 255 characters!\n"
+-msgstr " Saw %d lines that exceeded 255 characters!\n"
+-
+-msgid "PASS\n"
+-msgstr "OK"
+-
+-msgid " Warning: file contains binary data!\n"
+-msgstr " Warning: file contains binary data!\n"
+-
+-#, c-format
+-msgid " Warning: obsolete DSC version %.1f in file!\n"
+-msgstr " Warning: obsolete DSC version %.1f in file!\n"
+-
+-#, c-format
+-msgid " Warning: no %%EndComments comment in file!\n"
+-msgstr " Warning: no %%EndComments comment in file!\n"
+-
+-msgid ""
+-"Usage: cupstestdsc [options] filename.ps [... filename.ps]\n"
+-" cupstestdsc [options] -\n"
+-"\n"
+-"Options:\n"
+-"\n"
+-" -h Show program usage\n"
+-"\n"
+-" Note: this program only validates the DSC comments, not the PostScript itself.\n"
+-msgstr ""
+-"Usage: cupstestdsc [options] filename.ps [... filename.ps]\n"
+-" cupstestdsc [options] -\n"
+-"\n"
+-"Options:\n"
+-"\n"
+-" -h Show program usage\n"
+-"\n"
+-" Note: this program only validates the DSC comments, not the PostScript itself.\n"
+-
+-
+-#, c-format
+-msgid "Password for %s on %s? "
+-msgstr "Mot de passe pour %s sur %s? "
+-
+-msgid ""
+-" **FAIL** 1284DeviceId must be 1284DeviceID!\n"
+-" REF: Page 72, section 5.5\n"
+-msgstr ""
+-" **FAIL** 1284DeviceId must be 1284DeviceID!\n"
+-" REF: Page 72, section 5.5\n"
+-
+-
+-#
+-# End of "$Id$".
+-#
+diff -urNad cupsys-1.2.8~/pdftops/pdftops.cxx cupsys-1.2.8/pdftops/pdftops.cxx
+--- cupsys-1.2.8~/pdftops/pdftops.cxx 2006-01-10 20:53:28.000000000 +0000
++++ cupsys-1.2.8/pdftops/pdftops.cxx 2007-03-13 15:02:07.000000000 +0000
+@@ -279,9 +279,16 @@
+
+ globalParams = new GlobalParams(buffer);
+
+- globalParams->setPSPaperWidth(width);
+- globalParams->setPSPaperHeight(length);
+- globalParams->setPSImageableArea(left, bottom, right, top);
++ if (fit || globalParams->getPSPaperWidth() > 0)
++ {
++ // Only set paper size and area if we are fitting to the job's
++ // page size or the pdftops.conf file does not contain
++ // "psPaperSize match"...
++ globalParams->setPSPaperWidth(width);
++ globalParams->setPSPaperHeight(length);
++ globalParams->setPSImageableArea(left, bottom, right, top);
++ }
++
+ globalParams->setPSDuplex(duplex);
+ globalParams->setPSExpandSmaller(fit);
+ globalParams->setPSShrinkLarger(fit);
+diff -urNad cupsys-1.2.8~/scheduler/classes.c cupsys-1.2.8/scheduler/classes.c
+--- cupsys-1.2.8~/scheduler/classes.c 2007-03-13 15:02:07.000000000 +0000
++++ cupsys-1.2.8/scheduler/classes.c 2007-03-13 15:02:07.000000000 +0000
+@@ -3,7 +3,7 @@
+ *
+ * Printer class routines for the Common UNIX Printing System (CUPS).
+ *
+- * Copyright 1997-2006 by Easy Software Products, all rights reserved.
++ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+@@ -371,7 +371,21 @@
+ {
+ cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
+
+- p = cupsdAddClass(value);
++ /*
++ * Since prior classes may have implicitly defined this class,
++ * see if it already exists...
++ */
++
++ if ((p = cupsdFindDest(value)) != NULL)
++ {
++ p->type = CUPS_PRINTER_CLASS;
++ cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
++ LocalPort, value);
++ cupsdSetString(&p->error_policy, "retry-job");
++ }
++ else
++ p = cupsdAddClass(value);
++
+ p->accepting = 1;
+ p->state = IPP_PRINTER_IDLE;
+
+@@ -708,6 +722,7 @@
+ time_t curtime; /* Current time */
+ struct tm *curdate; /* Current date */
+ cups_option_t *option; /* Current option */
++ const char *ptr; /* Pointer into info/location */
+
+
+ /*
+@@ -782,10 +797,40 @@
+ cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
+
+ if (pclass->info)
+- cupsFilePrintf(fp, "Info %s\n", pclass->info);
++ {
++ if ((ptr = strchr(pclass->info, '#')) != NULL)
++ {
++ /*
++ * Need to quote the first # in the info string...
++ */
++
++ cupsFilePuts(fp, "Info ");
++ cupsFileWrite(fp, pclass->info, ptr - pclass->info);
++ cupsFilePutChar(fp, '\\');
++ cupsFilePuts(fp, ptr);
++ cupsFilePutChar(fp, '\n');
++ }
++ else
++ cupsFilePrintf(fp, "Info %s\n", pclass->info);
++ }
+
+ if (pclass->location)
+- cupsFilePrintf(fp, "Location %s\n", pclass->location);
++ {
++ if ((ptr = strchr(pclass->info, '#')) != NULL)
++ {
++ /*
++ * Need to quote the first # in the location string...
++ */
++
++ cupsFilePuts(fp, "Location ");
++ cupsFileWrite(fp, pclass->location, ptr - pclass->location);
++ cupsFilePutChar(fp, '\\');
++ cupsFilePuts(fp, ptr);
++ cupsFilePutChar(fp, '\n');
++ }
++ else
++ cupsFilePrintf(fp, "Location %s\n", pclass->location);
++ }
+
+ if (pclass->state == IPP_PRINTER_STOPPED)
+ {
+diff -urNad cupsys-1.2.8~/scheduler/client.c cupsys-1.2.8/scheduler/client.c
+--- cupsys-1.2.8~/scheduler/client.c 2007-02-07 20:54:37.000000000 +0000
++++ cupsys-1.2.8/scheduler/client.c 2007-03-13 15:02:07.000000000 +0000
+@@ -3472,7 +3472,7 @@
+ int pid, /* Process ID of command */
+ status; /* Status of command */
+ char command[1024], /* Command */
+- *argv[11], /* Command-line arguments */
++ *argv[12], /* Command-line arguments */
+ *envp[MAX_ENV + 1], /* Environment variables */
+ home[1024], /* HOME environment variable */
+ infofile[1024], /* Type-in information for cert */
+diff -urNad cupsys-1.2.8~/scheduler/cups-lpd.c cupsys-1.2.8/scheduler/cups-lpd.c
+--- cupsys-1.2.8~/scheduler/cups-lpd.c 2006-10-10 20:47:03.000000000 +0100
++++ cupsys-1.2.8/scheduler/cups-lpd.c 2007-03-13 15:02:07.000000000 +0000
+@@ -96,7 +96,8 @@
+ int destsize, cups_option_t **options,
+ int *accepting, int *shared, ipp_pstate_t *state);
+ static int print_file(http_t *http, int id, const char *filename,
+- const char *docname, const char *user, int last);
++ const char *docname, const char *user,
++ const char *format, int last);
+ static int recv_print_job(const char *name, int num_defaults,
+ cups_option_t *defaults);
+ static int remove_jobs(const char *name, const char *agent,
+@@ -824,6 +825,7 @@
+ const char *filename, /* I - File to print */
+ const char *docname, /* I - document-name */
+ const char *user, /* I - requesting-user-name */
++ const char *format, /* I - document-format */
+ int last) /* I - 1 = last file in job */
+ {
+ ipp_t *request; /* IPP request */
+@@ -846,6 +848,10 @@
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
+ "document-name", NULL, docname);
+
++ if (format)
++ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
++ "document-format", NULL, format);
++
+ if (last)
+ ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+
+@@ -1278,6 +1284,8 @@
+ docnumber ++;
+
+ if (print_file(http, id, temp[i], docname, user,
++ cupsGetOption("document-format", num_options,
++ options),
+ docnumber == doccount))
+ status = 1;
+ else
+diff -urNad cupsys-1.2.8~/scheduler/ipp.c cupsys-1.2.8/scheduler/ipp.c
+--- cupsys-1.2.8~/scheduler/ipp.c 2006-12-06 20:10:16.000000000 +0000
++++ cupsys-1.2.8/scheduler/ipp.c 2007-03-13 15:02:37.000000000 +0000
+@@ -5466,9 +5466,6 @@
+ if ((job->dtype & dmask) != dtype &&
+ (!job->printer || (job->printer->type & dmask) != dtype))
+ continue;
+- if (username[0] && strcasecmp(username, job->username))
+- continue;
+-
+ if (completed && job->state_value <= IPP_JOB_STOPPED)
+ continue;
+
+@@ -5480,6 +5477,9 @@
+ if (!job->attrs)
+ continue;
+
++ if (username[0] && strcasecmp(username, job->username))
++ continue;
++
+ if (count > 0)
+ ippAddSeparator(con->response);
+
+diff -urNad cupsys-1.2.8~/scheduler/ipp.c.orig cupsys-1.2.8/scheduler/ipp.c.orig
+--- cupsys-1.2.8~/scheduler/ipp.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ cupsys-1.2.8/scheduler/ipp.c.orig 2006-12-06 20:10:16.000000000 +0000
+@@ -0,0 +1,9165 @@
++/*
++ * "$Id: ipp.c 6145 2006-12-06 20:10:16Z mike $"
++ *
++ * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
++ *
++ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
++ *
++ * These coded instructions, statements, and computer programs are the
++ * property of Easy Software Products and are protected by Federal
++ * copyright law. Distribution and use rights are outlined in the file
++ * "LICENSE.txt" which should have been included with this file. If this
++ * file is missing or damaged please contact Easy Software Products
++ * at:
++ *
++ * Attn: CUPS Licensing Information
++ * Easy Software Products
++ * 44141 Airport View Drive, Suite 204
++ * Hollywood, Maryland 20636 USA
++ *
++ * Voice: (301) 373-9600
++ * EMail: cups-info at cups.org
++ * WWW: http://www.cups.org
++ *
++ * Contents:
++ *
++ * cupsdProcessIPPRequest() - Process an incoming IPP request...
++ * accept_jobs() - Accept print jobs to a printer.
++ * add_class() - Add a class to the system.
++ * add_file() - Add a file to a job.
++ * add_job() - Add a job to a print queue.
++ * add_job_state_reasons() - Add the "job-state-reasons" attribute based
++ * upon the job and printer state...
++ * add_job_subscriptions() - Add any subcriptions for a job.
++ * add_job_uuid() - Add job-uuid attribute to a job.
++ * add_printer() - Add a printer to the system.
++ * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
++ * based upon the printer state...
++ * add_queued_job_count() - Add the "queued-job-count" attribute for
++ * apply_printer_defaults() - Apply printer default options to a job.
++ * authenticate_job() - Set job authentication info.
++ * cancel_all_jobs() - Cancel all print jobs.
++ * cancel_job() - Cancel a print job.
++ * cancel_subscription() - Cancel a subscription.
++ * check_quotas() - Check quotas for a printer and user.
++ * copy_attribute() - Copy a single attribute.
++ * copy_attrs() - Copy attributes from one request to another.
++ * copy_banner() - Copy a banner file to the requests directory
++ * for the specified job.
++ * copy_file() - Copy a PPD file or interface script...
++ * copy_model() - Copy a PPD model file, substituting default
++ * values as needed...
++ * copy_job_attrs() - Copy job attributes.
++ * copy_printer_attrs() - Copy printer attributes.
++ * copy_subscription_attrs() - Copy subscription attributes.
++ * create_job() - Print a file to a printer or class.
++ * create_requested_array() - Create an array for the requested-attributes.
++ * create_subscription() - Create a notification subscription.
++ * delete_printer() - Remove a printer or class from the system.
++ * get_default() - Get the default destination.
++ * get_devices() - Get the list of available devices on the
++ * local system.
++ * get_job_attrs() - Get job attributes.
++ * get_jobs() - Get a list of jobs for the specified printer.
++ * get_notifications() - Get events for a subscription.
++ * get_ppds() - Get the list of PPD files on the local
++ * system.
++ * get_printer_attrs() - Get printer attributes.
++ * get_printers() - Get a list of printers.
++ * get_subscription_attrs() - Get subscription attributes.
++ * get_subscriptions() - Get subscriptions.
++ * get_username() - Get the username associated with a request.
++ * hold_job() - Hold a print job.
++ * move_job() - Move a job to a new destination.
++ * ppd_parse_line() - Parse a PPD default line.
++ * print_job() - Print a file to a printer or class.
++ * read_ps_line() - Read a line from a PS file...
++ * read_ps_job_ticket() - Reads a job ticket embedded in a PS file.
++ * reject_jobs() - Reject print jobs to a printer.
++ * release_job() - Release a held print job.
++ * restart_job() - Restart an old print job.
++ * save_auth_info() - Save authentication information for a job.
++ * send_document() - Send a file to a printer or class.
++ * send_http_error() - Send a HTTP error back to the IPP client.
++ * send_ipp_status() - Send a status back to the IPP client.
++ * set_default() - Set the default destination...
++ * set_job_attrs() - Set job attributes.
++ * set_printer_defaults() - Set printer default options from a request.
++ * start_printer() - Start a printer.
++ * stop_printer() - Stop a printer.
++ * url_encode_attr() - URL-encode a string attribute.
++ * user_allowed() - See if a user is allowed to print to a queue.
++ * validate_job() - Validate printer options and destination.
++ * validate_name() - Make sure the printer name only contains
++ * valid chars.
++ * validate_user() - Validate the user for the request.
++ */
++
++/*
++ * Include necessary headers...
++ */
++
++#include "cupsd.h"
++
++#ifdef HAVE_LIBPAPER
++# include <paper.h>
++#endif /* HAVE_LIBPAPER */
++
++
++/*
++ * Local functions...
++ */
++
++static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void add_class(cupsd_client_t *con, ipp_attribute_t *uri);
++static int add_file(cupsd_client_t *con, cupsd_job_t *job,
++ mime_type_t *filetype, int compression);
++static cupsd_job_t *add_job(cupsd_client_t *con, ipp_attribute_t *uri,
++ cupsd_printer_t **dprinter,
++ mime_type_t *filetype);
++static void add_job_state_reasons(cupsd_client_t *con, cupsd_job_t *job);
++static void add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
++static void add_job_uuid(cupsd_client_t *con, cupsd_job_t *job);
++static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
++static void add_printer_state_reasons(cupsd_client_t *con,
++ cupsd_printer_t *p);
++static void add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
++static void apply_printer_defaults(cupsd_printer_t *printer,
++ cupsd_job_t *job);
++static void authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void cancel_all_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void cancel_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void cancel_subscription(cupsd_client_t *con, int id);
++static int check_quotas(cupsd_client_t *con, cupsd_printer_t *p);
++static ipp_attribute_t *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
++ int quickcopy);
++static void copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
++ ipp_tag_t group, int quickcopy);
++static int copy_banner(cupsd_client_t *con, cupsd_job_t *job,
++ const char *name);
++static int copy_file(const char *from, const char *to);
++static int copy_model(cupsd_client_t *con, const char *from,
++ const char *to);
++static void copy_job_attrs(cupsd_client_t *con,
++ cupsd_job_t *job,
++ cups_array_t *ra);
++static void copy_printer_attrs(cupsd_client_t *con,
++ cupsd_printer_t *printer,
++ cups_array_t *ra);
++static void copy_subscription_attrs(cupsd_client_t *con,
++ cupsd_subscription_t *sub,
++ cups_array_t *ra);
++static void create_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static cups_array_t *create_requested_array(ipp_t *request);
++static void create_subscription(cupsd_client_t *con, ipp_attribute_t *uri);
++static void delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
++static void get_default(cupsd_client_t *con);
++static void get_devices(cupsd_client_t *con);
++static void get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void get_notifications(cupsd_client_t *con);
++static void get_ppds(cupsd_client_t *con);
++static void get_printers(cupsd_client_t *con, int type);
++static void get_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void get_subscription_attrs(cupsd_client_t *con, int sub_id);
++static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
++static const char *get_username(cupsd_client_t *con);
++static void hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void move_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static int ppd_parse_line(const char *line, char *option, int olen,
++ char *choice, int clen);
++static void print_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void read_ps_job_ticket(cupsd_client_t *con);
++static void reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void release_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void renew_subscription(cupsd_client_t *con, int sub_id);
++static void restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static void save_auth_info(cupsd_client_t *con, cupsd_job_t *job);
++static void send_document(cupsd_client_t *con, ipp_attribute_t *uri);
++static void send_http_error(cupsd_client_t *con, http_status_t status);
++static void send_ipp_status(cupsd_client_t *con, ipp_status_t status,
++ const char *message, ...)
++# ifdef __GNUC__
++__attribute__ ((__format__ (__printf__, 3, 4)))
++# endif /* __GNUC__ */
++;
++static void set_default(cupsd_client_t *con, ipp_attribute_t *uri);
++static void set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
++static void set_printer_defaults(cupsd_client_t *con,
++ cupsd_printer_t *printer);
++static void start_printer(cupsd_client_t *con, ipp_attribute_t *uri);
++static void stop_printer(cupsd_client_t *con, ipp_attribute_t *uri);
++static void url_encode_attr(ipp_attribute_t *attr, char *buffer,
++ int bufsize);
++static int user_allowed(cupsd_printer_t *p, const char *username);
++static void validate_job(cupsd_client_t *con, ipp_attribute_t *uri);
++static int validate_name(const char *name);
++static int validate_user(cupsd_job_t *job, cupsd_client_t *con,
++ const char *owner, char *username,
++ int userlen);
++
++
++/*
++ * 'cupsdProcessIPPRequest()' - Process an incoming IPP request...
++ */
++
++int /* O - 1 on success, 0 on failure */
++cupsdProcessIPPRequest(
++ cupsd_client_t *con) /* I - Client connection */
++{
++ ipp_tag_t group; /* Current group tag */
++ ipp_attribute_t *attr; /* Current attribute */
++ ipp_attribute_t *charset; /* Character set attribute */
++ ipp_attribute_t *language; /* Language attribute */
++ ipp_attribute_t *uri; /* Printer URI attribute */
++ ipp_attribute_t *username; /* requesting-user-name attr */
++ int sub_id; /* Subscription ID */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "cupsdProcessIPPRequest(%p[%d]): operation_id = %04x",
++ con, con->http.fd, con->request->request.op.operation_id);
++
++ /*
++ * First build an empty response message for this request...
++ */
++
++ con->response = ippNew();
++
++ con->response->request.status.version[0] = con->request->request.op.version[0];
++ con->response->request.status.version[1] = con->request->request.op.version[1];
++ con->response->request.status.request_id = con->request->request.op.request_id;
++
++ /*
++ * Then validate the request header and required attributes...
++ */
++
++ if (con->request->request.any.version[0] != 1)
++ {
++ /*
++ * Return an error, since we only support IPP 1.x.
++ */
++
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Bad request version number %d.%d",
++ IPP_VERSION_NOT_SUPPORTED, con->http.hostname,
++ con->request->request.any.version[0],
++ con->request->request.any.version[1]);
++
++ send_ipp_status(con, IPP_VERSION_NOT_SUPPORTED,
++ _("Bad request version number %d.%d!"),
++ con->request->request.any.version[0],
++ con->request->request.any.version[1]);
++ }
++ else if (!con->request->attrs)
++ {
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s No attributes in request",
++ IPP_BAD_REQUEST, con->http.hostname);
++
++ send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request!"));
++ }
++ else
++ {
++ /*
++ * Make sure that the attributes are provided in the correct order and
++ * don't repeat groups...
++ */
++
++ for (attr = con->request->attrs, group = attr->group_tag;
++ attr;
++ attr = attr->next)
++ if (attr->group_tag < group && attr->group_tag != IPP_TAG_ZERO)
++ {
++ /*
++ * Out of order; return an error...
++ */
++
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Attribute groups are out of order",
++ IPP_BAD_REQUEST, con->http.hostname);
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Attribute groups are out of order (%x < %x)!"),
++ attr->group_tag, group);
++ break;
++ }
++ else
++ group = attr->group_tag;
++
++ if (!attr)
++ {
++ /*
++ * Then make sure that the first three attributes are:
++ *
++ * attributes-charset
++ * attributes-natural-language
++ * printer-uri/job-uri
++ */
++
++ attr = con->request->attrs;
++ if (attr && !strcmp(attr->name, "attributes-charset") &&
++ (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_CHARSET)
++ charset = attr;
++ else
++ charset = NULL;
++
++ if (attr)
++ attr = attr->next;
++
++ if (attr && !strcmp(attr->name, "attributes-natural-language") &&
++ (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
++ language = attr;
++ else
++ language = NULL;
++
++ if ((attr = ippFindAttribute(con->request, "printer-uri",
++ IPP_TAG_URI)) != NULL)
++ uri = attr;
++ else if ((attr = ippFindAttribute(con->request, "job-uri",
++ IPP_TAG_URI)) != NULL)
++ uri = attr;
++ else
++ uri = NULL;
++
++ if (charset)
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
++ "attributes-charset", NULL, charset->values[0].string.text);
++ else
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
++ "attributes-charset", NULL, DefaultCharset);
++
++ if (language)
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
++ "attributes-natural-language", NULL,
++ language->values[0].string.text);
++ else
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
++ "attributes-natural-language", NULL, DefaultLanguage);
++
++ if (!charset || !language ||
++ (!uri &&
++ con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
++ con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
++ con->request->request.op.operation_id != CUPS_GET_CLASSES &&
++ con->request->request.op.operation_id != CUPS_GET_DEVICES &&
++ con->request->request.op.operation_id != CUPS_GET_PPDS))
++ {
++ /*
++ * Return an error, since attributes-charset,
++ * attributes-natural-language, and printer-uri/job-uri are required
++ * for all operations.
++ */
++
++ if (!charset)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "Missing attributes-charset attribute!");
++
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Missing attributes-charset attribute",
++ IPP_BAD_REQUEST, con->http.hostname);
++ }
++
++ if (!language)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "Missing attributes-natural-language attribute!");
++
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Missing attributes-natural-language attribute",
++ IPP_BAD_REQUEST, con->http.hostname);
++ }
++
++ if (!uri)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "Missing printer-uri or job-uri attribute!");
++
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Missing printer-uri or job-uri attribute",
++ IPP_BAD_REQUEST, con->http.hostname);
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
++
++ for (attr = con->request->attrs; attr; attr = attr->next)
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "attr \"%s\": group_tag = %x, value_tag = %x",
++ attr->name ? attr->name : "(null)", attr->group_tag,
++ attr->value_tag);
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Missing required attributes!"));
++ }
++ else
++ {
++ /*
++ * OK, all the checks pass so far; make sure requesting-user-name is
++ * not "root" from a remote host...
++ */
++
++ if ((username = ippFindAttribute(con->request, "requesting-user-name",
++ IPP_TAG_NAME)) != NULL)
++ {
++ /*
++ * Check for root user...
++ */
++
++ if (!strcmp(username->values[0].string.text, "root") &&
++ strcasecmp(con->http.hostname, "localhost") &&
++ strcmp(con->username, "root"))
++ {
++ /*
++ * Remote unauthenticated user masquerading as local root...
++ */
++
++ _cupsStrFree(username->values[0].string.text);
++ username->values[0].string.text = _cupsStrAlloc(RemoteRoot);
++ }
++ }
++
++ if ((attr = ippFindAttribute(con->request, "notify-subscription-id",
++ IPP_TAG_INTEGER)) != NULL)
++ sub_id = attr->values[0].integer;
++ else
++ sub_id = 0;
++
++ /*
++ * Then try processing the operation...
++ */
++
++ if (uri)
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
++ ippOpString(con->request->request.op.operation_id),
++ uri->values[0].string.text);
++ else
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "%s",
++ ippOpString(con->request->request.op.operation_id));
++
++ switch (con->request->request.op.operation_id)
++ {
++ case IPP_PRINT_JOB :
++ print_job(con, uri);
++ break;
++
++ case IPP_VALIDATE_JOB :
++ validate_job(con, uri);
++ break;
++
++ case IPP_CREATE_JOB :
++ create_job(con, uri);
++ break;
++
++ case IPP_SEND_DOCUMENT :
++ send_document(con, uri);
++ break;
++
++ case IPP_CANCEL_JOB :
++ cancel_job(con, uri);
++ break;
++
++ case IPP_GET_JOB_ATTRIBUTES :
++ get_job_attrs(con, uri);
++ break;
++
++ case IPP_GET_JOBS :
++ get_jobs(con, uri);
++ break;
++
++ case IPP_GET_PRINTER_ATTRIBUTES :
++ get_printer_attrs(con, uri);
++ break;
++
++ case IPP_HOLD_JOB :
++ hold_job(con, uri);
++ break;
++
++ case IPP_RELEASE_JOB :
++ release_job(con, uri);
++ break;
++
++ case IPP_RESTART_JOB :
++ restart_job(con, uri);
++ break;
++
++ case IPP_PAUSE_PRINTER :
++ stop_printer(con, uri);
++ break;
++
++ case IPP_RESUME_PRINTER :
++ start_printer(con, uri);
++ break;
++
++ case IPP_PURGE_JOBS :
++ cancel_all_jobs(con, uri);
++ break;
++
++ case IPP_SET_JOB_ATTRIBUTES :
++ set_job_attrs(con, uri);
++ break;
++
++ case CUPS_GET_DEFAULT :
++ get_default(con);
++ break;
++
++ case CUPS_GET_PRINTERS :
++ get_printers(con, 0);
++ break;
++
++ case CUPS_GET_CLASSES :
++ get_printers(con, CUPS_PRINTER_CLASS);
++ break;
++
++ case CUPS_ADD_PRINTER :
++ add_printer(con, uri);
++ break;
++
++ case CUPS_DELETE_PRINTER :
++ delete_printer(con, uri);
++ break;
++
++ case CUPS_ADD_CLASS :
++ add_class(con, uri);
++ break;
++
++ case CUPS_DELETE_CLASS :
++ delete_printer(con, uri);
++ break;
++
++ case CUPS_ACCEPT_JOBS :
++ case IPP_ENABLE_PRINTER :
++ accept_jobs(con, uri);
++ break;
++
++ case CUPS_REJECT_JOBS :
++ case IPP_DISABLE_PRINTER :
++ reject_jobs(con, uri);
++ break;
++
++ case CUPS_SET_DEFAULT :
++ set_default(con, uri);
++ break;
++
++ case CUPS_GET_DEVICES :
++ get_devices(con);
++ break;
++
++ case CUPS_GET_PPDS :
++ get_ppds(con);
++ break;
++
++ case CUPS_MOVE_JOB :
++ move_job(con, uri);
++ break;
++
++ case CUPS_AUTHENTICATE_JOB :
++ authenticate_job(con, uri);
++ break;
++
++ case IPP_CREATE_PRINTER_SUBSCRIPTION :
++ case IPP_CREATE_JOB_SUBSCRIPTION :
++ create_subscription(con, uri);
++ break;
++
++ case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
++ get_subscription_attrs(con, sub_id);
++ break;
++
++ case IPP_GET_SUBSCRIPTIONS :
++ get_subscriptions(con, uri);
++ break;
++
++ case IPP_RENEW_SUBSCRIPTION :
++ renew_subscription(con, sub_id);
++ break;
++
++ case IPP_CANCEL_SUBSCRIPTION :
++ cancel_subscription(con, sub_id);
++ break;
++
++ case IPP_GET_NOTIFICATIONS :
++ get_notifications(con);
++ break;
++
++ default :
++ cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
++ "%04X %s Operation %04X (%s) not supported",
++ IPP_OPERATION_NOT_SUPPORTED, con->http.hostname,
++ con->request->request.op.operation_id,
++ ippOpString(con->request->request.op.operation_id));
++
++ send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED,
++ _("%s not supported!"),
++ ippOpString(con->request->request.op.operation_id));
++ break;
++ }
++ }
++ }
++ }
++
++ if (con->response)
++ {
++ /*
++ * Sending data from the scheduler...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "cupsdProcessIPPRequest: %d status_code=%x (%s)",
++ con->http.fd, con->response->request.status.status_code,
++ ippErrorString(con->response->request.status.status_code));
++
++ if (cupsdSendHeader(con, HTTP_OK, "application/ipp"))
++ {
++#ifdef CUPSD_USE_CHUNKING
++ /*
++ * Because older versions of CUPS (1.1.17 and older) and some IPP
++ * clients do not implement chunking properly, we cannot use
++ * chunking by default. This may become the default in future
++ * CUPS releases, or we might add a configuration directive for
++ * it.
++ */
++
++ if (con->http.version == HTTP_1_1)
++ {
++ if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
++ return (0);
++
++ if (cupsdFlushHeader(con) < 0)
++ return (0);
++
++ con->http.data_encoding = HTTP_ENCODE_CHUNKED;
++ }
++ else
++#endif /* CUPSD_USE_CHUNKING */
++ {
++ size_t length; /* Length of response */
++
++
++ length = ippLength(con->response);
++
++ if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
++ CUPS_LLCAST length) < 0)
++ return (0);
++
++ if (cupsdFlushHeader(con) < 0)
++ return (0);
++
++ con->http.data_encoding = HTTP_ENCODE_LENGTH;
++ con->http.data_remaining = length;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "cupsdProcessIPPRequest: Adding fd %d to OutputSet...",
++ con->http.fd);
++
++ FD_SET(con->http.fd, OutputSet);
++
++ /*
++ * Tell the caller the response header was sent successfully...
++ */
++
++ return (1);
++ }
++ else
++ {
++ /*
++ * Tell the caller the response header could not be sent...
++ */
++
++ return (0);
++ }
++ }
++ else
++ {
++ /*
++ * Sending data from a subprocess like cups-deviced; tell the caller
++ * everything is A-OK so far...
++ */
++
++ return (1);
++ }
++}
++
++
++/*
++ * 'accept_jobs()' - Accept print jobs to a printer.
++ */
++
++static void
++accept_jobs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer or class URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ const char *name; /* Printer name */
++ cupsd_printer_t *printer; /* Printer data */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Accept jobs sent to the printer...
++ */
++
++ printer->accepting = 1;
++ printer->state_message[0] = '\0';
++
++ cupsdAddPrinterHistory(printer);
++
++ if (dtype & CUPS_PRINTER_CLASS)
++ cupsdSaveAllClasses();
++ else
++ cupsdSaveAllPrinters();
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" now accepting jobs (\"%s\").", name,
++ get_username(con));
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'add_class()' - Add a class to the system.
++ */
++
++static void
++add_class(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - URI of class */
++{
++ http_status_t status; /* Policy status */
++ int i; /* Looping var */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *pclass, /* Class */
++ *member; /* Member printer/class */
++ cups_ptype_t dtype; /* Destination type */
++ ipp_attribute_t *attr; /* Printer attribute */
++ int modify; /* Non-zero if we just modified */
++ char newname[IPP_MAX_NAME]; /* New class name */
++ int need_restart_job; /* Need to restart job? */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Do we have a valid URI?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++
++ if (strncmp(resource, "/classes/", 9) || strlen(resource) == 9)
++ {
++ /*
++ * No, return an error...
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The printer-uri must be of the form "
++ "\"ipp://HOSTNAME/classes/CLASSNAME\"."));
++ return;
++ }
++
++ /*
++ * Do we have a valid printer name?
++ */
++
++ if (!validate_name(resource + 9))
++ {
++ /*
++ * No, return an error...
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The printer-uri \"%s\" contains invalid characters."),
++ uri->values[0].string.text);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * See if the class already exists; if not, create a new class...
++ */
++
++ if ((pclass = cupsdFindClass(resource + 9)) == NULL)
++ {
++ /*
++ * Class doesn't exist; see if we have a printer of the same name...
++ */
++
++ if ((pclass = cupsdFindPrinter(resource + 9)) != NULL &&
++ !(pclass->type & CUPS_PRINTER_REMOTE))
++ {
++ /*
++ * Yes, return an error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("A printer named \"%s\" already exists!"),
++ resource + 9);
++ return;
++ }
++
++ /*
++ * No, add the pclass...
++ */
++
++ pclass = cupsdAddClass(resource + 9);
++ modify = 0;
++ }
++ else if (pclass->type & CUPS_PRINTER_IMPLICIT)
++ {
++ /*
++ * Rename the implicit class to "AnyClass" or remove it...
++ */
++
++ if (ImplicitAnyClasses)
++ {
++ snprintf(newname, sizeof(newname), "Any%s", resource + 9);
++ cupsdRenamePrinter(pclass, newname);
++ }
++ else
++ cupsdDeletePrinter(pclass, 1);
++
++ /*
++ * Add the class as a new local class...
++ */
++
++ pclass = cupsdAddClass(resource + 9);
++ modify = 0;
++ }
++ else if (pclass->type & CUPS_PRINTER_REMOTE)
++ {
++ /*
++ * Rename the remote class to "Class"...
++ */
++
++ snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname);
++ cupsdRenamePrinter(pclass, newname);
++
++ /*
++ * Add the class as a new local class...
++ */
++
++ pclass = cupsdAddClass(resource + 9);
++ modify = 0;
++ }
++ else
++ modify = 1;
++
++ /*
++ * Look for attributes and copy them over as needed...
++ */
++
++ need_restart_job = 0;
++
++ if ((attr = ippFindAttribute(con->request, "printer-location",
++ IPP_TAG_TEXT)) != NULL)
++ cupsdSetString(&pclass->location, attr->values[0].string.text);
++
++ if ((attr = ippFindAttribute(con->request, "printer-info",
++ IPP_TAG_TEXT)) != NULL)
++ cupsdSetString(&pclass->info, attr->values[0].string.text);
++
++ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
++ IPP_TAG_BOOLEAN)) != NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
++ pclass->name, attr->values[0].boolean, pclass->accepting);
++
++ pclass->accepting = attr->values[0].boolean;
++ cupsdAddPrinterHistory(pclass);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "printer-is-shared",
++ IPP_TAG_BOOLEAN)) != NULL)
++ {
++ if (pclass->shared && !attr->values[0].boolean)
++ cupsdSendBrowseDelete(pclass);
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Setting %s printer-is-shared to %d (was %d.)",
++ pclass->name, attr->values[0].boolean, pclass->shared);
++
++ pclass->shared = attr->values[0].boolean;
++ }
++
++ if ((attr = ippFindAttribute(con->request, "printer-state",
++ IPP_TAG_ENUM)) != NULL)
++ {
++ if (attr->values[0].integer != IPP_PRINTER_IDLE &&
++ attr->values[0].integer != IPP_PRINTER_STOPPED)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Attempt to set %s printer-state to bad value %d!"),
++ pclass->name, attr->values[0].integer);
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
++ attr->values[0].integer, pclass->state);
++
++ if (attr->values[0].integer == IPP_PRINTER_STOPPED)
++ cupsdStopPrinter(pclass, 0);
++ else
++ {
++ cupsdSetPrinterState(pclass, (ipp_pstate_t)(attr->values[0].integer), 0);
++ need_restart_job = 1;
++ }
++ }
++ if ((attr = ippFindAttribute(con->request, "printer-state-message",
++ IPP_TAG_TEXT)) != NULL)
++ {
++ strlcpy(pclass->state_message, attr->values[0].string.text,
++ sizeof(pclass->state_message));
++ cupsdAddPrinterHistory(pclass);
++ }
++ if ((attr = ippFindAttribute(con->request, "member-uris",
++ IPP_TAG_URI)) != NULL)
++ {
++ /*
++ * Clear the printer array as needed...
++ */
++
++ need_restart_job = 1;
++
++ if (pclass->num_printers > 0)
++ {
++ free(pclass->printers);
++ pclass->num_printers = 0;
++ }
++
++ /*
++ * Add each printer or class that is listed...
++ */
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ /*
++ * Search for the printer or class URI...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!cupsdValidateDest(host, resource, &dtype, &member))
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Add it to the class...
++ */
++
++ cupsdAddPrinterToClass(pclass, member);
++ }
++ }
++
++ set_printer_defaults(con, pclass);
++
++ /*
++ * Update the printer class attributes and return...
++ */
++
++ cupsdSetPrinterAttrs(pclass);
++ cupsdSaveAllClasses();
++
++ if (need_restart_job && pclass->job)
++ {
++ cupsd_job_t *job;
++
++ /*
++ * Stop the current job and then restart it below...
++ */
++
++ job = (cupsd_job_t *)pclass->job;
++
++ cupsdStopJob(job, 1);
++
++ job->state->values[0].integer = IPP_JOB_PENDING;
++ job->state_value = IPP_JOB_PENDING;
++ }
++
++ if (need_restart_job)
++ cupsdCheckJobs();
++
++ cupsdWritePrintcap();
++
++ if (modify)
++ {
++ cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, pclass, NULL,
++ "Class \"%s\" modified by \"%s\".", pclass->name,
++ get_username(con));
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" modified by \"%s\".",
++ pclass->name, get_username(con));
++ }
++ else
++ {
++ cupsdAddPrinterHistory(pclass);
++
++ cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, pclass, NULL,
++ "New class \"%s\" added by \"%s\".", pclass->name,
++ get_username(con));
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "New class \"%s\" added by \"%s\".",
++ pclass->name, get_username(con));
++ }
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'add_file()' - Add a file to a job.
++ */
++
++static int /* O - 0 on success, -1 on error */
++add_file(cupsd_client_t *con, /* I - Connection to client */
++ cupsd_job_t *job, /* I - Job to add to */
++ mime_type_t *filetype, /* I - Type of file */
++ int compression) /* I - Compression */
++{
++ mime_type_t **filetypes; /* New filetypes array... */
++ int *compressions; /* New compressions array... */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "add_file(con=%p[%d], job=%d, filetype=%s/%s, compression=%d)",
++ con, con->http.fd, job->id, filetype->super, filetype->type,
++ compression);
++
++ /*
++ * Add the file to the job...
++ */
++
++ if (job->num_files == 0)
++ {
++ compressions = (int *)malloc(sizeof(int));
++ filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
++ }
++ else
++ {
++ compressions = (int *)realloc(job->compressions,
++ (job->num_files + 1) * sizeof(int));
++ filetypes = (mime_type_t **)realloc(job->filetypes,
++ (job->num_files + 1) *
++ sizeof(mime_type_t *));
++ }
++
++ if (!compressions || !filetypes)
++ {
++ cupsdCancelJob(job, 1, IPP_JOB_ABORTED);
++
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("Unable to allocate memory for file types!"));
++ return (-1);
++ }
++
++ job->compressions = compressions;
++ job->compressions[job->num_files] = compression;
++ job->filetypes = filetypes;
++ job->filetypes[job->num_files] = filetype;
++
++ job->num_files ++;
++
++ return (0);
++}
++
++
++/*
++ * 'add_job()' - Add a job to a print queue.
++ */
++
++static cupsd_job_t * /* O - Job object */
++add_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri, /* I - printer-uri */
++ cupsd_printer_t **dprinter, /* I - Destination printer */
++ mime_type_t *filetype) /* I - First print file type, if any */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ const char *dest; /* Destination */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ const char *val; /* Default option value */
++ int priority; /* Job priority */
++ char *title; /* Job name/title */
++ cupsd_job_t *job; /* Current job */
++ char job_uri[HTTP_MAX_URI], /* Job URI */
++ method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *printer; /* Printer data */
++ int kbytes; /* Size of print file */
++ int i; /* Looping var */
++ int lowerpagerange; /* Page range bound */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return (NULL);
++ }
++
++ if (dprinter)
++ *dprinter = printer;
++
++ /*
++ * Check remote printing to non-shared printer...
++ */
++
++ if (!printer->shared &&
++ strcasecmp(con->http.hostname, "localhost") &&
++ strcasecmp(con->http.hostname, ServerName))
++ {
++ send_ipp_status(con, IPP_NOT_AUTHORIZED,
++ _("The printer or class is not shared!"));
++ return (NULL);
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return (NULL);
++ }
++ else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0])
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return (NULL);
++ }
++
++ /*
++ * See if the printer is accepting jobs...
++ */
++
++ if (!printer->accepting)
++ {
++ send_ipp_status(con, IPP_NOT_ACCEPTING,
++ _("Destination \"%s\" is not accepting jobs."),
++ dest);
++ return (NULL);
++ }
++
++ /*
++ * Validate job template attributes; for now just document-format,
++ * copies, and page-ranges...
++ */
++
++ if (filetype && printer->filetypes &&
++ !cupsArrayFind(printer->filetypes, filetype))
++ {
++ char mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
++ /* MIME media type string */
++
++
++ snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
++ filetype->type);
++
++ send_ipp_status(con, IPP_DOCUMENT_FORMAT,
++ _("Unsupported format \'%s\'!"), mimetype);
++
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
++ "document-format", NULL, mimetype);
++
++ return (NULL);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "copies",
++ IPP_TAG_INTEGER)) != NULL)
++ {
++ if (attr->values[0].integer < 1 || attr->values[0].integer > MaxCopies)
++ {
++ send_ipp_status(con, IPP_ATTRIBUTES, _("Bad copies value %d."),
++ attr->values[0].integer);
++ ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
++ "copies", attr->values[0].integer);
++ return (NULL);
++ }
++ }
++
++ if ((attr = ippFindAttribute(con->request, "page-ranges",
++ IPP_TAG_RANGE)) != NULL)
++ {
++ for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
++ {
++ if (attr->values[i].range.lower < lowerpagerange ||
++ attr->values[i].range.lower > attr->values[i].range.upper)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad page-ranges values %d-%d."),
++ attr->values[i].range.lower,
++ attr->values[i].range.upper);
++ return (NULL);
++ }
++
++ lowerpagerange = attr->values[i].range.upper + 1;
++ }
++ }
++
++ /*
++ * Make sure we aren't over our limit...
++ */
++
++ if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
++ cupsdCleanJobs();
++
++ if (cupsArrayCount(Jobs) >= MaxJobs && MaxJobs)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Too many active jobs."));
++ return (NULL);
++ }
++
++ if (!check_quotas(con, printer))
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
++ return (NULL);
++ }
++
++ /*
++ * Create the job and set things up...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-priority",
++ IPP_TAG_INTEGER)) != NULL)
++ priority = attr->values[0].integer;
++ else
++ {
++ if ((val = cupsGetOption("job-priority", printer->num_options,
++ printer->options)) != NULL)
++ priority = atoi(val);
++ else
++ priority = 50;
++
++ ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
++ priority);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "job-name",
++ IPP_TAG_NAME)) != NULL)
++ title = attr->values[0].string.text;
++ else
++ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
++ title = "Untitled");
++
++ if ((job = cupsdAddJob(priority, printer->name)) == NULL)
++ {
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("Unable to add job for destination \"%s\"!"), dest);
++ return (NULL);
++ }
++
++ job->dtype = dtype;
++ job->attrs = con->request;
++ con->request = ippNewRequest(job->attrs->request.op.operation_id);
++
++ add_job_uuid(con, job);
++ apply_printer_defaults(printer, job);
++
++ attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
++
++ if (con->username[0])
++ {
++ cupsdSetString(&job->username, con->username);
++
++ if (attr)
++ cupsdSetString(&attr->values[0].string.text, con->username);
++
++ save_auth_info(con, job);
++ }
++ else if (attr)
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "add_job: requesting-user-name=\"%s\"",
++ attr->values[0].string.text);
++
++ cupsdSetString(&job->username, attr->values[0].string.text);
++ }
++ else
++ cupsdSetString(&job->username, "anonymous");
++
++ if (!attr)
++ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
++ "job-originating-user-name", NULL, job->username);
++ else
++ {
++ attr->group_tag = IPP_TAG_JOB;
++ _cupsStrFree(attr->name);
++ attr->name = _cupsStrAlloc("job-originating-user-name");
++ }
++
++ if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
++ IPP_TAG_ZERO)) != NULL)
++ {
++ /*
++ * Request contains a job-originating-host-name attribute; validate it...
++ */
++
++ if (attr->value_tag != IPP_TAG_NAME ||
++ attr->num_values != 1 ||
++ strcmp(con->http.hostname, "localhost"))
++ {
++ /*
++ * Can't override the value if we aren't connected via localhost.
++ * Also, we can only have 1 value and it must be a name value.
++ */
++
++ switch (attr->value_tag)
++ {
++ case IPP_TAG_STRING :
++ case IPP_TAG_TEXTLANG :
++ case IPP_TAG_NAMELANG :
++ case IPP_TAG_TEXT :
++ case IPP_TAG_NAME :
++ case IPP_TAG_KEYWORD :
++ case IPP_TAG_URI :
++ case IPP_TAG_URISCHEME :
++ case IPP_TAG_CHARSET :
++ case IPP_TAG_LANGUAGE :
++ case IPP_TAG_MIMETYPE :
++ /*
++ * Free old strings...
++ */
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ _cupsStrFree(attr->values[i].string.text);
++ attr->values[i].string.text = NULL;
++ if (attr->values[i].string.charset)
++ {
++ _cupsStrFree(attr->values[i].string.charset);
++ attr->values[i].string.charset = NULL;
++ }
++ }
++
++ default :
++ break;
++ }
++
++ /*
++ * Use the default connection hostname instead...
++ */
++
++ attr->value_tag = IPP_TAG_NAME;
++ attr->num_values = 1;
++ attr->values[0].string.text = _cupsStrAlloc(con->http.hostname);
++ }
++
++ attr->group_tag = IPP_TAG_JOB;
++ }
++ else
++ {
++ /*
++ * No job-originating-host-name attribute, so use the hostname from
++ * the connection...
++ */
++
++ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
++ "job-originating-host-name", NULL, con->http.hostname);
++ }
++
++ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
++ time(NULL));
++ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
++ "time-at-processing", 0);
++ attr->value_tag = IPP_TAG_NOVALUE;
++ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
++ "time-at-completed", 0);
++ attr->value_tag = IPP_TAG_NOVALUE;
++
++ /*
++ * Add remaining job attributes...
++ */
++
++ ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
++ job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
++ "job-state", IPP_JOB_STOPPED);
++ job->state_value = (ipp_jstate_t)job->state->values[0].integer;
++ job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
++ "job-media-sheets-completed", 0);
++ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
++ printer->uri);
++ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
++ title);
++
++ if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
++ IPP_TAG_INTEGER)) != NULL)
++ attr->values[0].integer = 0;
++ else
++ attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
++ "job-k-octets", 0);
++
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++ if (!attr)
++ {
++ if ((val = cupsGetOption("job-hold-until", printer->num_options,
++ printer->options)) == NULL)
++ val = "no-hold";
++
++ attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-hold-until", NULL, val);
++ }
++ if (attr && strcmp(attr->values[0].string.text, "no-hold") &&
++ !(printer->type & CUPS_PRINTER_REMOTE))
++ {
++ /*
++ * Hold job until specified time...
++ */
++
++ cupsdSetJobHoldUntil(job, attr->values[0].string.text);
++
++ job->state->values[0].integer = IPP_JOB_HELD;
++ job->state_value = IPP_JOB_HELD;
++ }
++ else if (job->attrs->request.op.operation_id == IPP_CREATE_JOB)
++ {
++ job->hold_until = time(NULL) + 60;
++ job->state->values[0].integer = IPP_JOB_HELD;
++ job->state_value = IPP_JOB_HELD;
++ }
++ else
++ {
++ job->state->values[0].integer = IPP_JOB_PENDING;
++ job->state_value = IPP_JOB_PENDING;
++ }
++
++ if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) ||
++ Classification)
++ {
++ /*
++ * Add job sheets options...
++ */
++
++ if ((attr = ippFindAttribute(job->attrs, "job-sheets",
++ IPP_TAG_ZERO)) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Adding default job-sheets values \"%s,%s\"...",
++ printer->job_sheets[0], printer->job_sheets[1]);
++
++ attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
++ 2, NULL, NULL);
++ attr->values[0].string.text = _cupsStrAlloc(printer->job_sheets[0]);
++ attr->values[1].string.text = _cupsStrAlloc(printer->job_sheets[1]);
++ }
++
++ job->job_sheets = attr;
++
++ /*
++ * Enforce classification level if set...
++ */
++
++ if (Classification)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Classification=\"%s\", ClassifyOverride=%d",
++ Classification ? Classification : "(null)",
++ ClassifyOverride);
++
++ if (ClassifyOverride)
++ {
++ if (!strcmp(attr->values[0].string.text, "none") &&
++ (attr->num_values == 1 ||
++ !strcmp(attr->values[1].string.text, "none")))
++ {
++ /*
++ * Force the leading banner to have the classification on it...
++ */
++
++ cupsdSetString(&attr->values[0].string.text, Classification);
++
++ cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED "
++ "job-sheets=\"%s,none\", "
++ "job-originating-user-name=\"%s\"",
++ job->id, Classification, job->username);
++ }
++ else if (attr->num_values == 2 &&
++ strcmp(attr->values[0].string.text,
++ attr->values[1].string.text) &&
++ strcmp(attr->values[0].string.text, "none") &&
++ strcmp(attr->values[1].string.text, "none"))
++ {
++ /*
++ * Can't put two different security markings on the same document!
++ */
++
++ cupsdSetString(&attr->values[1].string.text, attr->values[0].string.text);
++
++ cupsdLogMessage(CUPSD_LOG_NOTICE, "[Job %d] CLASSIFICATION FORCED "
++ "job-sheets=\"%s,%s\", "
++ "job-originating-user-name=\"%s\"",
++ job->id, attr->values[0].string.text,
++ attr->values[1].string.text, job->username);
++ }
++ else if (strcmp(attr->values[0].string.text, Classification) &&
++ strcmp(attr->values[0].string.text, "none") &&
++ (attr->num_values == 1 ||
++ (strcmp(attr->values[1].string.text, Classification) &&
++ strcmp(attr->values[1].string.text, "none"))))
++ {
++ if (attr->num_values == 1)
++ cupsdLogMessage(CUPSD_LOG_NOTICE,
++ "[Job %d] CLASSIFICATION OVERRIDDEN "
++ "job-sheets=\"%s\", "
++ "job-originating-user-name=\"%s\"",
++ job->id, attr->values[0].string.text, job->username);
++ else
++ cupsdLogMessage(CUPSD_LOG_NOTICE,
++ "[Job %d] CLASSIFICATION OVERRIDDEN "
++ "job-sheets=\"%s,%s\",fffff "
++ "job-originating-user-name=\"%s\"",
++ job->id, attr->values[0].string.text,
++ attr->values[1].string.text, job->username);
++ }
++ }
++ else if (strcmp(attr->values[0].string.text, Classification) &&
++ (attr->num_values == 1 ||
++ strcmp(attr->values[1].string.text, Classification)))
++ {
++ /*
++ * Force the banner to have the classification on it...
++ */
++
++ if (attr->num_values > 1 &&
++ !strcmp(attr->values[0].string.text, attr->values[1].string.text))
++ {
++ cupsdSetString(&(attr->values[0].string.text), Classification);
++ cupsdSetString(&(attr->values[1].string.text), Classification);
++ }
++ else
++ {
++ if (attr->num_values == 1 ||
++ strcmp(attr->values[0].string.text, "none"))
++ cupsdSetString(&(attr->values[0].string.text), Classification);
++
++ if (attr->num_values > 1 &&
++ strcmp(attr->values[1].string.text, "none"))
++ cupsdSetString(&(attr->values[1].string.text), Classification);
++ }
++
++ if (attr->num_values > 1)
++ cupsdLogMessage(CUPSD_LOG_NOTICE,
++ "[Job %d] CLASSIFICATION FORCED "
++ "job-sheets=\"%s,%s\", "
++ "job-originating-user-name=\"%s\"",
++ job->id, attr->values[0].string.text,
++ attr->values[1].string.text, job->username);
++ else
++ cupsdLogMessage(CUPSD_LOG_NOTICE,
++ "[Job %d] CLASSIFICATION FORCED "
++ "job-sheets=\"%s\", "
++ "job-originating-user-name=\"%s\"",
++ job->id, Classification, job->username);
++ }
++ }
++
++ /*
++ * See if we need to add the starting sheet...
++ */
++
++ if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)))
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Adding start banner page \"%s\" to job %d.",
++ attr->values[0].string.text, job->id);
++
++ kbytes = copy_banner(con, job, attr->values[0].string.text);
++
++ cupsdUpdateQuota(printer, job->username, 0, kbytes);
++ }
++ }
++ else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
++ IPP_TAG_ZERO)) != NULL)
++ job->sheets = attr;
++
++ /*
++ * Fill in the response info...
++ */
++
++ snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
++ LocalPort, job->id);
++
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
++ job_uri);
++
++ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
++
++ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
++ job->state_value);
++ add_job_state_reasons(con, job);
++
++ con->response->request.status.status_code = IPP_OK;
++
++ /*
++ * Add any job subscriptions...
++ */
++
++ add_job_subscriptions(con, job);
++
++ /*
++ * Set all but the first two attributes to the job attributes group...
++ */
++
++ for (attr = job->attrs->attrs->next->next; attr; attr = attr->next)
++ attr->group_tag = IPP_TAG_JOB;
++
++ /*
++ * Fire the "job created" event...
++ */
++
++ cupsdAddEvent(CUPSD_EVENT_JOB_CREATED, printer, job, "Job created.");
++
++ /*
++ * Return the new job...
++ */
++
++ return (job);
++}
++
++
++/*
++ * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
++ * upon the job and printer state...
++ */
++
++static void
++add_job_state_reasons(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job) /* I - Job info */
++{
++ cupsd_printer_t *dest; /* Destination printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
++ con, con->http.fd, job ? job->id : 0);
++
++ switch (job ? job->state_value : IPP_JOB_CANCELED)
++ {
++ case IPP_JOB_PENDING :
++ dest = cupsdFindDest(job->dest);
++
++ if (dest && dest->state == IPP_PRINTER_STOPPED)
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "printer-stopped");
++ else
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "none");
++ break;
++
++ case IPP_JOB_HELD :
++ if (ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD) != NULL ||
++ ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_NAME) != NULL)
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-hold-until-specified");
++ else
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-incoming");
++ break;
++
++ case IPP_JOB_PROCESSING :
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-printing");
++ break;
++
++ case IPP_JOB_STOPPED :
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-stopped");
++ break;
++
++ case IPP_JOB_CANCELED :
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-canceled-by-user");
++ break;
++
++ case IPP_JOB_ABORTED :
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "aborted-by-system");
++ break;
++
++ case IPP_JOB_COMPLETED :
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
++ "job-state-reasons", NULL, "job-completed-successfully");
++ break;
++ }
++}
++
++
++/*
++ * 'add_job_subscriptions()' - Add any subcriptions for a job.
++ */
++
++static void
++add_job_subscriptions(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job) /* I - Newly created job */
++{
++ int i; /* Looping var */
++ ipp_attribute_t *prev, /* Previous attribute */
++ *next, /* Next attribute */
++ *attr; /* Current attribute */
++ cupsd_subscription_t *sub; /* Subscription object */
++ const char *recipient, /* notify-recipient-uri */
++ *pullmethod; /* notify-pull-method */
++ ipp_attribute_t *user_data; /* notify-user-data */
++ int interval; /* notify-time-interval */
++ unsigned mask; /* notify-events */
++
++
++ /*
++ * Find the first subscription group attribute; return if we have
++ * none...
++ */
++
++ for (attr = job->attrs->attrs, prev = NULL;
++ attr;
++ prev = attr, attr = attr->next)
++ if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
++ break;
++
++ if (!attr)
++ return;
++
++ /*
++ * Process the subscription attributes in the request...
++ */
++
++ while (attr)
++ {
++ recipient = NULL;
++ pullmethod = NULL;
++ user_data = NULL;
++ interval = 0;
++ mask = CUPSD_EVENT_NONE;
++
++ while (attr && attr->group_tag != IPP_TAG_ZERO)
++ {
++ if (!strcmp(attr->name, "notify-recipient") &&
++ attr->value_tag == IPP_TAG_URI)
++ recipient = attr->values[0].string.text;
++ else if (!strcmp(attr->name, "notify-pull-method") &&
++ attr->value_tag == IPP_TAG_KEYWORD)
++ pullmethod = attr->values[0].string.text;
++ else if (!strcmp(attr->name, "notify-charset") &&
++ attr->value_tag == IPP_TAG_CHARSET &&
++ strcmp(attr->values[0].string.text, "us-ascii") &&
++ strcmp(attr->values[0].string.text, "utf-8"))
++ {
++ send_ipp_status(con, IPP_CHARSET,
++ _("Character set \"%s\" not supported!"),
++ attr->values[0].string.text);
++ return;
++ }
++ else if (!strcmp(attr->name, "notify-natural-language") &&
++ (attr->value_tag != IPP_TAG_LANGUAGE ||
++ strcmp(attr->values[0].string.text, DefaultLanguage)))
++ {
++ send_ipp_status(con, IPP_CHARSET,
++ _("Language \"%s\" not supported!"),
++ attr->values[0].string.text);
++ return;
++ }
++ else if (!strcmp(attr->name, "notify-user-data") &&
++ attr->value_tag == IPP_TAG_STRING)
++ {
++ if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
++ {
++ send_ipp_status(con, IPP_REQUEST_VALUE,
++ _("The notify-user-data value is too large "
++ "(%d > 63 octets)!"),
++ attr->values[0].unknown.length);
++ return;
++ }
++
++ user_data = attr;
++ }
++ else if (!strcmp(attr->name, "notify-events") &&
++ attr->value_tag == IPP_TAG_KEYWORD)
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ mask |= cupsdEventValue(attr->values[i].string.text);
++ }
++ else if (!strcmp(attr->name, "notify-lease-duration"))
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The notify-lease-duration attribute cannot be "
++ "used with job subscriptions."));
++ return;
++ }
++ else if (!strcmp(attr->name, "notify-time-interval") &&
++ attr->value_tag == IPP_TAG_INTEGER)
++ interval = attr->values[0].integer;
++
++ attr = attr->next;
++ }
++
++ if (!recipient && !pullmethod)
++ break;
++
++ if (mask == CUPSD_EVENT_NONE)
++ mask = CUPSD_EVENT_JOB_COMPLETED;
++
++ sub = cupsdAddSubscription(mask, cupsdFindDest(job->dest), job, recipient,
++ 0);
++
++ sub->interval = interval;
++
++ cupsdSetString(&sub->owner, job->username);
++
++ if (user_data)
++ {
++ sub->user_data_len = user_data->values[0].unknown.length;
++ memcpy(sub->user_data, user_data->values[0].unknown.data,
++ sub->user_data_len);
++ }
++
++ ippAddSeparator(con->response);
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-subscription-id", sub->id);
++
++ if (attr)
++ attr = attr->next;
++ }
++
++ cupsdSaveAllSubscriptions();
++
++ /*
++ * Remove all of the subscription attributes from the job request...
++ */
++
++ for (attr = job->attrs->attrs, prev = NULL; attr; attr = next)
++ {
++ next = attr->next;
++
++ if (attr->group_tag == IPP_TAG_SUBSCRIPTION ||
++ attr->group_tag == IPP_TAG_ZERO)
++ {
++ /*
++ * Free and remove this attribute...
++ */
++
++ _ippFreeAttr(attr);
++
++ if (prev)
++ prev->next = next;
++ else
++ job->attrs->attrs = next;
++ }
++ else
++ prev = attr;
++ }
++
++ job->attrs->last = prev;
++ job->attrs->current = prev;
++}
++
++
++/*
++ * 'add_job_uuid()' - Add job-uuid attribute to a job.
++ *
++ * See RFC 4122 for the definition of UUIDs and the format.
++ */
++
++static void
++add_job_uuid(cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job) /* I - Job */
++{
++ char uuid[1024]; /* job-uuid string */
++ _cups_md5_state_t md5state; /* MD5 state */
++ unsigned char md5sum[16]; /* MD5 digest/sum */
++
++
++ /*
++ * First see if the job already has a job-uuid attribute; if so, return...
++ */
++
++ if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
++ return;
++
++ /*
++ * No job-uuid attribute, so build a version 3 UUID with the local job
++ * ID at the end; see RFC 4122 for details. Start with the MD5 sum of
++ * the ServerName, server name and port that the client connected to,
++ * and local job ID...
++ */
++
++ snprintf(uuid, sizeof(uuid), "%s:%s:%d:%d", ServerName, con->servername,
++ con->serverport, job->id);
++
++ _cupsMD5Init(&md5state);
++ _cupsMD5Append(&md5state, (unsigned char *)uuid, strlen(uuid));
++ _cupsMD5Finish(&md5state, md5sum);
++
++ /*
++ * Format the UUID URI using the MD5 sum and job ID.
++ */
++
++ snprintf(uuid, sizeof(uuid),
++ "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
++ "%02x%02x%02x%02x%02x%02x",
++ md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
++ (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
++ md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
++ md5sum[14], md5sum[15]);
++
++ ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
++}
++
++
++/*
++ * 'add_printer()' - Add a printer to the system.
++ */
++
++static void
++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 */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *printer; /* Printer/class */
++ ipp_attribute_t *attr; /* Printer attribute */
++ cups_file_t *fp; /* Script/PPD file */
++ char line[1024]; /* Line from file... */
++ char srcfile[1024], /* Source Script/PPD file */
++ dstfile[1024]; /* Destination Script/PPD file */
++ int modify; /* Non-zero if we are modifying */
++ char newname[IPP_MAX_NAME]; /* New printer name */
++ int need_restart_job; /* Need to restart job? */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Do we have a valid URI?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/printers/", 10) || strlen(resource) == 10)
++ {
++ /*
++ * No, return an error...
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The printer-uri must be of the form "
++ "\"ipp://HOSTNAME/printers/PRINTERNAME\"."));
++ return;
++ }
++
++ /*
++ * Do we have a valid printer name?
++ */
++
++ if (!validate_name(resource + 10))
++ {
++ /*
++ * No, return an error...
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The printer-uri \"%s\" contains invalid characters."),
++ uri->values[0].string.text);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * See if the printer already exists; if not, create a new printer...
++ */
++
++ if ((printer = cupsdFindPrinter(resource + 10)) == NULL)
++ {
++ /*
++ * Printer doesn't exist; see if we have a class of the same name...
++ */
++
++ if ((printer = cupsdFindClass(resource + 10)) != NULL &&
++ !(printer->type & CUPS_PRINTER_REMOTE))
++ {
++ /*
++ * Yes, return an error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("A class named \"%s\" already exists!"),
++ resource + 10);
++ return;
++ }
++
++ /*
++ * No, add the printer...
++ */
++
++ printer = cupsdAddPrinter(resource + 10);
++ modify = 0;
++ }
++ else if (printer->type & CUPS_PRINTER_IMPLICIT)
++ {
++ /*
++ * Rename the implicit printer to "AnyPrinter" or delete it...
++ */
++
++ if (ImplicitAnyClasses)
++ {
++ snprintf(newname, sizeof(newname), "Any%s", resource + 10);
++ cupsdRenamePrinter(printer, newname);
++ }
++ else
++ cupsdDeletePrinter(printer, 1);
++
++ /*
++ * Add the printer as a new local printer...
++ */
++
++ printer = cupsdAddPrinter(resource + 10);
++ modify = 0;
++ }
++ else if (printer->type & CUPS_PRINTER_REMOTE)
++ {
++ /*
++ * Rename the remote printer to "Printer at server"...
++ */
++
++ snprintf(newname, sizeof(newname), "%s@%s", resource + 10,
++ printer->hostname);
++ cupsdRenamePrinter(printer, newname);
++
++ /*
++ * Add the printer as a new local printer...
++ */
++
++ printer = cupsdAddPrinter(resource + 10);
++ modify = 0;
++ }
++ else
++ modify = 1;
++
++ /*
++ * Look for attributes and copy them over as needed...
++ */
++
++ need_restart_job = 0;
++
++ if ((attr = ippFindAttribute(con->request, "printer-location",
++ IPP_TAG_TEXT)) != NULL)
++ cupsdSetString(&printer->location, attr->values[0].string.text);
++
++ if ((attr = ippFindAttribute(con->request, "printer-info",
++ IPP_TAG_TEXT)) != NULL)
++ cupsdSetString(&printer->info, attr->values[0].string.text);
++
++ if ((attr = ippFindAttribute(con->request, "device-uri",
++ IPP_TAG_URI)) != NULL)
++ {
++ /*
++ * Do we have a valid device URI?
++ */
++
++ need_restart_job = 1;
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!strcmp(method, "file"))
++ {
++ /*
++ * See if the administrator has enabled file devices...
++ */
++
++ if (!FileDevice && strcmp(resource, "/dev/null"))
++ {
++ /*
++ * File devices are disabled and the URL is not file:/dev/null...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("File device URIs have been disabled! "
++ "To enable, see the FileDevice directive in "
++ "\"%s/cupsd.conf\"."),
++ ServerRoot);
++ return;
++ }
++ }
++ else
++ {
++ /*
++ * See if the backend exists and is executable...
++ */
++
++ snprintf(srcfile, sizeof(srcfile), "%s/backend/%s", ServerBin, method);
++ if (access(srcfile, X_OK))
++ {
++ /*
++ * Could not find device in list!
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"!"),
++ attr->values[0].string.text);
++ return;
++ }
++ }
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Setting %s device-uri to \"%s\" (was \"%s\".)",
++ printer->name,
++ cupsdSanitizeURI(attr->values[0].string.text, line,
++ sizeof(line)),
++ cupsdSanitizeURI(printer->device_uri, resource,
++ sizeof(resource)));
++
++ cupsdSetString(&printer->device_uri, attr->values[0].string.text);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "port-monitor",
++ IPP_TAG_KEYWORD)) != NULL)
++ {
++ ipp_attribute_t *supported; /* port-monitor-supported attribute */
++
++
++ need_restart_job = 1;
++
++ supported = ippFindAttribute(printer->attrs, "port-monitor-supported",
++ IPP_TAG_KEYWORD);
++ for (i = 0; i < supported->num_values; i ++)
++ if (!strcmp(supported->values[i].string.text,
++ attr->values[0].string.text))
++ break;
++
++ if (i >= supported->num_values)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"!"),
++ attr->values[0].string.text);
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Setting %s port-monitor to \"%s\" (was \"%s\".)",
++ printer->name, attr->values[0].string.text,
++ printer->port_monitor);
++
++ if (strcmp(attr->values[0].string.text, "none"))
++ cupsdSetString(&printer->port_monitor, attr->values[0].string.text);
++ else
++ cupsdClearString(&printer->port_monitor);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs",
++ IPP_TAG_BOOLEAN)) != NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Setting %s printer-is-accepting-jobs to %d (was %d.)",
++ printer->name, attr->values[0].boolean, printer->accepting);
++
++ printer->accepting = attr->values[0].boolean;
++ cupsdAddPrinterHistory(printer);
++ }
++
++ if ((attr = ippFindAttribute(con->request, "printer-is-shared",
++ IPP_TAG_BOOLEAN)) != NULL)
++ {
++ if (printer->shared && !attr->values[0].boolean)
++ cupsdSendBrowseDelete(printer);
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Setting %s printer-is-shared to %d (was %d.)",
++ printer->name, attr->values[0].boolean, printer->shared);
++
++ printer->shared = attr->values[0].boolean;
++ }
++
++ if ((attr = ippFindAttribute(con->request, "printer-state",
++ IPP_TAG_ENUM)) != NULL)
++ {
++ if (attr->values[0].integer != IPP_PRINTER_IDLE &&
++ attr->values[0].integer != IPP_PRINTER_STOPPED)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d!"),
++ attr->values[0].integer);
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
++ attr->values[0].integer, printer->state);
++
++ if (attr->values[0].integer == IPP_PRINTER_STOPPED)
++ cupsdStopPrinter(printer, 0);
++ else
++ {
++ need_restart_job = 1;
++ cupsdSetPrinterState(printer, (ipp_pstate_t)(attr->values[0].integer), 0);
++ }
++ }
++ if ((attr = ippFindAttribute(con->request, "printer-state-message",
++ IPP_TAG_TEXT)) != NULL)
++ {
++ strlcpy(printer->state_message, attr->values[0].string.text,
++ sizeof(printer->state_message));
++ cupsdAddPrinterHistory(printer);
++ }
++
++ set_printer_defaults(con, printer);
++
++ /*
++ * See if we have all required attributes...
++ */
++
++ if (!printer->device_uri)
++ cupsdSetString(&printer->device_uri, "file:///dev/null");
++
++ /*
++ * See if we have an interface script or PPD file attached to the request...
++ */
++
++ if (con->filename)
++ {
++ need_restart_job = 1;
++
++ strlcpy(srcfile, con->filename, sizeof(srcfile));
++
++ if ((fp = cupsFileOpen(srcfile, "rb")))
++ {
++ /*
++ * Yes; get the first line from it...
++ */
++
++ line[0] = '\0';
++ cupsFileGets(fp, line, sizeof(line));
++ cupsFileClose(fp);
++
++ /*
++ * Then see what kind of file it is...
++ */
++
++ snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
++ printer->name);
++
++ if (!strncmp(line, "*PPD-Adobe", 10))
++ {
++ /*
++ * The new file is a PPD file, so remove any old interface script
++ * that might be lying around...
++ */
++
++ unlink(dstfile);
++ }
++ else
++ {
++ /*
++ * This must be an interface script, so move the file over to the
++ * interfaces directory and make it executable...
++ */
++
++ if (copy_file(srcfile, dstfile))
++ {
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("Unable to copy interface script - %s!"),
++ strerror(errno));
++ return;
++ }
++ else
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Copied interface script successfully!");
++ chmod(dstfile, 0755);
++ }
++ }
++
++ snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
++ printer->name);
++
++ if (!strncmp(line, "*PPD-Adobe", 10))
++ {
++ /*
++ * The new file is a PPD file, so move the file over to the
++ * ppd directory and make it readable by all...
++ */
++
++ if (copy_file(srcfile, dstfile))
++ {
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("Unable to copy PPD file - %s!"),
++ strerror(errno));
++ return;
++ }
++ else
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Copied PPD file successfully!");
++ chmod(dstfile, 0644);
++ }
++ }
++ else
++ {
++ /*
++ * This must be an interface script, so remove any old PPD file that
++ * may be lying around...
++ */
++
++ unlink(dstfile);
++ }
++ }
++ }
++ else if ((attr = ippFindAttribute(con->request, "ppd-name",
++ IPP_TAG_NAME)) != NULL)
++ {
++ need_restart_job = 1;
++
++ if (!strcmp(attr->values[0].string.text, "raw"))
++ {
++ /*
++ * Raw driver, remove any existing PPD or interface script files.
++ */
++
++ snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
++ printer->name);
++ unlink(dstfile);
++
++ snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
++ printer->name);
++ unlink(dstfile);
++ }
++ else
++ {
++ /*
++ * PPD model file...
++ */
++
++ snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
++ printer->name);
++ unlink(dstfile);
++
++ snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
++ printer->name);
++
++ if (copy_model(con, attr->values[0].string.text, dstfile))
++ {
++ send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file!"));
++ return;
++ }
++ else
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Copied PPD file successfully!");
++ chmod(dstfile, 0644);
++ }
++ }
++ }
++
++ /*
++ * Update the printer attributes and return...
++ */
++
++ cupsdSetPrinterAttrs(printer);
++ cupsdSaveAllPrinters();
++
++ if (need_restart_job && printer->job)
++ {
++ cupsd_job_t *job;
++
++ /*
++ * Stop the current job and then restart it below...
++ */
++
++ job = (cupsd_job_t *)printer->job;
++
++ cupsdStopJob(job, 1);
++
++ job->state->values[0].integer = IPP_JOB_PENDING;
++ job->state_value = IPP_JOB_PENDING;
++ }
++
++ if (need_restart_job)
++ cupsdCheckJobs();
++
++ cupsdWritePrintcap();
++
++ if (modify)
++ {
++ cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED, printer, NULL,
++ "Printer \"%s\" modified by \"%s\".", printer->name,
++ get_username(con));
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
++ printer->name, get_username(con));
++ }
++ else
++ {
++ cupsdAddPrinterHistory(printer);
++
++ cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED, printer, NULL,
++ "New printer \"%s\" added by \"%s\".", printer->name,
++ get_username(con));
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
++ printer->name, get_username(con));
++ }
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
++ * based upon the printer state...
++ */
++
++static void
++add_printer_state_reasons(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_printer_t *p) /* I - Printer info */
++{
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "add_printer_state_reasons(%p[%d], %p[%s])",
++ con, con->http.fd, p, p->name);
++
++ if (p->num_reasons == 0)
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
++ "printer-state-reasons", NULL,
++ p->state == IPP_PRINTER_STOPPED ? "paused" : "none");
++ else
++ ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
++ "printer-state-reasons", p->num_reasons, NULL,
++ (const char * const *)p->reasons);
++}
++
++
++/*
++ * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
++ * the specified printer or class.
++ */
++
++static void
++add_queued_job_count(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_printer_t *p) /* I - Printer or class */
++{
++ int count; /* Number of jobs on destination */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
++ con, con->http.fd, p, p->name);
++
++ count = cupsdGetPrinterJobCount(p->name);
++
++ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
++ "queued-job-count", count);
++}
++
++
++/*
++ * 'apply_printer_defaults()' - Apply printer default options to a job.
++ */
++
++static void
++apply_printer_defaults(
++ cupsd_printer_t *printer, /* I - Printer */
++ cupsd_job_t *job) /* I - Job */
++{
++ int i, /* Looping var */
++ num_options; /* Number of default options */
++ cups_option_t *options, /* Default options */
++ *option; /* Current option */
++
++
++ /*
++ * Collect all of the default options and add the missing ones to the
++ * job object...
++ */
++
++ for (i = printer->num_options, num_options = 0, option = printer->options;
++ i > 0;
++ i --, option ++)
++ if (!ippFindAttribute(job->attrs, option->name, IPP_TAG_ZERO))
++ {
++ num_options = cupsAddOption(option->name, option->value, num_options,
++ &options);
++ }
++
++ /*
++ * Encode these options as attributes in the job object...
++ */
++
++ cupsEncodeOptions2(job->attrs, num_options, options, IPP_TAG_JOB);
++ cupsFreeOptions(num_options, options);
++}
++
++
++/*
++ * 'authenticate_job()' - Set job authentication info.
++ */
++
++static void
++authenticate_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job URI */
++{
++ ipp_attribute_t *attr; /* Job-id attribute */
++ int jobid; /* Job ID */
++ cupsd_job_t *job; /* Current job */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
++ con, con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Start with "everything is OK" status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job has been completed...
++ */
++
++ if (job->state_value != IPP_JOB_HELD)
++ {
++ /*
++ * Return a "not-possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is not held for authentication!"),
++ jobid);
++ return;
++ }
++
++ /*
++ * See if we have already authenticated...
++ */
++
++ if (!con->username[0])
++ {
++ send_ipp_status(con, IPP_NOT_AUTHORIZED,
++ _("No authentication information provided!"));
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * Save the authentication information for this job...
++ */
++
++ save_auth_info(con, job);
++
++ /*
++ * Reset the job-hold-until value to "no-hold"...
++ */
++
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++
++ if (attr)
++ {
++ attr->value_tag = IPP_TAG_KEYWORD;
++ cupsdSetString(&(attr->values[0].string.text), "no-hold");
++ }
++
++ /*
++ * Release the job and return...
++ */
++
++ cupsdReleaseJob(job);
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was authenticated by \"%s\".", jobid,
++ con->username);
++}
++
++
++/*
++ * 'cancel_all_jobs()' - Cancel all print jobs.
++ */
++
++static void
++cancel_all_jobs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job or Printer URI */
++{
++ http_status_t status; /* Policy status */
++ const char *dest; /* Destination */
++ cups_ptype_t dtype; /* Destination type */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ userpass[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ ipp_attribute_t *attr; /* Attribute in request */
++ const char *username; /* Username */
++ int purge; /* Purge? */
++ cupsd_printer_t *printer; /* Printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a printer URI...
++ */
++
++ if (strcmp(uri->name, "printer-uri"))
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("The printer-uri attribute is required!"));
++ return;
++ }
++
++ /*
++ * Get the username (if any) for the jobs we want to cancel (only if
++ * "my-jobs" is specified...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "my-jobs",
++ IPP_TAG_BOOLEAN)) != NULL &&
++ attr->values[0].boolean)
++ {
++ if ((attr = ippFindAttribute(con->request, "requesting-user-name",
++ IPP_TAG_NAME)) != NULL)
++ username = attr->values[0].string.text;
++ else
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Missing requesting-user-name attribute!"));
++ return;
++ }
++ }
++ else
++ username = NULL;
++
++ /*
++ * Look for the "purge-jobs" attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "purge-jobs",
++ IPP_TAG_BOOLEAN)) != NULL)
++ purge = attr->values[0].boolean;
++ else
++ purge = 1;
++
++ /*
++ * And if the destination is valid...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), userpass, sizeof(userpass), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI?
++ */
++
++ if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
++ (!strncmp(resource, "/classes/", 9) && resource[9]))
++ {
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++ else if (strcmp(resource, "/printers/"))
++ {
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer-uri \"%s\" is not valid."),
++ uri->values[0].string.text);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Cancel all jobs on all printers...
++ */
++
++ cupsdCancelJobs(NULL, username, purge);
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
++ purge ? "purged" : "canceled", get_username(con));
++ }
++ else
++ {
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Cancel all of the jobs on the named printer...
++ */
++
++ cupsdCancelJobs(dest, username, purge);
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
++ dest, purge ? "purged" : "canceled", get_username(con));
++ }
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'cancel_job()' - Cancel a print job.
++ */
++
++static void
++cancel_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job or Printer URI */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ int jobid; /* Job ID */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_job_t *job; /* Job information */
++ const char *dest; /* Destination */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ cupsd_printer_t *printer; /* Printer data */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ if ((jobid = attr->values[0].integer) == 0)
++ {
++ /*
++ * Find the current job on the specified printer...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * See if the printer is currently printing a job...
++ */
++
++ if (printer->job)
++ jobid = ((cupsd_job_t *)printer->job)->id;
++ else
++ {
++ /*
++ * No, see if there are any pending jobs...
++ */
++
++ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
++ job;
++ job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
++ if (job->state_value <= IPP_JOB_PROCESSING &&
++ !strcasecmp(job->dest, dest))
++ break;
++
++ if (job)
++ jobid = job->id;
++ else
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s!"),
++ dest);
++ return;
++ }
++ }
++ }
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * See if the job is already completed, canceled, or aborted; if so,
++ * we can't cancel...
++ */
++
++ if (job->state_value >= IPP_JOB_CANCELED)
++ {
++ switch (job->state_value)
++ {
++ case IPP_JOB_CANCELED :
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is already canceled - can\'t cancel."),
++ jobid);
++ break;
++
++ case IPP_JOB_ABORTED :
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is already aborted - can\'t cancel."),
++ jobid);
++ break;
++
++ default :
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is already completed - can\'t cancel."),
++ jobid);
++ break;
++ }
++
++ return;
++ }
++
++ /*
++ * Cancel the job and return...
++ */
++
++ cupsdCancelJob(job, 0, IPP_JOB_CANCELED);
++ cupsdCheckJobs();
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was canceled by \"%s\".", jobid,
++ username);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'cancel_subscription()' - Cancel a subscription.
++ */
++
++static void
++cancel_subscription(
++ cupsd_client_t *con, /* I - Client connection */
++ int sub_id) /* I - Subscription ID */
++{
++ http_status_t status; /* Policy status */
++ cupsd_subscription_t *sub; /* Subscription */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "cancel_subscription(con=%p[%d], sub_id=%d)",
++ con, con->http.fd, sub_id);
++
++ /*
++ * Is the subscription ID valid?
++ */
++
++ if ((sub = cupsdFindSubscription(sub_id)) == NULL)
++ {
++ /*
++ * Bad subscription ID...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("notify-subscription-id %d no good!"), sub_id);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
++ DefaultPolicyPtr,
++ con, sub->owner)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Cancel the subscription...
++ */
++
++ cupsdDeleteSubscription(sub, 1);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'check_quotas()' - Check quotas for a printer and user.
++ */
++
++static int /* O - 1 if OK, 0 if not */
++check_quotas(cupsd_client_t *con, /* I - Client connection */
++ cupsd_printer_t *p) /* I - Printer or class */
++{
++ int i; /* Looping var */
++ char username[33]; /* Username */
++ cupsd_quota_t *q; /* Quota data */
++ struct passwd *pw; /* User password data */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
++ con, con->http.fd, p, p->name);
++
++ /*
++ * Check input...
++ */
++
++ if (!con || !p)
++ return (0);
++
++ /*
++ * Figure out who is printing...
++ */
++
++ strlcpy(username, get_username(con), sizeof(username));
++
++ /*
++ * Check global active job limits for printers and users...
++ */
++
++ if (MaxJobsPerPrinter)
++ {
++ /*
++ * Check if there are too many pending jobs on this printer...
++ */
++
++ if (cupsdGetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
++ p->name);
++ return (0);
++ }
++ }
++
++ if (MaxJobsPerUser)
++ {
++ /*
++ * Check if there are too many pending jobs for this user...
++ */
++
++ if (cupsdGetUserJobCount(username) >= MaxJobsPerUser)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
++ username);
++ return (0);
++ }
++ }
++
++ /*
++ * Check against users...
++ */
++
++ if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
++ return (1);
++
++ if (p->num_users)
++ {
++ pw = getpwnam(username);
++ endpwent();
++
++ for (i = 0; i < p->num_users; i ++)
++ if (p->users[i][0] == '@')
++ {
++ /*
++ * Check group membership...
++ */
++
++ if (cupsdCheckGroup(username, pw, p->users[i] + 1))
++ break;
++ }
++ else if (!strcasecmp(username, p->users[i]))
++ break;
++
++ if ((i < p->num_users) == p->deny_users)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Denying user \"%s\" access to printer \"%s\"...",
++ username, p->name);
++ return (0);
++ }
++ }
++
++ /*
++ * Check quotas...
++ */
++
++ if (p->k_limit || p->page_limit)
++ {
++ if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "Unable to allocate quota data for user \"%s\"!",
++ username);
++ return (0);
++ }
++
++ if ((q->k_count >= p->k_limit && p->k_limit) ||
++ (q->page_count >= p->page_limit && p->page_limit))
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "User \"%s\" is over the quota limit...",
++ username);
++ return (0);
++ }
++ }
++
++ /*
++ * If we have gotten this far, we're done!
++ */
++
++ return (1);
++}
++
++
++/*
++ * 'copy_attribute()' - Copy a single attribute.
++ */
++
++static ipp_attribute_t * /* O - New attribute */
++copy_attribute(
++ ipp_t *to, /* O - Destination request/response */
++ ipp_attribute_t *attr, /* I - Attribute to copy */
++ int quickcopy) /* I - Do a quick copy? */
++{
++ int i; /* Looping var */
++ ipp_attribute_t *toattr; /* Destination attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "copy_attribute(%p, %p[%s,%x,%x])", to, attr,
++ attr->name ? attr->name : "(null)", attr->group_tag,
++ attr->value_tag);
++
++ switch (attr->value_tag & ~IPP_TAG_COPY)
++ {
++ case IPP_TAG_ZERO :
++ toattr = ippAddSeparator(to);
++ break;
++
++ case IPP_TAG_INTEGER :
++ case IPP_TAG_ENUM :
++ toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
++ attr->name, attr->num_values, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ toattr->values[i].integer = attr->values[i].integer;
++ break;
++
++ case IPP_TAG_BOOLEAN :
++ toattr = ippAddBooleans(to, attr->group_tag, attr->name,
++ attr->num_values, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ toattr->values[i].boolean = attr->values[i].boolean;
++ break;
++
++ case IPP_TAG_STRING :
++ case IPP_TAG_TEXT :
++ case IPP_TAG_NAME :
++ case IPP_TAG_KEYWORD :
++ case IPP_TAG_URI :
++ case IPP_TAG_URISCHEME :
++ case IPP_TAG_CHARSET :
++ case IPP_TAG_LANGUAGE :
++ case IPP_TAG_MIMETYPE :
++ toattr = ippAddStrings(to, attr->group_tag,
++ (ipp_tag_t)(attr->value_tag | quickcopy),
++ attr->name, attr->num_values, NULL, NULL);
++
++ if (quickcopy)
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ toattr->values[i].string.text = attr->values[i].string.text;
++ }
++ else
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ toattr->values[i].string.text = _cupsStrAlloc(attr->values[i].string.text);
++ }
++ break;
++
++ case IPP_TAG_DATE :
++ toattr = ippAddDate(to, attr->group_tag, attr->name,
++ attr->values[0].date);
++ break;
++
++ case IPP_TAG_RESOLUTION :
++ toattr = ippAddResolutions(to, attr->group_tag, attr->name,
++ attr->num_values, IPP_RES_PER_INCH,
++ NULL, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
++ toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
++ toattr->values[i].resolution.units = attr->values[i].resolution.units;
++ }
++ break;
++
++ case IPP_TAG_RANGE :
++ toattr = ippAddRanges(to, attr->group_tag, attr->name,
++ attr->num_values, NULL, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ toattr->values[i].range.lower = attr->values[i].range.lower;
++ toattr->values[i].range.upper = attr->values[i].range.upper;
++ }
++ break;
++
++ case IPP_TAG_TEXTLANG :
++ case IPP_TAG_NAMELANG :
++ toattr = ippAddStrings(to, attr->group_tag,
++ (ipp_tag_t)(attr->value_tag | quickcopy),
++ attr->name, attr->num_values, NULL, NULL);
++
++ if (quickcopy)
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ toattr->values[i].string.charset = attr->values[i].string.charset;
++ toattr->values[i].string.text = attr->values[i].string.text;
++ }
++ }
++ else
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ if (!i)
++ toattr->values[i].string.charset =
++ _cupsStrAlloc(attr->values[i].string.charset);
++ else
++ toattr->values[i].string.charset =
++ toattr->values[0].string.charset;
++
++ toattr->values[i].string.text = _cupsStrAlloc(attr->values[i].string.text);
++ }
++ }
++ break;
++
++ case IPP_TAG_BEGIN_COLLECTION :
++ toattr = ippAddCollections(to, attr->group_tag, attr->name,
++ attr->num_values, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ toattr->values[i].collection = ippNew();
++ copy_attrs(toattr->values[i].collection, attr->values[i].collection,
++ NULL, IPP_TAG_ZERO, 0);
++ }
++ break;
++
++ default :
++ toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
++ attr->name, attr->num_values, NULL);
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ toattr->values[i].unknown.length = attr->values[i].unknown.length;
++
++ if (toattr->values[i].unknown.length > 0)
++ {
++ if ((toattr->values[i].unknown.data =
++ malloc(toattr->values[i].unknown.length)) == NULL)
++ toattr->values[i].unknown.length = 0;
++ else
++ memcpy(toattr->values[i].unknown.data,
++ attr->values[i].unknown.data,
++ toattr->values[i].unknown.length);
++ }
++ }
++ break; /* anti-compiler-warning-code */
++ }
++
++ return (toattr);
++}
++
++
++/*
++ * 'copy_attrs()' - Copy attributes from one request to another.
++ */
++
++static void
++copy_attrs(ipp_t *to, /* I - Destination request */
++ ipp_t *from, /* I - Source request */
++ cups_array_t *ra, /* I - Requested attributes */
++ ipp_tag_t group, /* I - Group to copy */
++ int quickcopy) /* I - Do a quick copy? */
++{
++ ipp_attribute_t *fromattr; /* Source attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
++ to, from, ra, group, quickcopy);
++
++ if (!to || !from)
++ return;
++
++ for (fromattr = from->attrs; fromattr; fromattr = fromattr->next)
++ {
++ /*
++ * Filter attributes as needed...
++ */
++
++ if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
++ fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
++ continue;
++
++ if (!ra || cupsArrayFind(ra, fromattr->name))
++ copy_attribute(to, fromattr, quickcopy);
++ }
++}
++
++
++/*
++ * 'copy_banner()' - Copy a banner file to the requests directory for the
++ * specified job.
++ */
++
++static int /* O - Size of banner file in kbytes */
++copy_banner(cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job, /* I - Job information */
++ const char *name) /* I - Name of banner */
++{
++ int i; /* Looping var */
++ int kbytes; /* Size of banner file in kbytes */
++ char filename[1024]; /* Job filename */
++ cupsd_banner_t *banner; /* Pointer to banner */
++ cups_file_t *in; /* Input file */
++ cups_file_t *out; /* Output file */
++ int ch; /* Character from file */
++ char attrname[255], /* Name of attribute */
++ *s; /* Pointer into name */
++ ipp_attribute_t *attr; /* Attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)",
++ con, con->http.fd, job, job->id, name ? name : "(null)");
++
++ /*
++ * Find the banner; return if not found or "none"...
++ */
++
++ if (!name || !strcmp(name, "none") ||
++ (banner = cupsdFindBanner(name)) == NULL)
++ return (0);
++
++ /*
++ * Open the banner and job files...
++ */
++
++ if (add_file(con, job, banner->filetype, 0))
++ return (0);
++
++ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
++ job->num_files);
++ if ((out = cupsFileOpen(filename, "w")) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "copy_banner: Unable to create banner job file %s - %s",
++ filename, strerror(errno));
++ job->num_files --;
++ return (0);
++ }
++
++ fchmod(cupsFileNumber(out), 0640);
++ fchown(cupsFileNumber(out), RunUser, Group);
++
++ /*
++ * Try the localized banner file under the subdirectory...
++ */
++
++ strlcpy(attrname, job->attrs->attrs->next->values[0].string.text,
++ sizeof(attrname));
++ if (strlen(attrname) > 2 && attrname[2] == '-')
++ {
++ /*
++ * Convert ll-cc to ll_CC...
++ */
++
++ attrname[2] = '_';
++ attrname[3] = toupper(attrname[3] & 255);
++ attrname[4] = toupper(attrname[4] & 255);
++ }
++
++ snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
++ attrname, name);
++
++ if (access(filename, 0) && strlen(attrname) > 2)
++ {
++ /*
++ * Wasn't able to find "ll_CC" locale file; try the non-national
++ * localization banner directory.
++ */
++
++ attrname[2] = '\0';
++
++ snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
++ attrname, name);
++ }
++
++ if (access(filename, 0))
++ {
++ /*
++ * Use the non-localized banner file.
++ */
++
++ snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
++ }
++
++ if ((in = cupsFileOpen(filename, "r")) == NULL)
++ {
++ cupsFileClose(out);
++ unlink(filename);
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "copy_banner: Unable to open banner template file %s - %s",
++ filename, strerror(errno));
++ job->num_files --;
++ return (0);
++ }
++
++ /*
++ * Parse the file to the end...
++ */
++
++ while ((ch = cupsFileGetChar(in)) != EOF)
++ if (ch == '{')
++ {
++ /*
++ * Get an attribute name...
++ */
++
++ for (s = attrname; (ch = cupsFileGetChar(in)) != EOF;)
++ if (!isalpha(ch & 255) && ch != '-' && ch != '?')
++ break;
++ else if (s < (attrname + sizeof(attrname) - 1))
++ *s++ = ch;
++ else
++ break;
++
++ *s = '\0';
++
++ if (ch != '}')
++ {
++ /*
++ * Ignore { followed by stuff that is not an attribute name...
++ */
++
++ cupsFilePrintf(out, "{%s%c", attrname, ch);
++ continue;
++ }
++
++ /*
++ * See if it is defined...
++ */
++
++ if (attrname[0] == '?')
++ s = attrname + 1;
++ else
++ s = attrname;
++
++ if (!strcmp(s, "printer-name"))
++ {
++ cupsFilePuts(out, job->dest);
++ continue;
++ }
++ else if ((attr = ippFindAttribute(job->attrs, s, IPP_TAG_ZERO)) == NULL)
++ {
++ /*
++ * See if we have a leading question mark...
++ */
++
++ if (attrname[0] != '?')
++ {
++ /*
++ * Nope, write to file as-is; probably a PostScript procedure...
++ */
++
++ cupsFilePrintf(out, "{%s}", attrname);
++ }
++
++ continue;
++ }
++
++ /*
++ * Output value(s)...
++ */
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ if (i)
++ cupsFilePutChar(out, ',');
++
++ switch (attr->value_tag)
++ {
++ case IPP_TAG_INTEGER :
++ case IPP_TAG_ENUM :
++ if (!strncmp(s, "time-at-", 8))
++ cupsFilePuts(out, cupsdGetDateTime(attr->values[i].integer));
++ else
++ cupsFilePrintf(out, "%d", attr->values[i].integer);
++ break;
++
++ case IPP_TAG_BOOLEAN :
++ cupsFilePrintf(out, "%d", attr->values[i].boolean);
++ break;
++
++ case IPP_TAG_NOVALUE :
++ cupsFilePuts(out, "novalue");
++ break;
++
++ case IPP_TAG_RANGE :
++ cupsFilePrintf(out, "%d-%d", attr->values[i].range.lower,
++ attr->values[i].range.upper);
++ break;
++
++ case IPP_TAG_RESOLUTION :
++ cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
++ attr->values[i].resolution.yres,
++ attr->values[i].resolution.units == IPP_RES_PER_INCH ?
++ "dpi" : "dpc");
++ break;
++
++ case IPP_TAG_URI :
++ case IPP_TAG_STRING :
++ case IPP_TAG_TEXT :
++ case IPP_TAG_NAME :
++ case IPP_TAG_KEYWORD :
++ case IPP_TAG_CHARSET :
++ case IPP_TAG_LANGUAGE :
++ if (!strcasecmp(banner->filetype->type, "postscript"))
++ {
++ /*
++ * Need to quote strings for PS banners...
++ */
++
++ const char *p;
++
++ for (p = attr->values[i].string.text; *p; p ++)
++ {
++ if (*p == '(' || *p == ')' || *p == '\\')
++ {
++ cupsFilePutChar(out, '\\');
++ cupsFilePutChar(out, *p);
++ }
++ else if (*p < 32 || *p > 126)
++ cupsFilePrintf(out, "\\%03o", *p & 255);
++ else
++ cupsFilePutChar(out, *p);
++ }
++ }
++ else
++ cupsFilePuts(out, attr->values[i].string.text);
++ break;
++
++ default :
++ break; /* anti-compiler-warning-code */
++ }
++ }
++ }
++ else if (ch == '\\') /* Quoted char */
++ {
++ ch = cupsFileGetChar(in);
++
++ if (ch != '{') /* Only do special handling for \{ */
++ cupsFilePutChar(out, '\\');
++
++ cupsFilePutChar(out, ch);
++ }
++ else
++ cupsFilePutChar(out, ch);
++
++ cupsFileClose(in);
++
++ kbytes = (cupsFileTell(out) + 1023) / 1024;
++
++ if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
++ IPP_TAG_INTEGER)) != NULL)
++ attr->values[0].integer += kbytes;
++
++ cupsFileClose(out);
++
++ return (kbytes);
++}
++
++
++/*
++ * 'copy_file()' - Copy a PPD file or interface script...
++ */
++
++static int /* O - 0 = success, -1 = error */
++copy_file(const char *from, /* I - Source file */
++ const char *to) /* I - Destination file */
++{
++ cups_file_t *src, /* Source file */
++ *dst; /* Destination file */
++ int bytes; /* Bytes to read/write */
++ char buffer[2048]; /* Copy buffer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_file(\"%s\", \"%s\")", from, to);
++
++ /*
++ * Open the source and destination file for a copy...
++ */
++
++ if ((src = cupsFileOpen(from, "rb")) == NULL)
++ return (-1);
++
++ if ((dst = cupsFileOpen(to, "wb")) == NULL)
++ {
++ cupsFileClose(src);
++ return (-1);
++ }
++
++ /*
++ * Copy the source file to the destination...
++ */
++
++ while ((bytes = cupsFileRead(src, buffer, sizeof(buffer))) > 0)
++ if (cupsFileWrite(dst, buffer, bytes) < bytes)
++ {
++ cupsFileClose(src);
++ cupsFileClose(dst);
++ return (-1);
++ }
++
++ /*
++ * Close both files and return...
++ */
++
++ cupsFileClose(src);
++
++ return (cupsFileClose(dst));
++}
++
++
++/*
++ * 'copy_model()' - Copy a PPD model file, substituting default values
++ * as needed...
++ */
++
++static int /* O - 0 = success, -1 = error */
++copy_model(cupsd_client_t *con, /* I - Client connection */
++ const char *from, /* I - Source file */
++ const char *to) /* I - Destination file */
++{
++ fd_set *input; /* select() input set */
++ struct timeval timeout; /* select() timeout */
++ int maxfd; /* Maximum file descriptor for select() */
++ char tempfile[1024]; /* Temporary PPD file */
++ int tempfd; /* Temporary PPD file descriptor */
++ int temppid; /* Process ID of cups-driverd */
++ int temppipe[2]; /* Temporary pipes */
++ char *argv[4], /* Command-line arguments */
++ *envp[MAX_ENV]; /* Environment */
++ cups_file_t *src, /* Source file */
++ *dst; /* Destination file */
++ ppd_file_t *ppd; /* PPD file */
++ int bytes, /* Bytes from pipe */
++ total; /* Total bytes from pipe */
++ char buffer[2048]; /* Copy buffer */
++ int i; /* Looping var */
++ char option[PPD_MAX_NAME], /* Option name */
++ choice[PPD_MAX_NAME]; /* Choice name */
++ int num_defaults; /* Number of default options */
++ cups_option_t *defaults; /* Default options */
++ char cups_protocol[PPD_MAX_LINE];
++ /* cupsProtocol attribute */
++ int have_letter, /* Have Letter size */
++ have_a4; /* Have A4 size */
++#ifdef HAVE_LIBPAPER
++ char *paper_result; /* Paper size name from libpaper */
++ char system_paper[64]; /* Paper size name buffer */
++#endif /* HAVE_LIBPAPER */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "copy_model(con=%p, from=\"%s\", to=\"%s\")",
++ con, from, to);
++
++ /*
++ * Run cups-driverd to get the PPD file...
++ */
++
++ argv[0] = "cups-driverd";
++ argv[1] = "cat";
++ argv[2] = (char *)from;
++ argv[3] = NULL;
++
++ cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
++
++ snprintf(buffer, sizeof(buffer), "%s/daemon/cups-driverd", ServerBin);
++ snprintf(tempfile, sizeof(tempfile), "%s/%d.ppd", TempDir, con->http.fd);
++ tempfd = open(tempfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
++ if (tempfd < 0)
++ return (-1);
++
++ cupsdOpenPipe(temppipe);
++
++ if ((input = calloc(1, SetSize)) == NULL)
++ {
++ close(tempfd);
++ unlink(tempfile);
++
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "copy_model: Unable to allocate %d bytes for select()...",
++ SetSize);
++ return (-1);
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "copy_model: Running \"cups-driverd cat %s\"...", from);
++
++ if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
++ -1, 0, &temppid))
++ {
++ free(input);
++ close(tempfd);
++ unlink(tempfile);
++ return (-1);
++ }
++
++ close(temppipe[1]);
++
++ /*
++ * Wait up to 30 seconds for the PPD file to be copied...
++ */
++
++ total = 0;
++
++ if (temppipe[0] > CGIPipes[0])
++ maxfd = temppipe[0] + 1;
++ else
++ maxfd = CGIPipes[0] + 1;
++
++ for (;;)
++ {
++ /*
++ * See if we have data ready...
++ */
++
++ bytes = 0;
++
++ FD_SET(temppipe[0], input);
++ FD_SET(CGIPipes[0], input);
++
++ timeout.tv_sec = 30;
++ timeout.tv_usec = 0;
++
++ if ((i = select(maxfd, input, NULL, NULL, &timeout)) < 0)
++ {
++ if (errno == EINTR)
++ continue;
++ else
++ break;
++ }
++ else if (i == 0)
++ {
++ /*
++ * We have timed out...
++ */
++
++ break;
++ }
++
++ if (FD_ISSET(temppipe[0], input))
++ {
++ /*
++ * Read the PPD file from the pipe, and write it to the PPD file.
++ */
++
++ if ((bytes = read(temppipe[0], buffer, sizeof(buffer))) > 0)
++ {
++ if (write(tempfd, buffer, bytes) < bytes)
++ break;
++
++ total += bytes;
++ }
++ else
++ break;
++ }
++
++ if (FD_ISSET(CGIPipes[0], input))
++ cupsdUpdateCGI();
++ }
++
++ close(temppipe[0]);
++ close(tempfd);
++
++ free(input);
++
++ if (!total)
++ {
++ /*
++ * No data from cups-deviced...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_ERROR, "copy_model: empty PPD file!");
++ unlink(tempfile);
++ return (-1);
++ }
++
++ /*
++ * Read the source file and see what page sizes are supported...
++ */
++
++ if ((ppd = ppdOpenFile(tempfile)) == NULL)
++ {
++ unlink(tempfile);
++ return (-1);
++ }
++
++ have_letter = ppdPageSize(ppd, "Letter") != NULL;
++ have_a4 = ppdPageSize(ppd, "A4") != NULL;
++
++ /*
++ * Open the destination (if possible) and set the default options...
++ */
++
++ num_defaults = 0;
++ defaults = NULL;
++ cups_protocol[0] = '\0';
++
++ if ((dst = cupsFileOpen(to, "rb")) != NULL)
++ {
++ /*
++ * Read all of the default lines from the old PPD...
++ */
++
++ while (cupsFileGets(dst, buffer, sizeof(buffer)))
++ if (!strncmp(buffer, "*Default", 8))
++ {
++ /*
++ * Add the default option...
++ */
++
++ if (!ppd_parse_line(buffer, option, sizeof(option),
++ choice, sizeof(choice)))
++ {
++ ppd_option_t *ppdo; /* PPD option */
++
++
++ /*
++ * Only add the default if the default hasn't already been
++ * set and the choice exists in the new PPD...
++ */
++
++ if (!cupsGetOption(option, num_defaults, defaults) &&
++ (ppdo = ppdFindOption(ppd, option)) != NULL &&
++ ppdFindChoice(ppdo, choice))
++ num_defaults = cupsAddOption(option, choice, num_defaults,
++ &defaults);
++ }
++ }
++ else if (!strncmp(buffer, "*cupsProtocol:", 14))
++ strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
++
++ cupsFileClose(dst);
++ }
++#ifdef HAVE_LIBPAPER
++ else if ((paper_result = systempapername()) != NULL)
++ {
++ /*
++ * Set the default media sizes from the systemwide default...
++ */
++
++ strlcpy(system_paper, paper_result, sizeof(system_paper));
++ system_paper[0] = toupper(system_paper[0] & 255);
++
++ if ((!strcmp(system_paper, "Letter") && have_letter) ||
++ (!strcmp(system_paper, "A4") && have_a4))
++ {
++ num_defaults = cupsAddOption("PageSize", system_paper,
++ num_defaults, &defaults);
++ num_defaults = cupsAddOption("PageRegion", system_paper,
++ num_defaults, &defaults);
++ num_defaults = cupsAddOption("PaperDimension", system_paper,
++ num_defaults, &defaults);
++ num_defaults = cupsAddOption("ImageableArea", system_paper,
++ num_defaults, &defaults);
++ }
++ }
++#endif /* HAVE_LIBPAPER */
++ else
++ {
++ /*
++ * Add the default media sizes...
++ *
++ * Note: These values are generally not valid for large-format devices
++ * like plotters, however it is probably safe to say that those
++ * users will configure the media size after initially adding
++ * the device anyways...
++ */
++
++ if (!DefaultLanguage ||
++ !strcasecmp(DefaultLanguage, "C") ||
++ !strcasecmp(DefaultLanguage, "POSIX") ||
++ !strcasecmp(DefaultLanguage, "en") ||
++ !strncasecmp(DefaultLanguage, "en.", 3) ||
++ !strncasecmp(DefaultLanguage, "en_US", 5) ||
++ !strncasecmp(DefaultLanguage, "en_CA", 5) ||
++ !strncasecmp(DefaultLanguage, "fr_CA", 5))
++ {
++ /*
++ * These are the only locales that will default to "letter" size...
++ */
++
++ if (have_letter)
++ {
++ num_defaults = cupsAddOption("PageSize", "Letter", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("PageRegion", "Letter", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("PaperDimension", "Letter", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("ImageableArea", "Letter", num_defaults,
++ &defaults);
++ }
++ }
++ else if (have_a4)
++ {
++ /*
++ * The rest default to "a4" size...
++ */
++
++ num_defaults = cupsAddOption("PageSize", "A4", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("PageRegion", "A4", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("PaperDimension", "A4", num_defaults,
++ &defaults);
++ num_defaults = cupsAddOption("ImageableArea", "A4", num_defaults,
++ &defaults);
++ }
++ }
++
++ ppdClose(ppd);
++
++ /*
++ * Open the source file for a copy...
++ */
++
++ if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
++ {
++ cupsFreeOptions(num_defaults, defaults);
++ unlink(tempfile);
++ return (-1);
++ }
++
++ /*
++ * Open the destination file for a copy...
++ */
++
++ if ((dst = cupsFileOpen(to, "wb")) == NULL)
++ {
++ cupsFreeOptions(num_defaults, defaults);
++ cupsFileClose(src);
++ unlink(tempfile);
++ return (-1);
++ }
++
++ /*
++ * Copy the source file to the destination...
++ */
++
++ while (cupsFileGets(src, buffer, sizeof(buffer)))
++ {
++ if (!strncmp(buffer, "*Default", 8))
++ {
++ /*
++ * Check for an previous default option choice...
++ */
++
++ if (!ppd_parse_line(buffer, option, sizeof(option),
++ choice, sizeof(choice)))
++ {
++ const char *val; /* Default option value */
++
++
++ if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
++ {
++ /*
++ * Substitute the previous choice...
++ */
++
++ snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
++ }
++ }
++ }
++
++ cupsFilePrintf(dst, "%s\n", buffer);
++ }
++
++ if (cups_protocol[0])
++ cupsFilePrintf(dst, "%s\n", cups_protocol);
++
++ cupsFreeOptions(num_defaults, defaults);
++
++ /*
++ * Close both files and return...
++ */
++
++ cupsFileClose(src);
++
++ unlink(tempfile);
++
++ return (cupsFileClose(dst));
++}
++
++
++/*
++ * 'copy_job_attrs()' - Copy job attributes.
++ */
++
++static void
++copy_job_attrs(cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job, /* I - Job */
++ cups_array_t *ra) /* I - Requested attributes array */
++{
++ char job_uri[HTTP_MAX_URI]; /* Job URI */
++
++
++ /*
++ * Send the requested attributes for each job...
++ */
++
++ httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
++ con->servername, con->serverport, "/jobs/%d",
++ job->id);
++
++ if (!ra || cupsArrayFind(ra, "job-more-info"))
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
++ "job-more-info", NULL, job_uri);
++
++ if (job->state_value > IPP_JOB_PROCESSING &&
++ (!ra || cupsArrayFind(ra, "job-preserved")))
++ ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
++ job->num_files > 0);
++
++ if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
++ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
++ "job-printer-up-time", time(NULL));
++
++ if (!ra || cupsArrayFind(ra, "job-state-reasons"))
++ add_job_state_reasons(con, job);
++
++ if (!ra || cupsArrayFind(ra, "job-uri"))
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
++ "job-uri", NULL, job_uri);
++
++ copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0);
++}
++
++
++/*
++ * 'copy_printer_attrs()' - Copy printer attributes.
++ */
++
++static void
++copy_printer_attrs(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_printer_t *printer, /* I - Printer */
++ cups_array_t *ra) /* I - Requested attributes array */
++{
++ char printer_uri[HTTP_MAX_URI];
++ /* Printer URI */
++ time_t curtime; /* Current time */
++ int i; /* Looping var */
++ ipp_attribute_t *history; /* History collection */
++
++
++ /*
++ * Copy the printer attributes to the response using requested-attributes
++ * and document-format attributes that may be provided by the client.
++ */
++
++ curtime = time(NULL);
++
++#ifdef __APPLE__
++ if ((!ra || cupsArrayFind(ra, "com.apple.print.recoverable-message")) &&
++ printer->recoverable)
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
++ "com.apple.print.recoverable-message", NULL,
++ printer->recoverable);
++#endif /* __APPLE__ */
++
++ if (!ra || cupsArrayFind(ra, "printer-current-time"))
++ ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
++ ippTimeToDate(curtime));
++
++ if (!ra || cupsArrayFind(ra, "printer-error-policy"))
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
++ "printer-error-policy", NULL, printer->error_policy);
++
++ if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
++ ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
++ printer->accepting);
++
++ if (!ra || cupsArrayFind(ra, "printer-is-shared"))
++ ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-shared",
++ printer->shared);
++
++ if (!ra || cupsArrayFind(ra, "printer-op-policy"))
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
++ "printer-op-policy", NULL, printer->op_policy);
++
++ if (!ra || cupsArrayFind(ra, "printer-state"))
++ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
++ printer->state);
++
++ if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
++ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
++ "printer-state-change-time", printer->state_time);
++
++ if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
++ cupsArrayFind(ra, "printer-state-history"))
++ {
++ /*
++ * Printer history is only sent if specifically requested, so that
++ * older CUPS/IPP clients won't barf on the collection attributes.
++ */
++
++ history = ippAddCollections(con->response, IPP_TAG_PRINTER,
++ "printer-state-history",
++ printer->num_history, NULL);
++
++ for (i = 0; i < printer->num_history; i ++)
++ copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
++ NULL, IPP_TAG_ZERO, 0);
++ }
++
++ if (!ra || cupsArrayFind(ra, "printer-state-message"))
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
++ "printer-state-message", NULL, printer->state_message);
++
++ if (!ra || cupsArrayFind(ra, "printer-state-reasons"))
++ add_printer_state_reasons(con, printer);
++
++ if (!ra || cupsArrayFind(ra, "printer-type"))
++ {
++ int type; /* printer-type value */
++
++
++ /*
++ * Add the CUPS-specific printer-type attribute...
++ */
++
++ type = printer->type;
++
++ if (printer == DefaultPrinter)
++ type |= CUPS_PRINTER_DEFAULT;
++
++ if (!printer->accepting)
++ type |= CUPS_PRINTER_REJECTING;
++
++ if (!printer->shared)
++ type |= CUPS_PRINTER_NOT_SHARED;
++
++ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-type",
++ type);
++ }
++
++ if (!ra || cupsArrayFind(ra, "printer-up-time"))
++ ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
++ "printer-up-time", curtime);
++
++ if ((!ra || cupsArrayFind(ra, "printer-uri-supported")) &&
++ !ippFindAttribute(printer->attrs, "printer-uri-supported",
++ IPP_TAG_URI))
++ {
++ httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
++ "ipp", NULL, con->servername, con->serverport,
++ (printer->type & CUPS_PRINTER_CLASS) ?
++ "/classes/%s" : "/printers/%s", printer->name);
++ ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
++ "printer-uri-supported", NULL, printer_uri);
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-uri-supported=\"%s\"",
++ printer_uri);
++ }
++
++ if (!ra || cupsArrayFind(ra, "queued-job-count"))
++ add_queued_job_count(con, printer);
++
++ copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
++ copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
++}
++
++
++/*
++ * 'copy_subscription_attrs()' - Copy subscription attributes.
++ */
++
++static void
++copy_subscription_attrs(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_subscription_t *sub, /* I - Subscription */
++ cups_array_t *ra) /* I - Requested attributes array */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ char printer_uri[HTTP_MAX_URI];
++ /* Printer URI */
++ int count; /* Number of events */
++ unsigned mask; /* Current event mask */
++ const char *name; /* Current event name */
++
++
++ /*
++ * Copy the subscription attributes to the response using the
++ * requested-attributes attribute that may be provided by the client.
++ */
++
++ if (!ra || cupsArrayFind(ra, "notify-events"))
++ {
++ if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
++ {
++ /*
++ * Simple event list...
++ */
++
++ ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
++ (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
++ "notify-events", NULL, name);
++ }
++ else
++ {
++ /*
++ * Complex event list...
++ */
++
++ for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
++ if (sub->mask & mask)
++ count ++;
++
++ attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
++ (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
++ "notify-events", count, NULL, NULL);
++
++ for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
++ if (sub->mask & mask)
++ {
++ attr->values[count].string.text =
++ (char *)cupsdEventName((cupsd_eventmask_t)mask);
++
++ count ++;
++ }
++ }
++ }
++
++ if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-job-id", sub->job->id);
++
++ if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-lease-duration", sub->lease);
++
++ if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
++ {
++ httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
++ "ipp", NULL, con->servername, con->serverport,
++ "/printers/%s", sub->dest->name);
++ ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
++ "notify-printer-uri", NULL, printer_uri);
++ }
++
++ if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
++ ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
++ "notify-recipient-uri", NULL, sub->recipient);
++ else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
++ ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
++ "notify-pull-method", NULL, "ippget");
++
++ if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
++ ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
++ "notify-subscriber-user-name", NULL, sub->owner);
++
++ if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-subscription-id", sub->id);
++
++ if (!ra || cupsArrayFind(ra, "notify-time-interval"))
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-time-interval", sub->interval);
++
++ if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
++ ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
++ sub->user_data, sub->user_data_len);
++}
++
++
++/*
++ * 'create_job()' - Print a file to a printer or class.
++ */
++
++static void
++create_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ cupsd_job_t *job; /* New job */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Create the job object...
++ */
++
++ if ((job = add_job(con, uri, NULL, NULL)) == NULL)
++ return;
++
++ /*
++ * Save and log the job...
++ */
++
++ cupsdSaveJob(job);
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d created on \"%s\" by \"%s\".",
++ job->id, job->dest, job->username);
++}
++
++
++/*
++ * 'create_requested_array()' - Create an array for the requested-attributes.
++ */
++
++static cups_array_t * /* O - Array of attributes or NULL */
++create_requested_array(ipp_t *request) /* I - IPP request */
++{
++ int i; /* Looping var */
++ ipp_attribute_t *requested; /* requested-attributes attribute */
++ cups_array_t *ra; /* Requested attributes array */
++ char *value; /* Current value */
++
++
++ /*
++ * Get the requested-attributes attribute, and return NULL if we don't
++ * have one...
++ */
++
++ if ((requested = ippFindAttribute(request, "requested-attributes",
++ IPP_TAG_KEYWORD)) == NULL)
++ return (NULL);
++
++ /*
++ * If the attribute contains a single "all" keyword, return NULL...
++ */
++
++ if (requested->num_values == 1 &&
++ !strcmp(requested->values[0].string.text, "all"))
++ return (NULL);
++
++ /*
++ * Create an array using "strcmp" as the comparison function...
++ */
++
++ ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
++
++ for (i = 0; i < requested->num_values; i ++)
++ {
++ value = requested->values[i].string.text;
++
++ if (!strcmp(value, "job-template"))
++ {
++ cupsArrayAdd(ra, "copies");
++ cupsArrayAdd(ra, "copies-default");
++ cupsArrayAdd(ra, "copies-supported");
++ cupsArrayAdd(ra, "finishings");
++ cupsArrayAdd(ra, "finishings-default");
++ cupsArrayAdd(ra, "finishings-supported");
++ cupsArrayAdd(ra, "job-hold-until");
++ cupsArrayAdd(ra, "job-hold-until-default");
++ cupsArrayAdd(ra, "job-hold-until-supported");
++ cupsArrayAdd(ra, "job-priority");
++ cupsArrayAdd(ra, "job-priority-default");
++ cupsArrayAdd(ra, "job-priority-supported");
++ cupsArrayAdd(ra, "job-sheets");
++ cupsArrayAdd(ra, "job-sheets-default");
++ cupsArrayAdd(ra, "job-sheets-supported");
++ cupsArrayAdd(ra, "media");
++ cupsArrayAdd(ra, "media-default");
++ cupsArrayAdd(ra, "media-supported");
++ cupsArrayAdd(ra, "multiple-document-handling");
++ cupsArrayAdd(ra, "multiple-document-handling-default");
++ cupsArrayAdd(ra, "multiple-document-handling-supported");
++ cupsArrayAdd(ra, "number-up");
++ cupsArrayAdd(ra, "number-up-default");
++ cupsArrayAdd(ra, "number-up-supported");
++ cupsArrayAdd(ra, "orientation-requested");
++ cupsArrayAdd(ra, "orientation-requested-default");
++ cupsArrayAdd(ra, "orientation-requested-supported");
++ cupsArrayAdd(ra, "page-ranges");
++ cupsArrayAdd(ra, "page-ranges-supported");
++ cupsArrayAdd(ra, "printer-resolution");
++ cupsArrayAdd(ra, "printer-resolution-default");
++ cupsArrayAdd(ra, "printer-resolution-supported");
++ cupsArrayAdd(ra, "print-quality");
++ cupsArrayAdd(ra, "print-quality-default");
++ cupsArrayAdd(ra, "print-quality-supported");
++ cupsArrayAdd(ra, "sides");
++ cupsArrayAdd(ra, "sides-default");
++ cupsArrayAdd(ra, "sides-supported");
++ }
++ else if (!strcmp(value, "job-description"))
++ {
++ cupsArrayAdd(ra, "date-time-at-completed");
++ cupsArrayAdd(ra, "date-time-at-creation");
++ cupsArrayAdd(ra, "date-time-at-processing");
++ cupsArrayAdd(ra, "job-detailed-status-message");
++ cupsArrayAdd(ra, "job-document-access-errors");
++ cupsArrayAdd(ra, "job-id");
++ cupsArrayAdd(ra, "job-impressions");
++ cupsArrayAdd(ra, "job-impressions-completed");
++ cupsArrayAdd(ra, "job-k-octets");
++ cupsArrayAdd(ra, "job-k-octets-processed");
++ cupsArrayAdd(ra, "job-media-sheets");
++ cupsArrayAdd(ra, "job-media-sheets-completed");
++ cupsArrayAdd(ra, "job-message-from-operator");
++ cupsArrayAdd(ra, "job-more-info");
++ cupsArrayAdd(ra, "job-name");
++ cupsArrayAdd(ra, "job-originating-user-name");
++ cupsArrayAdd(ra, "job-printer-up-time");
++ cupsArrayAdd(ra, "job-printer-uri");
++ cupsArrayAdd(ra, "job-state");
++ cupsArrayAdd(ra, "job-state-message");
++ cupsArrayAdd(ra, "job-state-reasons");
++ cupsArrayAdd(ra, "job-uri");
++ cupsArrayAdd(ra, "number-of-documents");
++ cupsArrayAdd(ra, "number-of-intervening-jobs");
++ cupsArrayAdd(ra, "output-device-assigned");
++ cupsArrayAdd(ra, "time-at-completed");
++ cupsArrayAdd(ra, "time-at-creation");
++ cupsArrayAdd(ra, "time-at-processing");
++ }
++ else if (!strcmp(value, "printer-description"))
++ {
++ cupsArrayAdd(ra, "charset-configured");
++ cupsArrayAdd(ra, "charset-supported");
++ cupsArrayAdd(ra, "color-supported");
++ cupsArrayAdd(ra, "compression-supported");
++ cupsArrayAdd(ra, "document-format-default");
++ cupsArrayAdd(ra, "document-format-supported");
++ cupsArrayAdd(ra, "generated-natural-language-supported");
++ cupsArrayAdd(ra, "ipp-versions-supported");
++ cupsArrayAdd(ra, "job-impressions-supported");
++ cupsArrayAdd(ra, "job-k-octets-supported");
++ cupsArrayAdd(ra, "job-media-sheets-supported");
++ cupsArrayAdd(ra, "multiple-document-jobs-supported");
++ cupsArrayAdd(ra, "multiple-operation-time-out");
++ cupsArrayAdd(ra, "natural-language-configured");
++ cupsArrayAdd(ra, "notify-attributes-supported");
++ cupsArrayAdd(ra, "notify-lease-duration-default");
++ cupsArrayAdd(ra, "notify-lease-duration-supported");
++ cupsArrayAdd(ra, "notify-max-events-supported");
++ cupsArrayAdd(ra, "notify-events-default");
++ cupsArrayAdd(ra, "notify-events-supported");
++ cupsArrayAdd(ra, "notify-pull-method-supported");
++ cupsArrayAdd(ra, "notify-schemes-supported");
++ cupsArrayAdd(ra, "operations-supported");
++ cupsArrayAdd(ra, "pages-per-minute");
++ cupsArrayAdd(ra, "pages-per-minute-color");
++ cupsArrayAdd(ra, "pdl-override-supported");
++ cupsArrayAdd(ra, "printer-current-time");
++ cupsArrayAdd(ra, "printer-driver-installer");
++ cupsArrayAdd(ra, "printer-info");
++ cupsArrayAdd(ra, "printer-is-accepting-jobs");
++ cupsArrayAdd(ra, "printer-location");
++ cupsArrayAdd(ra, "printer-make-and-model");
++ cupsArrayAdd(ra, "printer-message-from-operator");
++ cupsArrayAdd(ra, "printer-more-info");
++ cupsArrayAdd(ra, "printer-more-info-manufacturer");
++ cupsArrayAdd(ra, "printer-name");
++ cupsArrayAdd(ra, "printer-state");
++ cupsArrayAdd(ra, "printer-state-message");
++ cupsArrayAdd(ra, "printer-state-reasons");
++ cupsArrayAdd(ra, "printer-up-time");
++ cupsArrayAdd(ra, "printer-uri-supported");
++ cupsArrayAdd(ra, "queued-job-count");
++ cupsArrayAdd(ra, "reference-uri-schemes-supported");
++ cupsArrayAdd(ra, "uri-authentication-supported");
++ cupsArrayAdd(ra, "uri-security-supported");
++ }
++ else if (!strcmp(value, "subscription-template"))
++ {
++ cupsArrayAdd(ra, "notify-attributes");
++ cupsArrayAdd(ra, "notify-charset");
++ cupsArrayAdd(ra, "notify-events");
++ cupsArrayAdd(ra, "notify-lease-duration");
++ cupsArrayAdd(ra, "notify-natural-language");
++ cupsArrayAdd(ra, "notify-pull-method");
++ cupsArrayAdd(ra, "notify-recipient-uri");
++ cupsArrayAdd(ra, "notify-time-interval");
++ cupsArrayAdd(ra, "notify-user-data");
++ }
++ else
++ cupsArrayAdd(ra, value);
++ }
++
++ return (ra);
++}
++
++
++/*
++ * 'create_subscription()' - Create a notification subscription.
++ */
++
++static void
++create_subscription(
++ cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ int i; /* Looping var */
++ ipp_attribute_t *attr; /* Current attribute */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char scheme[HTTP_MAX_URI],
++ /* Scheme portion of URI */
++ userpass[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *printer; /* Printer/class */
++ cupsd_job_t *job; /* Job */
++ int jobid; /* Job ID */
++ cupsd_subscription_t *sub; /* Subscription object */
++ const char *username, /* requesting-user-name or authenticated username */
++ *recipient, /* notify-recipient-uri */
++ *pullmethod; /* notify-pull-method */
++ ipp_attribute_t *user_data; /* notify-user-data */
++ int interval, /* notify-time-interval */
++ lease; /* notify-lease-duration */
++ unsigned mask; /* notify-events */
++
++
++#ifdef DEBUG
++ for (attr = con->request->attrs; attr; attr = attr->next)
++ {
++ if (attr->group_tag != IPP_TAG_ZERO)
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "g%04x v%04x %s", attr->group_tag,
++ attr->value_tag, attr->name);
++ else
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "----SEP----");
++ }
++#endif /* DEBUG */
++
++ /*
++ * Is the destination valid?
++ */
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "cupsdCreateSubscription(con=%p(%d), uri=\"%s\")",
++ con, con->http.fd, uri->values[0].string.text);
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
++ sizeof(scheme), userpass, sizeof(userpass), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!strcmp(resource, "/"))
++ {
++ dtype = (cups_ptype_t)0;
++ printer = NULL;
++ }
++ else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
++ {
++ dtype = (cups_ptype_t)0;
++ printer = NULL;
++ }
++ else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
++ {
++ dtype = CUPS_PRINTER_CLASS;
++ printer = NULL;
++ }
++ else if (!cupsdValidateDest(host, resource, &dtype, &printer))
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if (printer)
++ {
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++ }
++ else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Get the user that is requesting the subscription...
++ */
++
++ username = get_username(con);
++
++ /*
++ * Find the first subscription group attribute; return if we have
++ * none...
++ */
++
++ for (attr = con->request->attrs; attr; attr = attr->next)
++ if (attr->group_tag == IPP_TAG_SUBSCRIPTION)
++ break;
++
++ if (!attr)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("No subscription attributes in request!"));
++ return;
++ }
++
++ /*
++ * Process the subscription attributes in the request...
++ */
++
++ con->response->request.status.status_code = IPP_BAD_REQUEST;
++
++ while (attr)
++ {
++ recipient = NULL;
++ pullmethod = NULL;
++ user_data = NULL;
++ interval = 0;
++ lease = DefaultLeaseDuration;
++ jobid = 0;
++ mask = CUPSD_EVENT_NONE;
++
++ while (attr && attr->group_tag != IPP_TAG_ZERO)
++ {
++ if (!strcmp(attr->name, "notify-recipient") &&
++ attr->value_tag == IPP_TAG_URI)
++ {
++ /*
++ * Validate the recipient scheme against the ServerBin/notifier
++ * directory...
++ */
++
++ char notifier[1024]; /* Notifier filename */
++
++
++ recipient = attr->values[0].string.text;
++
++ if (httpSeparateURI(HTTP_URI_CODING_ALL, recipient,
++ scheme, sizeof(scheme), userpass, sizeof(userpass),
++ host, sizeof(host), &port,
++ resource, sizeof(resource)) < HTTP_URI_OK)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Bad notify-recipient URI \"%s\"!"), recipient);
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
++ "notify-status-code", IPP_URI_SCHEME);
++ return;
++ }
++
++ snprintf(notifier, sizeof(notifier), "%s/notifier/%s", ServerBin,
++ scheme);
++ if (access(notifier, X_OK))
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("notify-recipient URI \"%s\" uses unknown scheme!"),
++ recipient);
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
++ "notify-status-code", IPP_URI_SCHEME);
++ return;
++ }
++ }
++ else if (!strcmp(attr->name, "notify-pull-method") &&
++ attr->value_tag == IPP_TAG_KEYWORD)
++ {
++ pullmethod = attr->values[0].string.text;
++
++ if (strcmp(pullmethod, "ippget"))
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Bad notify-pull-method \"%s\"!"), pullmethod);
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
++ "notify-status-code", IPP_ATTRIBUTES);
++ return;
++ }
++ }
++ else if (!strcmp(attr->name, "notify-charset") &&
++ attr->value_tag == IPP_TAG_CHARSET &&
++ strcmp(attr->values[0].string.text, "us-ascii") &&
++ strcmp(attr->values[0].string.text, "utf-8"))
++ {
++ send_ipp_status(con, IPP_CHARSET,
++ _("Character set \"%s\" not supported!"),
++ attr->values[0].string.text);
++ return;
++ }
++ else if (!strcmp(attr->name, "notify-natural-language") &&
++ (attr->value_tag != IPP_TAG_LANGUAGE ||
++ strcmp(attr->values[0].string.text, DefaultLanguage)))
++ {
++ send_ipp_status(con, IPP_CHARSET,
++ _("Language \"%s\" not supported!"),
++ attr->values[0].string.text);
++ return;
++ }
++ else if (!strcmp(attr->name, "notify-user-data") &&
++ attr->value_tag == IPP_TAG_STRING)
++ {
++ if (attr->num_values > 1 || attr->values[0].unknown.length > 63)
++ {
++ send_ipp_status(con, IPP_REQUEST_VALUE,
++ _("The notify-user-data value is too large "
++ "(%d > 63 octets)!"),
++ attr->values[0].unknown.length);
++ return;
++ }
++
++ user_data = attr;
++ }
++ else if (!strcmp(attr->name, "notify-events") &&
++ attr->value_tag == IPP_TAG_KEYWORD)
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ mask |= cupsdEventValue(attr->values[i].string.text);
++ }
++ else if (!strcmp(attr->name, "notify-lease-duration") &&
++ attr->value_tag == IPP_TAG_INTEGER)
++ lease = attr->values[0].integer;
++ else if (!strcmp(attr->name, "notify-time-interval") &&
++ attr->value_tag == IPP_TAG_INTEGER)
++ interval = attr->values[0].integer;
++ else if (!strcmp(attr->name, "notify-job-id") &&
++ attr->value_tag == IPP_TAG_INTEGER)
++ jobid = attr->values[0].integer;
++
++ attr = attr->next;
++ }
++
++ if (recipient)
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
++ if (pullmethod)
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "pullmethod=\"%s\"", pullmethod);
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-lease-duration=%d", lease);
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "notify-time-interval=%d", interval);
++
++ if (!recipient && !pullmethod)
++ break;
++
++ if (mask == CUPSD_EVENT_NONE)
++ {
++ if (jobid)
++ mask = CUPSD_EVENT_JOB_COMPLETED;
++ else if (printer)
++ mask = CUPSD_EVENT_PRINTER_STATE_CHANGED;
++ else
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("notify-events not specified!"));
++ return;
++ }
++ }
++
++ if (MaxLeaseDuration && (lease == 0 || lease > MaxLeaseDuration))
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "create_subscription: Limiting notify-lease-duration to "
++ "%d seconds.",
++ MaxLeaseDuration);
++ lease = MaxLeaseDuration;
++ }
++
++ if (jobid)
++ {
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found!"), jobid);
++ return;
++ }
++ }
++ else
++ job = NULL;
++
++ sub = cupsdAddSubscription(mask, printer, job, recipient, 0);
++
++ if (job)
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
++ sub->id, job->id);
++ else if (printer)
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Added subscription %d for printer \"%s\"",
++ sub->id, printer->name);
++ else
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
++ sub->id);
++
++ sub->interval = interval;
++ sub->lease = lease;
++ sub->expire = lease ? time(NULL) + lease : 0;
++
++ cupsdSetString(&sub->owner, username);
++
++ if (user_data)
++ {
++ sub->user_data_len = user_data->values[0].unknown.length;
++ memcpy(sub->user_data, user_data->values[0].unknown.data,
++ sub->user_data_len);
++ }
++
++ ippAddSeparator(con->response);
++ ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
++ "notify-subscription-id", sub->id);
++
++ con->response->request.status.status_code = IPP_OK;
++
++ if (attr)
++ attr = attr->next;
++ }
++
++ cupsdSaveAllSubscriptions();
++
++}
++
++
++/*
++ * 'delete_printer()' - Remove a printer or class from the system.
++ */
++
++static void
++delete_printer(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - URI of printer or class */
++{
++ http_status_t status; /* Policy status */
++ const char *dest; /* Destination */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *printer; /* Printer/class */
++ char filename[1024]; /* Script/PPD filename */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Do we have a valid URI?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Remove old jobs...
++ */
++
++ cupsdCancelJobs(dest, NULL, 1);
++
++ /*
++ * Remove old subscriptions and send a "deleted printer" event...
++ */
++
++ cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
++ "%s \"%s\" deleted by \"%s\".",
++ (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
++ dest, get_username(con));
++
++ cupsdExpireSubscriptions(printer, NULL);
++
++ /*
++ * Remove any old PPD or script files...
++ */
++
++ snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
++ unlink(filename);
++
++ snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
++ unlink(filename);
++
++ if (dtype & CUPS_PRINTER_CLASS)
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".", dest,
++ get_username(con));
++
++ cupsdDeletePrinter(printer, 0);
++ cupsdSaveAllClasses();
++ }
++ else
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".", dest,
++ get_username(con));
++
++ cupsdDeletePrinter(printer, 0);
++ cupsdSaveAllPrinters();
++ }
++
++ cupsdWritePrintcap();
++
++ /*
++ * Return with no errors...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_default()' - Get the default destination.
++ */
++
++static void
++get_default(cupsd_client_t *con) /* I - Client connection */
++{
++ http_status_t status; /* Policy status */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->http.fd);
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ if (DefaultPrinter)
++ {
++ ra = create_requested_array(con->request);
++
++ copy_printer_attrs(con, DefaultPrinter, ra);
++
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++ }
++ else
++ send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
++}
++
++
++/*
++ * 'get_devices()' - Get the list of available devices on the local system.
++ */
++
++static void
++get_devices(cupsd_client_t *con) /* I - Client connection */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *limit, /* Limit attribute */
++ *requested; /* requested-attributes attribute */
++ char command[1024], /* cups-deviced command */
++ options[1024], /* Options to pass to command */
++ requested_str[256];
++ /* String for requested attributes */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->http.fd);
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Run cups-deviced command with the given options...
++ */
++
++ limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
++ requested = ippFindAttribute(con->request, "requested-attributes",
++ IPP_TAG_KEYWORD);
++
++ if (requested)
++ url_encode_attr(requested, requested_str, sizeof(requested_str));
++ else
++ strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
++
++ snprintf(command, sizeof(command), "%s/daemon/cups-deviced", ServerBin);
++ snprintf(options, sizeof(options),
++ "%d+%d+%d+%s",
++ con->request->request.op.request_id,
++ limit ? limit->values[0].integer : 0, (int)User,
++ requested_str);
++
++ if (cupsdSendCommand(con, command, options, 1))
++ {
++ /*
++ * Command started successfully, don't send an IPP response here...
++ */
++
++ ippDelete(con->response);
++ con->response = NULL;
++ }
++ else
++ {
++ /*
++ * Command failed, return "internal error" so the user knows something
++ * went wrong...
++ */
++
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("cups-deviced failed to execute."));
++ }
++}
++
++
++/*
++ * 'get_job_attrs()' - Get job attributes.
++ */
++
++static void
++get_job_attrs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job URI */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ int jobid; /* Job ID */
++ cupsd_job_t *job; /* Current job */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Copy attributes...
++ */
++
++ cupsdLoadJob(job);
++
++ ra = create_requested_array(con->request);
++ copy_job_attrs(con, job, ra);
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_jobs()' - Get a list of jobs for the specified printer.
++ */
++
++static void
++get_jobs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ const char *dest; /* Destination */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ cups_ptype_t dmask; /* Destination type mask */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ int completed; /* Completed jobs? */
++ int first_job_id; /* First job ID */
++ int limit; /* Maximum number of jobs to return */
++ int count; /* Number of jobs that match */
++ cupsd_job_t *job; /* Current job pointer */
++ cupsd_printer_t *printer; /* Printer */
++ cups_array_t *list; /* Which job list... */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
++ uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!strcmp(resource, "/") ||
++ (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6))
++ {
++ dest = NULL;
++ dtype = (cups_ptype_t)0;
++ dmask = (cups_ptype_t)0;
++ printer = NULL;
++ }
++ else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
++ {
++ dest = NULL;
++ dtype = (cups_ptype_t)0;
++ dmask = CUPS_PRINTER_CLASS;
++ printer = NULL;
++ }
++ else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
++ {
++ dest = NULL;
++ dtype = CUPS_PRINTER_CLASS;
++ dmask = CUPS_PRINTER_CLASS;
++ printer = NULL;
++ }
++ else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++ else
++ {
++ dtype &= CUPS_PRINTER_CLASS;
++ dmask = CUPS_PRINTER_CLASS;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if (printer)
++ {
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++ }
++ else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * See if the "which-jobs" attribute have been specified...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "which-jobs",
++ IPP_TAG_KEYWORD)) != NULL &&
++ !strcmp(attr->values[0].string.text, "completed"))
++ {
++ completed = 1;
++ list = Jobs;
++ }
++ else if (attr && !strcmp(attr->values[0].string.text, "all"))
++ {
++ completed = 0;
++ list = Jobs;
++ }
++ else
++ {
++ completed = 0;
++ list = ActiveJobs;
++ }
++
++ /*
++ * See if they want to limit the number of jobs reported...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "limit",
++ IPP_TAG_INTEGER)) != NULL)
++ limit = attr->values[0].integer;
++ else
++ limit = 1000000;
++
++ if ((attr = ippFindAttribute(con->request, "first-job-id",
++ IPP_TAG_INTEGER)) != NULL)
++ first_job_id = attr->values[0].integer;
++ else
++ first_job_id = 1;
++
++ /*
++ * See if we only want to see jobs for a specific user...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "my-jobs",
++ IPP_TAG_BOOLEAN)) != NULL &&
++ attr->values[0].boolean)
++ strlcpy(username, get_username(con), sizeof(username));
++ else
++ username[0] = '\0';
++
++ ra = create_requested_array(con->request);
++
++ /*
++ * OK, build a list of jobs for this printer...
++ */
++
++ for (count = 0, job = (cupsd_job_t *)cupsArrayFirst(list);
++ count < limit && job;
++ job = (cupsd_job_t *)cupsArrayNext(list))
++ {
++ /*
++ * Filter out jobs that don't match...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
++
++ if ((dest && strcmp(job->dest, dest)) &&
++ (!job->printer || !dest || strcmp(job->printer->name, dest)))
++ continue;
++ if ((job->dtype & dmask) != dtype &&
++ (!job->printer || (job->printer->type & dmask) != dtype))
++ continue;
++ if (username[0] && strcasecmp(username, job->username))
++ continue;
++
++ if (completed && job->state_value <= IPP_JOB_STOPPED)
++ continue;
++
++ if (job->id < first_job_id)
++ continue;
++
++ cupsdLoadJob(job);
++
++ if (!job->attrs)
++ continue;
++
++ if (count > 0)
++ ippAddSeparator(con->response);
++
++ count ++;
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count = %d", count);
++
++ copy_job_attrs(con, job, ra);
++ }
++
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_notifications()' - Get events for a subscription.
++ */
++
++static void
++get_notifications(cupsd_client_t *con) /* I - Client connection */
++{
++ int i, j; /* Looping vars */
++ http_status_t status; /* Policy status */
++ cupsd_subscription_t *sub; /* Subscription */
++ ipp_attribute_t *ids, /* notify-subscription-ids */
++ *sequences; /* notify-sequence-numbers */
++ int min_seq; /* Minimum sequence number */
++ int interval; /* Poll interval */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_subscription_attrs(con=%p[%d])",
++ con, con->http.fd);
++
++ /*
++ * Get subscription attributes...
++ */
++
++ ids = ippFindAttribute(con->request, "notify-subscription-ids",
++ IPP_TAG_INTEGER);
++ sequences = ippFindAttribute(con->request, "notify-sequence-numbers",
++ IPP_TAG_INTEGER);
++
++ if (!ids)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Missing notify-subscription-ids attribute!"));
++ return;
++ }
++
++ /*
++ * Are the subscription IDs valid?
++ */
++
++ for (i = 0, interval = 60; i < ids->num_values; i ++)
++ {
++ if ((sub = cupsdFindSubscription(ids->values[i].integer)) == NULL)
++ {
++ /*
++ * Bad subscription ID...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("notify-subscription-id %d no good!"),
++ ids->values[i].integer);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
++ DefaultPolicyPtr,
++ con, sub->owner)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Check the subscription type and update the interval accordingly.
++ */
++
++ if (sub->job && sub->job->state_value == IPP_JOB_PROCESSING &&
++ interval > 10)
++ interval = 10;
++ else if (sub->job && sub->job->state_value >= IPP_JOB_STOPPED)
++ interval = 0;
++ else if (sub->dest && sub->dest->state == IPP_PRINTER_PROCESSING &&
++ interval > 30)
++ interval = 30;
++ }
++
++ /*
++ * Tell the client to poll again in N seconds...
++ */
++
++ if (interval > 0)
++ ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
++ "notify-get-interval", interval);
++
++ ippAddInteger(con->response, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
++ "printer-up-time", time(NULL));
++
++ /*
++ * Copy the subscription event attributes to the response.
++ */
++
++ con->response->request.status.status_code =
++ interval ? IPP_OK : IPP_OK_EVENTS_COMPLETE;
++
++ for (i = 0; i < ids->num_values; i ++)
++ {
++ /*
++ * Get the subscription and sequence number...
++ */
++
++ sub = cupsdFindSubscription(ids->values[i].integer);
++
++ if (sequences && i < sequences->num_values)
++ min_seq = sequences->values[i].integer;
++ else
++ min_seq = 1;
++
++ /*
++ * If we don't have any new events, nothing to do here...
++ */
++
++ if (min_seq > (sub->first_event_id + sub->num_events))
++ continue;
++
++ /*
++ * Otherwise copy all of the new events...
++ */
++
++ if (sub->first_event_id > min_seq)
++ j = 0;
++ else
++ j = min_seq - sub->first_event_id;
++
++ for (; j < sub->num_events; j ++)
++ {
++ ippAddSeparator(con->response);
++
++ copy_attrs(con->response, sub->events[j]->attrs, NULL,
++ IPP_TAG_EVENT_NOTIFICATION, 0);
++ }
++ }
++}
++
++
++/*
++ * 'get_ppds()' - Get the list of PPD files on the local system.
++ */
++
++static void
++get_ppds(cupsd_client_t *con) /* I - Client connection */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *limit, /* Limit attribute */
++ *make, /* ppd-make attribute */
++ *requested; /* requested-attributes attribute */
++ char command[1024], /* cups-deviced command */
++ options[1024], /* Options to pass to command */
++ requested_str[256],
++ /* String for requested attributes */
++ make_str[256]; /* Escaped ppd-make string */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->http.fd);
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Run cups-driverd command with the given options...
++ */
++
++ limit = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER);
++ make = ippFindAttribute(con->request, "ppd-make", IPP_TAG_TEXT);
++ requested = ippFindAttribute(con->request, "requested-attributes",
++ IPP_TAG_KEYWORD);
++
++ if (requested)
++ url_encode_attr(requested, requested_str, sizeof(requested_str));
++ else
++ strlcpy(requested_str, "requested-attributes=all", sizeof(requested_str));
++
++ if (make)
++ url_encode_attr(make, make_str, sizeof(make_str));
++ else
++ make_str[0] = '\0';
++
++ snprintf(command, sizeof(command), "%s/daemon/cups-driverd", ServerBin);
++ snprintf(options, sizeof(options), "list+%d+%d+%s%s%s",
++ con->request->request.op.request_id,
++ limit ? limit->values[0].integer : 0,
++ requested_str, make ? "%20" : "", make_str);
++
++ if (cupsdSendCommand(con, command, options, 0))
++ {
++ /*
++ * Command started successfully, don't send an IPP response here...
++ */
++
++ ippDelete(con->response);
++ con->response = NULL;
++ }
++ else
++ {
++ /*
++ * Command failed, return "internal error" so the user knows something
++ * went wrong...
++ */
++
++ send_ipp_status(con, IPP_INTERNAL_ERROR,
++ _("cups-driverd failed to execute."));
++ }
++}
++
++
++/*
++ * 'get_printer_attrs()' - Get printer attributes.
++ */
++
++static void
++get_printer_attrs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *printer; /* Printer/class */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!cupsdValidateDest(host, resource, &dtype, &printer))
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Send the attributes...
++ */
++
++ ra = create_requested_array(con->request);
++
++ copy_printer_attrs(con, printer, ra);
++
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_printers()' - Get a list of printers or classes.
++ */
++
++static void
++get_printers(cupsd_client_t *con, /* I - Client connection */
++ int type) /* I - 0 or CUPS_PRINTER_CLASS */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ int limit; /* Maximum number of printers to return */
++ int count; /* Number of printers that match */
++ cupsd_printer_t *printer; /* Current printer pointer */
++ int printer_type, /* printer-type attribute */
++ printer_mask; /* printer-type-mask attribute */
++ char *location; /* Location string */
++ const char *username; /* Current user */
++ char *first_printer_name; /* first-printer-name attribute */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
++ con->http.fd, type);
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Check for printers...
++ */
++
++ if (!Printers || !cupsArrayCount(Printers))
++ {
++ send_ipp_status(con, IPP_NOT_FOUND, _("No destinations added."));
++ return;
++ }
++
++ /*
++ * See if they want to limit the number of printers reported...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "limit",
++ IPP_TAG_INTEGER)) != NULL)
++ limit = attr->values[0].integer;
++ else
++ limit = 10000000;
++
++ if ((attr = ippFindAttribute(con->request, "first-printer-name",
++ IPP_TAG_NAME)) != NULL)
++ first_printer_name = attr->values[0].string.text;
++ else
++ first_printer_name = NULL;
++
++ /*
++ * Support filtering...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "printer-type",
++ IPP_TAG_ENUM)) != NULL)
++ printer_type = attr->values[0].integer;
++ else
++ printer_type = 0;
++
++ if ((attr = ippFindAttribute(con->request, "printer-type-mask",
++ IPP_TAG_ENUM)) != NULL)
++ printer_mask = attr->values[0].integer;
++ else
++ printer_mask = 0;
++
++ if ((attr = ippFindAttribute(con->request, "printer-location",
++ IPP_TAG_TEXT)) != NULL)
++ location = attr->values[0].string.text;
++ else
++ location = NULL;
++
++ if (con->username[0])
++ username = con->username;
++ else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
++ IPP_TAG_NAME)) != NULL)
++ username = attr->values[0].string.text;
++ else
++ username = NULL;
++
++ ra = create_requested_array(con->request);
++
++ /*
++ * OK, build a list of printers for this printer...
++ */
++
++ if (first_printer_name)
++ {
++ if ((printer = cupsdFindDest(first_printer_name)) == NULL)
++ printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
++ }
++ else
++ printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
++
++ for (count = 0;
++ count < limit && printer;
++ printer = (cupsd_printer_t *)cupsArrayNext(Printers))
++ {
++ if ((!type || (printer->type & CUPS_PRINTER_CLASS) == type) &&
++ (printer->type & printer_mask) == printer_type &&
++ (!location || !printer->location ||
++ !strcasecmp(printer->location, location)))
++ {
++ /*
++ * If HideImplicitMembers is enabled, see if this printer or class
++ * is a member of an implicit class...
++ */
++
++ if (ImplicitClasses && HideImplicitMembers &&
++ printer->in_implicit_class)
++ continue;
++
++ /*
++ * If a username is specified, see if it is allowed or denied
++ * access...
++ */
++
++ if (printer->num_users && username && !user_allowed(printer, username))
++ continue;
++
++ /*
++ * Add the group separator as needed...
++ */
++
++ if (count > 0)
++ ippAddSeparator(con->response);
++
++ count ++;
++
++ /*
++ * Send the attributes...
++ */
++
++ copy_printer_attrs(con, printer, ra);
++ }
++ }
++
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_subscription_attrs()' - Get subscription attributes.
++ */
++
++static void
++get_subscription_attrs(
++ cupsd_client_t *con, /* I - Client connection */
++ int sub_id) /* I - Subscription ID */
++{
++ http_status_t status; /* Policy status */
++ cupsd_subscription_t *sub; /* Subscription */
++ cups_array_t *ra; /* Requested attributes array */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "get_subscription_attrs(con=%p[%d], sub_id=%d)",
++ con, con->http.fd, sub_id);
++
++ /*
++ * Is the subscription ID valid?
++ */
++
++ if ((sub = cupsdFindSubscription(sub_id)) == NULL)
++ {
++ /*
++ * Bad subscription ID...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("notify-subscription-id %d no good!"), sub_id);
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
++ DefaultPolicyPtr,
++ con, sub->owner)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Copy the subscription attributes to the response using the
++ * requested-attributes attribute that may be provided by the client.
++ */
++
++ ra = create_requested_array(con->request);
++
++ copy_subscription_attrs(con, sub, ra);
++
++ cupsArrayDelete(ra);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'get_subscriptions()' - Get subscriptions.
++ */
++
++static void
++get_subscriptions(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer/job URI */
++{
++ http_status_t status; /* Policy status */
++ int count; /* Number of subscriptions */
++ int limit; /* Limit */
++ cupsd_subscription_t *sub; /* Subscription */
++ cups_array_t *ra; /* Requested attributes array */
++ ipp_attribute_t *attr; /* Attribute */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_job_t *job; /* Job pointer */
++ cupsd_printer_t *printer; /* Printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "get_subscriptions(con=%p[%d], uri=%s)",
++ con, con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!strcmp(resource, "/") ||
++ (!strncmp(resource, "/jobs", 5) && strlen(resource) <= 6) ||
++ (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10) ||
++ (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9))
++ {
++ printer = NULL;
++ job = NULL;
++ }
++ else if (!strncmp(resource, "/jobs/", 6) && resource[6])
++ {
++ printer = NULL;
++ job = cupsdFindJob(atoi(resource + 6));
++
++ if (!job)
++ {
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist!"),
++ resource + 6);
++ return;
++ }
++ }
++ else if (!cupsdValidateDest(host, resource, &dtype, &printer))
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++ else if ((attr = ippFindAttribute(con->request, "notify-job-id",
++ IPP_TAG_INTEGER)) != NULL)
++ {
++ job = cupsdFindJob(attr->values[0].integer);
++
++ if (!job)
++ {
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"),
++ attr->values[0].integer);
++ return;
++ }
++ }
++ else
++ job = NULL;
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
++ DefaultPolicyPtr,
++ con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Copy the subscription attributes to the response using the
++ * requested-attributes attribute that may be provided by the client.
++ */
++
++ ra = create_requested_array(con->request);
++
++ if ((attr = ippFindAttribute(con->request, "limit",
++ IPP_TAG_INTEGER)) != NULL)
++ limit = attr->values[0].integer;
++ else
++ limit = 0;
++
++ /*
++ * See if we only want to see subscriptions for a specific user...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "my-subscriptions",
++ IPP_TAG_BOOLEAN)) != NULL &&
++ attr->values[0].boolean)
++ strlcpy(username, get_username(con), sizeof(username));
++ else
++ username[0] = '\0';
++
++ for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions), count = 0;
++ sub;
++ sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
++ if ((!printer || sub->dest == printer) && (!job || sub->job == job) &&
++ (!username[0] || !strcasecmp(username, sub->owner)))
++ {
++ ippAddSeparator(con->response);
++ copy_subscription_attrs(con, sub, ra);
++
++ count ++;
++ if (limit && count >= limit)
++ break;
++ }
++
++ cupsArrayDelete(ra);
++
++ if (count)
++ con->response->request.status.status_code = IPP_OK;
++ else
++ send_ipp_status(con, IPP_NOT_FOUND, _("No subscriptions found."));
++}
++
++
++/*
++ * 'get_username()' - Get the username associated with a request.
++ */
++
++static const char * /* O - Username */
++get_username(cupsd_client_t *con) /* I - Connection */
++{
++ ipp_attribute_t *attr; /* Attribute */
++
++
++ if (con->username[0])
++ return (con->username);
++ else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
++ IPP_TAG_NAME)) != NULL)
++ return (attr->values[0].string.text);
++ else
++ return ("anonymous");
++}
++
++
++/*
++ * 'hold_job()' - Hold a print job.
++ */
++
++static void
++hold_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job or Printer URI */
++{
++ ipp_attribute_t *attr, /* Current job-hold-until */
++ *newattr; /* New job-hold-until */
++ int jobid; /* Job ID */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_job_t *job; /* Job information */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->http.fd,
++ uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * Hold the job and return...
++ */
++
++ cupsdHoldJob(job);
++
++ cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
++ "Job held by user.");
++
++ if ((newattr = ippFindAttribute(con->request, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
++
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++
++ if (attr)
++ {
++ /*
++ * Free the old hold value and copy the new one over...
++ */
++
++ _cupsStrFree(attr->values[0].string.text);
++
++ if (newattr)
++ {
++ attr->value_tag = newattr->value_tag;
++ attr->values[0].string.text =
++ _cupsStrAlloc(newattr->values[0].string.text);
++ }
++ else
++ {
++ attr->value_tag = IPP_TAG_KEYWORD;
++ attr->values[0].string.text = _cupsStrAlloc("indefinite");
++ }
++
++ /*
++ * Hold job until specified time...
++ */
++
++ cupsdSetJobHoldUntil(job, attr->values[0].string.text);
++
++ cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
++ "Job job-hold-until value changed by user.");
++ }
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid,
++ username);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'move_job()' - Move a job to a new destination.
++ */
++
++static void
++move_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job URI */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ int jobid; /* Job ID */
++ cupsd_job_t *job; /* Current job */
++ const char *src; /* Source printer/class */
++ cups_ptype_t stype, /* Source type (printer or class) */
++ dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_printer_t *sprinter, /* Source printer */
++ *dprinter; /* Destination printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->http.fd,
++ uri->values[0].string.text);
++
++ /*
++ * Get the new printer or class...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-printer-uri",
++ IPP_TAG_URI)) == NULL)
++ {
++ /*
++ * Need job-printer-uri...
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("job-printer-uri attribute missing!"));
++ return;
++ }
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!cupsdValidateDest(host, resource, &dtype, &dprinter))
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ /*
++ * Move all jobs...
++ */
++
++ if ((src = cupsdValidateDest(host, resource, &stype, &sprinter)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ job = NULL;
++ }
++ else
++ {
++ /*
++ * Otherwise, just move a single job...
++ */
++
++ if ((job = cupsdFindJob(attr->values[0].integer)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("Job #%d does not exist!"), attr->values[0].integer);
++ return;
++ }
++ else
++ {
++ /*
++ * Job found, initialize source pointers...
++ */
++
++ src = NULL;
++ sprinter = NULL;
++ }
++ }
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ jobid = atoi(resource + 6);
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("Job #%d does not exist!"), jobid);
++ return;
++ }
++ else
++ {
++ /*
++ * Job found, initialize source pointers...
++ */
++
++ src = NULL;
++ sprinter = NULL;
++ }
++ }
++
++ /*
++ * Now move the job or jobs...
++ */
++
++ if (job)
++ {
++ /*
++ * See if the job has been completed...
++ */
++
++ if (job->state_value > IPP_JOB_STOPPED)
++ {
++ /*
++ * Return a "not-possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is finished and cannot be altered!"),
++ job->id);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * Move the job to a different printer or class...
++ */
++
++ cupsdMoveJob(job, dprinter);
++ }
++ else
++ {
++ /*
++ * Got the source printer, now look through the jobs...
++ */
++
++ for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
++ job;
++ job = (cupsd_job_t *)cupsArrayNext(Jobs))
++ {
++ /*
++ * See if the job is pointing at the source printer or has not been
++ * completed...
++ */
++
++ if (strcasecmp(job->dest, src) ||
++ job->state_value > IPP_JOB_STOPPED)
++ continue;
++
++ /*
++ * See if the job can be moved by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ continue;
++
++ /*
++ * Move the job to a different printer or class...
++ */
++
++ cupsdMoveJob(job, dprinter);
++ }
++ }
++
++ /*
++ * Start jobs if possible...
++ */
++
++ cupsdCheckJobs();
++
++ /*
++ * Return with "everything is OK" status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'ppd_parse_line()' - Parse a PPD default line.
++ */
++
++static int /* O - 0 on success, -1 on failure */
++ppd_parse_line(const char *line, /* I - Line */
++ char *option, /* O - Option name */
++ int olen, /* I - Size of option name */
++ char *choice, /* O - Choice name */
++ int clen) /* I - Size of choice name */
++{
++ /*
++ * Verify this is a default option line...
++ */
++
++ if (strncmp(line, "*Default", 8))
++ return (-1);
++
++ /*
++ * Read the option name...
++ */
++
++ for (line += 8, olen --; isalnum(*line & 255); line ++)
++ if (olen > 0)
++ {
++ *option++ = *line;
++ olen --;
++ }
++
++ *option = '\0';
++
++ /*
++ * Skip everything else up to the colon (:)...
++ */
++
++ while (*line && *line != ':')
++ line ++;
++
++ if (!*line)
++ return (-1);
++
++ line ++;
++
++ /*
++ * Now grab the option choice, skipping leading whitespace...
++ */
++
++ while (isspace(*line & 255))
++ line ++;
++
++ for (clen --; isalnum(*line & 255); line ++)
++ if (clen > 0)
++ {
++ *choice++ = *line;
++ clen --;
++ }
++
++ *choice = '\0';
++
++ /*
++ * Return with no errors...
++ */
++
++ return (0);
++}
++
++
++/*
++ * 'print_job()' - Print a file to a printer or class.
++ */
++
++static void
++print_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ ipp_attribute_t *format; /* Document-format attribute */
++ cupsd_job_t *job; /* New job */
++ char filename[1024]; /* Job filename */
++ mime_type_t *filetype; /* Type of file */
++ char super[MIME_MAX_SUPER], /* Supertype of file */
++ type[MIME_MAX_TYPE], /* Subtype of file */
++ mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
++ /* Textual name of mime type */
++ cupsd_printer_t *printer; /* Printer data */
++ struct stat fileinfo; /* File information */
++ int kbytes; /* Size of file */
++ int compression; /* Document compression */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->http.fd,
++ uri->values[0].string.text);
++
++ /*
++ * Validate print file attributes, for now just document-format and
++ * compression (CUPS only supports "none" and "gzip")...
++ */
++
++ compression = CUPS_FILE_NONE;
++
++ if ((attr = ippFindAttribute(con->request, "compression",
++ IPP_TAG_KEYWORD)) != NULL)
++ {
++ if (strcmp(attr->values[0].string.text, "none")
++#ifdef HAVE_LIBZ
++ && strcmp(attr->values[0].string.text, "gzip")
++#endif /* HAVE_LIBZ */
++ )
++ {
++ send_ipp_status(con, IPP_ATTRIBUTES,
++ _("Unsupported compression \"%s\"!"),
++ attr->values[0].string.text);
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
++ "compression", NULL, attr->values[0].string.text);
++ return;
++ }
++
++#ifdef HAVE_LIBZ
++ if (!strcmp(attr->values[0].string.text, "gzip"))
++ compression = CUPS_FILE_GZIP;
++#endif /* HAVE_LIBZ */
++ }
++
++ /*
++ * Do we have a file to print?
++ */
++
++ if (!con->filename)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
++ return;
++ }
++
++ /*
++ * Is it a format we support?
++ */
++
++ if ((format = ippFindAttribute(con->request, "document-format",
++ IPP_TAG_MIMETYPE)) != NULL)
++ {
++ /*
++ * Grab format from client...
++ */
++
++ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Could not scan type \"%s\"!"),
++ format->values[0].string.text);
++ return;
++ }
++ }
++ else
++ {
++ /*
++ * No document format attribute? Auto-type it!
++ */
++
++ strcpy(super, "application");
++ strcpy(type, "octet-stream");
++ }
++
++ if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
++ {
++ /*
++ * Auto-type the file...
++ */
++
++ ipp_attribute_t *doc_name; /* document-name attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: auto-typing file...");
++
++ doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
++ filetype = mimeFileType(MimeDatabase, con->filename,
++ doc_name ? doc_name->values[0].string.text : NULL,
++ &compression);
++
++ if (filetype)
++ {
++ /*
++ * Replace the document-format attribute value with the auto-typed one.
++ */
++
++ snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
++ filetype->type);
++
++ if (format)
++ {
++ _cupsStrFree(format->values[0].string.text);
++
++ format->values[0].string.text = _cupsStrAlloc(mimetype);
++ }
++ else
++ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
++ "document-format", NULL, mimetype);
++ }
++ else
++ filetype = mimeType(MimeDatabase, super, type);
++ }
++ else
++ filetype = mimeType(MimeDatabase, super, type);
++
++ if (!filetype)
++ {
++ send_ipp_status(con, IPP_DOCUMENT_FORMAT,
++ _("Unsupported format \'%s/%s\'!"), super, type);
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Hint: Do you have the raw file printing rules enabled?");
++
++ if (format)
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
++ "document-format", NULL, format->values[0].string.text);
++
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "print_job: request file type is %s/%s.",
++ filetype->super, filetype->type);
++
++ /*
++ * Read any embedded job ticket info from PS files...
++ */
++
++ if (!strcasecmp(filetype->super, "application") &&
++ !strcasecmp(filetype->type, "postscript"))
++ read_ps_job_ticket(con);
++
++ /*
++ * Create the job object...
++ */
++
++ if ((job = add_job(con, uri, &printer, filetype)) == NULL)
++ return;
++
++ /*
++ * Update quota data...
++ */
++
++ if (stat(con->filename, &fileinfo))
++ kbytes = 0;
++ else
++ kbytes = (fileinfo.st_size + 1023) / 1024;
++
++ cupsdUpdateQuota(printer, job->username, 0, kbytes);
++
++ if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
++ IPP_TAG_INTEGER)) != NULL)
++ attr->values[0].integer += kbytes;
++
++ /*
++ * Add the job file...
++ */
++
++ if (add_file(con, job, filetype, compression))
++ return;
++
++ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
++ job->num_files);
++ rename(con->filename, filename);
++ cupsdClearString(&con->filename);
++
++ /*
++ * See if we need to add the ending sheet...
++ */
++
++ attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
++
++ if (!(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
++ attr && attr->num_values > 1)
++ {
++ /*
++ * Yes...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Adding end banner page \"%s\" to job %d.",
++ attr->values[1].string.text, job->id);
++
++ kbytes = copy_banner(con, job, attr->values[1].string.text);
++
++ cupsdUpdateQuota(printer, job->username, 0, kbytes);
++ }
++
++ /*
++ * Log and save the job...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d queued on \"%s\" by \"%s\".", job->id,
++ job->dest, job->username);
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Job %d hold_until = %d", job->id,
++ (int)job->hold_until);
++
++ cupsdSaveJob(job);
++
++ /*
++ * Start the job if possible...
++ */
++
++ cupsdCheckJobs();
++}
++
++
++/*
++ * 'read_ps_job_ticket()' - Reads a job ticket embedded in a PS file.
++ *
++ * This function only gets called when printing a single PostScript
++ * file using the Print-Job operation. It doesn't work for Create-Job +
++ * Send-File, since the job attributes need to be set at job creation
++ * time for banners to work. The embedded PS job ticket stuff is here
++ * only to allow the Windows printer driver for CUPS to pass in JCL
++ * options and IPP attributes which otherwise would be lost.
++ *
++ * The format of a PS job ticket is simple:
++ *
++ * %cupsJobTicket: attr1=value1 attr2=value2 ... attrN=valueN
++ *
++ * %cupsJobTicket: attr1=value1
++ * %cupsJobTicket: attr2=value2
++ * ...
++ * %cupsJobTicket: attrN=valueN
++ *
++ * Job ticket lines must appear immediately after the first line that
++ * specifies PostScript format (%!PS-Adobe-3.0), and CUPS will stop
++ * looking for job ticket info when it finds a line that does not begin
++ * with "%cupsJobTicket:".
++ *
++ * The maximum length of a job ticket line, including the prefix, is
++ * 255 characters to conform with the Adobe DSC.
++ *
++ * Read-only attributes are rejected with a notice to the error log in
++ * case a malicious user tries anything. Since the job ticket is read
++ * prior to attribute validation in print_job(), job ticket attributes
++ * will go through the same validation as IPP attributes...
++ */
++
++static void
++read_ps_job_ticket(cupsd_client_t *con) /* I - Client connection */
++{
++ cups_file_t *fp; /* File to read from */
++ char line[256]; /* Line data */
++ int num_options; /* Number of options */
++ cups_option_t *options; /* Options */
++ ipp_t *ticket; /* New attributes */
++ ipp_attribute_t *attr, /* Current attribute */
++ *attr2, /* Job attribute */
++ *prev2; /* Previous job attribute */
++
++
++ /*
++ * First open the print file...
++ */
++
++ if ((fp = cupsFileOpen(con->filename, "rb")) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "read_ps_job_ticket: Unable to open PostScript print file "
++ "- %s",
++ strerror(errno));
++ return;
++ }
++
++ /*
++ * Skip the first line...
++ */
++
++ if (cupsFileGets(fp, line, sizeof(line)) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "read_ps_job_ticket: Unable to read from PostScript print "
++ "file - %s",
++ strerror(errno));
++ cupsFileClose(fp);
++ return;
++ }
++
++ if (strncmp(line, "%!PS-Adobe-", 11))
++ {
++ /*
++ * Not a DSC-compliant file, so no job ticket info will be available...
++ */
++
++ cupsFileClose(fp);
++ return;
++ }
++
++ /*
++ * Read job ticket info from the file...
++ */
++
++ num_options = 0;
++ options = NULL;
++
++ while (cupsFileGets(fp, line, sizeof(line)))
++ {
++ /*
++ * Stop at the first non-ticket line...
++ */
++
++ if (strncmp(line, "%cupsJobTicket:", 15))
++ break;
++
++ /*
++ * Add the options to the option array...
++ */
++
++ num_options = cupsParseOptions(line + 15, num_options, &options);
++ }
++
++ /*
++ * Done with the file; see if we have any options...
++ */
++
++ cupsFileClose(fp);
++
++ if (num_options == 0)
++ return;
++
++ /*
++ * OK, convert the options to an attribute list, and apply them to
++ * the request...
++ */
++
++ ticket = ippNew();
++ cupsEncodeOptions(ticket, num_options, options);
++
++ /*
++ * See what the user wants to change.
++ */
++
++ for (attr = ticket->attrs; attr; attr = attr->next)
++ {
++ if (attr->group_tag != IPP_TAG_JOB || !attr->name)
++ continue;
++
++ if (!strcmp(attr->name, "job-originating-host-name") ||
++ !strcmp(attr->name, "job-originating-user-name") ||
++ !strcmp(attr->name, "job-media-sheets-completed") ||
++ !strcmp(attr->name, "job-k-octets") ||
++ !strcmp(attr->name, "job-id") ||
++ !strncmp(attr->name, "job-state", 9) ||
++ !strncmp(attr->name, "time-at-", 8))
++ continue; /* Read-only attrs */
++
++ if ((attr2 = ippFindAttribute(con->request, attr->name,
++ IPP_TAG_ZERO)) != NULL)
++ {
++ /*
++ * Some other value; first free the old value...
++ */
++
++ if (con->request->attrs == attr2)
++ {
++ con->request->attrs = attr2->next;
++ prev2 = NULL;
++ }
++ else
++ {
++ for (prev2 = con->request->attrs; prev2; prev2 = prev2->next)
++ if (prev2->next == attr2)
++ {
++ prev2->next = attr2->next;
++ break;
++ }
++ }
++
++ if (con->request->last == attr2)
++ con->request->last = prev2;
++
++ _ippFreeAttr(attr2);
++ }
++
++ /*
++ * Add new option by copying it...
++ */
++
++ copy_attribute(con->request, attr, 0);
++ }
++
++ /*
++ * Then free the attribute list and option array...
++ */
++
++ ippDelete(ticket);
++ cupsFreeOptions(num_options, options);
++}
++
++
++/*
++ * 'reject_jobs()' - Reject print jobs to a printer.
++ */
++
++static void
++reject_jobs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer or class URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ const char *name; /* Printer name */
++ cupsd_printer_t *printer; /* Printer data */
++ ipp_attribute_t *attr; /* printer-state-message text */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Reject jobs sent to the printer...
++ */
++
++ printer->accepting = 0;
++
++ if ((attr = ippFindAttribute(con->request, "printer-state-message",
++ IPP_TAG_TEXT)) == NULL)
++ strcpy(printer->state_message, "Rejecting Jobs");
++ else
++ strlcpy(printer->state_message, attr->values[0].string.text,
++ sizeof(printer->state_message));
++
++ cupsdAddPrinterHistory(printer);
++
++ if (dtype & CUPS_PRINTER_CLASS)
++ {
++ cupsdSaveAllClasses();
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
++ name, get_username(con));
++ }
++ else
++ {
++ cupsdSaveAllPrinters();
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
++ name, get_username(con));
++ }
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'release_job()' - Release a held print job.
++ */
++
++static void
++release_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job or Printer URI */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ int jobid; /* Job ID */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_job_t *job; /* Job information */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if job is "held"...
++ */
++
++ if (job->state_value != IPP_JOB_HELD)
++ {
++ /*
++ * Nope - return a "not possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * Reset the job-hold-until value to "no-hold"...
++ */
++
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++
++ if (attr)
++ {
++ _cupsStrFree(attr->values[0].string.text);
++
++ attr->value_tag = IPP_TAG_KEYWORD;
++ attr->values[0].string.text = _cupsStrAlloc("no-hold");
++
++ cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
++ "Job job-hold-until value changed by user.");
++ }
++
++ /*
++ * Release the job and return...
++ */
++
++ cupsdReleaseJob(job);
++
++ cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
++ "Job released by user.");
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid,
++ username);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'renew_subscription()' - Renew an existing subscription...
++ */
++
++static void
++renew_subscription(
++ cupsd_client_t *con, /* I - Client connection */
++ int sub_id) /* I - Subscription ID */
++{
++ http_status_t status; /* Policy status */
++ cupsd_subscription_t *sub; /* Subscription */
++ ipp_attribute_t *lease; /* notify-lease-duration */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "renew_subscription(con=%p[%d], sub_id=%d)",
++ con, con->http.fd, sub_id);
++
++ /*
++ * Is the subscription ID valid?
++ */
++
++ if ((sub = cupsdFindSubscription(sub_id)) == NULL)
++ {
++ /*
++ * Bad subscription ID...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("notify-subscription-id %d no good!"), sub_id);
++ return;
++ }
++
++ if (sub->job)
++ {
++ /*
++ * Job subscriptions cannot be renewed...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job subscriptions cannot be renewed!"));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
++ DefaultPolicyPtr,
++ con, sub->owner)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Renew the subscription...
++ */
++
++ lease = ippFindAttribute(con->request, "notify-lease-duration",
++ IPP_TAG_INTEGER);
++
++ sub->lease = lease ? lease->values[0].integer : DefaultLeaseDuration;
++
++ if (MaxLeaseDuration && (sub->lease == 0 || sub->lease > MaxLeaseDuration))
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "renew_subscription: Limiting notify-lease-duration to "
++ "%d seconds.",
++ MaxLeaseDuration);
++ sub->lease = MaxLeaseDuration;
++ }
++
++ sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
++
++ cupsdSaveAllSubscriptions();
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'restart_job()' - Restart an old print job.
++ */
++
++static void
++restart_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job or Printer URI */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ int jobid; /* Job ID */
++ char method[HTTP_MAX_URI], /* Method portion of URI */
++ username[HTTP_MAX_URI], /* Username portion of URI */
++ host[HTTP_MAX_URI], /* Host portion of URI */
++ resource[HTTP_MAX_URI]; /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ cupsd_job_t *job; /* Job information */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if job is in any of the "completed" states...
++ */
++
++ if (job->state_value <= IPP_JOB_PROCESSING)
++ {
++ /*
++ * Nope - return a "not possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete!"),
++ jobid);
++ return;
++ }
++
++ /*
++ * See if we have retained the job files...
++ */
++
++ cupsdLoadJob(job);
++
++ if (!job->attrs || job->num_files == 0)
++ {
++ /*
++ * Nope - return a "not possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d cannot be restarted - no files!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * Restart the job and return...
++ */
++
++ cupsdRestartJob(job);
++
++ cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was restarted by \"%s\".", jobid,
++ username);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'save_auth_info()' - Save authentication information for a job.
++ */
++
++static void
++save_auth_info(cupsd_client_t *con, /* I - Client connection */
++ cupsd_job_t *job) /* I - Job */
++{
++ int i; /* Looping var */
++ char filename[1024]; /* Job authentication filename */
++ cups_file_t *fp; /* Job authentication file */
++ char line[1024]; /* Line for file */
++
++
++ /*
++ * This function saves the in-memory authentication information for
++ * a job so that it can be used to authenticate with a remote host.
++ * The information is stored in a file that is readable only by the
++ * root user. The username and password are Base-64 encoded, each
++ * on a separate line, followed by random number (up to 1024) of
++ * newlines to limit the amount of information that is exposed.
++ *
++ * Because of the potential for exposing of authentication information,
++ * this functionality is only enabled when running cupsd as root.
++ *
++ * This caching only works for the Basic and BasicDigest authentication
++ * types. Digest authentication cannot be cached this way, and in
++ * the future Kerberos authentication may make all of this obsolete.
++ *
++ * Authentication information is saved whenever an authenticated
++ * Print-Job, Create-Job, or CUPS-Authenticate-Job operation is
++ * performed.
++ *
++ * This information is deleted after a job is completed or canceled,
++ * so reprints may require subsequent re-authentication.
++ */
++
++ if (RunUser)
++ return;
++
++ /*
++ * Create the authentication file and change permissions...
++ */
++
++ snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
++ if ((fp = cupsFileOpen(filename, "w")) == NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_ERROR,
++ "Unable to save authentication info to \"%s\" - %s",
++ filename, strerror(errno));
++ return;
++ }
++
++ fchown(cupsFileNumber(fp), 0, 0);
++ fchmod(cupsFileNumber(fp), 0400);
++
++ /*
++ * Write the authenticated username...
++ */
++
++ httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
++ cupsFilePrintf(fp, "%s\n", line);
++
++ /*
++ * Write the authenticated password...
++ */
++
++ httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
++ cupsFilePrintf(fp, "%s\n", line);
++
++ /*
++ * Write a random number of newlines to the end of the file...
++ */
++
++ for (i = (rand() % 1024); i >= 0; i --)
++ cupsFilePutChar(fp, '\n');
++
++ /*
++ * Close the file and return...
++ */
++
++ cupsFileClose(fp);
++}
++
++
++/*
++ * 'send_document()' - Send a file to a printer or class.
++ */
++
++static void
++send_document(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ ipp_attribute_t *attr; /* Current attribute */
++ ipp_attribute_t *format; /* Document-format attribute */
++ int jobid; /* Job ID number */
++ cupsd_job_t *job; /* Current job */
++ char job_uri[HTTP_MAX_URI],
++ /* Job URI */
++ method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ mime_type_t *filetype; /* Type of file */
++ char super[MIME_MAX_SUPER],
++ /* Supertype of file */
++ type[MIME_MAX_TYPE],
++ /* Subtype of file */
++ mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
++ /* Textual name of mime type */
++ char filename[1024]; /* Job filename */
++ cupsd_printer_t *printer; /* Current printer */
++ struct stat fileinfo; /* File information */
++ int kbytes; /* Size of file */
++ int compression; /* Type of compression */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ printer = cupsdFindDest(job->dest);
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * OK, see if the client is sending the document compressed - CUPS
++ * only supports "none" and "gzip".
++ */
++
++ compression = CUPS_FILE_NONE;
++
++ if ((attr = ippFindAttribute(con->request, "compression",
++ IPP_TAG_KEYWORD)) != NULL)
++ {
++ if (strcmp(attr->values[0].string.text, "none")
++#ifdef HAVE_LIBZ
++ && strcmp(attr->values[0].string.text, "gzip")
++#endif /* HAVE_LIBZ */
++ )
++ {
++ send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"!"),
++ attr->values[0].string.text);
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
++ "compression", NULL, attr->values[0].string.text);
++ return;
++ }
++
++#ifdef HAVE_LIBZ
++ if (!strcmp(attr->values[0].string.text, "gzip"))
++ compression = CUPS_FILE_GZIP;
++#endif /* HAVE_LIBZ */
++ }
++
++ /*
++ * Do we have a file to print?
++ */
++
++ if (!con->filename)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?!"));
++ return;
++ }
++
++ /*
++ * Is it a format we support?
++ */
++
++ if ((format = ippFindAttribute(con->request, "document-format",
++ IPP_TAG_MIMETYPE)) != NULL)
++ {
++ /*
++ * Grab format from client...
++ */
++
++ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
++ format->values[0].string.text);
++ return;
++ }
++ }
++ else
++ {
++ /*
++ * No document format attribute? Auto-type it!
++ */
++
++ strcpy(super, "application");
++ strcpy(type, "octet-stream");
++ }
++
++ if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
++ {
++ /*
++ * Auto-type the file...
++ */
++
++ ipp_attribute_t *doc_name; /* document-name attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "send_document: auto-typing file...");
++
++ doc_name = ippFindAttribute(con->request, "document-name", IPP_TAG_NAME);
++ filetype = mimeFileType(MimeDatabase, con->filename,
++ doc_name ? doc_name->values[0].string.text : NULL,
++ &compression);
++
++ if (filetype)
++ {
++ /*
++ * Replace the document-format attribute value with the auto-typed one.
++ */
++
++ snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
++ filetype->type);
++
++ if (format)
++ {
++ _cupsStrFree(format->values[0].string.text);
++ format->values[0].string.text = _cupsStrAlloc(mimetype);
++ }
++ else
++ ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
++ "document-format", NULL, mimetype);
++ }
++ else
++ filetype = mimeType(MimeDatabase, super, type);
++ }
++ else
++ filetype = mimeType(MimeDatabase, super, type);
++
++ if (!filetype)
++ {
++ send_ipp_status(con, IPP_DOCUMENT_FORMAT,
++ _("Unsupported format \'%s/%s\'!"), super, type);
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Hint: Do you have the raw file printing rules enabled?");
++
++ if (format)
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
++ "document-format", NULL, format->values[0].string.text);
++
++ return;
++ }
++
++ if (printer->filetypes && !cupsArrayFind(printer->filetypes, filetype))
++ {
++ snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
++ filetype->type);
++
++ send_ipp_status(con, IPP_DOCUMENT_FORMAT,
++ _("Unsupported format \'%s\'!"), mimetype);
++
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
++ "document-format", NULL, mimetype);
++
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "send_document: request file type is %s/%s.",
++ filetype->super, filetype->type);
++
++ /*
++ * Add the file to the job...
++ */
++
++ cupsdLoadJob(job);
++
++ if (add_file(con, job, filetype, compression))
++ return;
++
++ if (stat(con->filename, &fileinfo))
++ kbytes = 0;
++ else
++ kbytes = (fileinfo.st_size + 1023) / 1024;
++
++ cupsdUpdateQuota(printer, job->username, 0, kbytes);
++
++ if ((attr = ippFindAttribute(job->attrs, "job-k-octets",
++ IPP_TAG_INTEGER)) != NULL)
++ attr->values[0].integer += kbytes;
++
++ snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
++ job->num_files);
++ rename(con->filename, filename);
++
++ cupsdClearString(&con->filename);
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "File of type %s/%s queued in job #%d by \"%s\".",
++ filetype->super, filetype->type, job->id, job->username);
++
++ /*
++ * Start the job if this is the last document...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "last-document",
++ IPP_TAG_BOOLEAN)) != NULL &&
++ attr->values[0].boolean)
++ {
++ /*
++ * See if we need to add the ending sheet...
++ */
++
++ if (printer &&
++ !(printer->type & (CUPS_PRINTER_REMOTE | CUPS_PRINTER_IMPLICIT)) &&
++ (attr = ippFindAttribute(job->attrs, "job-sheets",
++ IPP_TAG_ZERO)) != NULL &&
++ attr->num_values > 1)
++ {
++ /*
++ * Yes...
++ */
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Adding end banner page \"%s\" to job %d.",
++ attr->values[1].string.text, job->id);
++
++ kbytes = copy_banner(con, job, attr->values[1].string.text);
++
++ cupsdUpdateQuota(printer, job->username, 0, kbytes);
++ }
++
++ if (job->state_value == IPP_JOB_STOPPED)
++ {
++ job->state->values[0].integer = IPP_JOB_PENDING;
++ job->state_value = IPP_JOB_PENDING;
++ }
++ else if (job->state_value == IPP_JOB_HELD)
++ {
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++
++ if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
++ {
++ job->state->values[0].integer = IPP_JOB_PENDING;
++ job->state_value = IPP_JOB_PENDING;
++ }
++ }
++
++ cupsdSaveJob(job);
++
++ /*
++ * Start the job if possible... Since cupsdCheckJobs() can cancel a
++ * job if it doesn't print, we need to re-find the job afterwards...
++ */
++
++ jobid = job->id;
++
++ cupsdCheckJobs();
++
++ job = cupsdFindJob(jobid);
++ }
++ else
++ {
++ if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
++ IPP_TAG_KEYWORD)) == NULL)
++ attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
++
++ if (!attr || !strcmp(attr->values[0].string.text, "no-hold"))
++ {
++ job->state->values[0].integer = IPP_JOB_HELD;
++ job->state_value = IPP_JOB_HELD;
++ job->hold_until = time(NULL) + 60;
++ cupsdSaveJob(job);
++ }
++ }
++
++ /*
++ * Fill in the response info...
++ */
++
++ snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
++ LocalPort, jobid);
++
++ ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
++ job_uri);
++
++ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
++
++ ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
++ job ? job->state_value : IPP_JOB_CANCELED);
++ add_job_state_reasons(con, job);
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'send_http_error()' - Send a HTTP error back to the IPP client.
++ */
++
++static void
++send_http_error(cupsd_client_t *con, /* I - Client connection */
++ http_status_t status) /* I - HTTP status code */
++{
++ cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s",
++ ippOpString(con->request->request.op.operation_id),
++ httpStatus(status));
++
++ cupsdSendError(con, status);
++
++ ippDelete(con->response);
++ con->response = NULL;
++
++ return;
++}
++
++
++/*
++ * 'send_ipp_status()' - Send a status back to the IPP client.
++ */
++
++static void
++send_ipp_status(cupsd_client_t *con, /* I - Client connection */
++ ipp_status_t status, /* I - IPP status code */
++ const char *message, /* I - Status message */
++ ...) /* I - Additional args as needed */
++{
++ va_list ap; /* Pointer to additional args */
++ char formatted[1024]; /* Formatted errror message */
++
++
++ if (message)
++ {
++ va_start(ap, message);
++ vsnprintf(formatted, sizeof(formatted),
++ _cupsLangString(con->language, message), ap);
++ va_end(ap);
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
++ ippOpString(con->request->request.op.operation_id),
++ ippErrorString(status), formatted);
++ }
++ else
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
++ ippOpString(con->request->request.op.operation_id),
++ ippErrorString(status));
++
++ con->response->request.status.status_code = status;
++
++ if (ippFindAttribute(con->response, "attributes-charset",
++ IPP_TAG_ZERO) == NULL)
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
++ "attributes-charset", NULL, DefaultCharset);
++
++ if (ippFindAttribute(con->response, "attributes-natural-language",
++ IPP_TAG_ZERO) == NULL)
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
++ "attributes-natural-language", NULL, DefaultLanguage);
++
++ if (message)
++ ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_TEXT,
++ "status-message", NULL, formatted);
++}
++
++
++/*
++ * 'set_default()' - Set the default destination...
++ */
++
++static void
++set_default(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ const char *name; /* Printer name */
++ cupsd_printer_t *printer; /* Printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Set it as the default...
++ */
++
++ DefaultPrinter = printer;
++
++ cupsdSaveAllPrinters();
++ cupsdSaveAllClasses();
++
++ cupsdWritePrintcap();
++
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Default destination set to \"%s\" by \"%s\".", name,
++ get_username(con));
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'set_job_attrs()' - Set job attributes.
++ */
++
++static void
++set_job_attrs(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Job URI */
++{
++ ipp_attribute_t *attr, /* Current attribute */
++ *attr2; /* Job attribute */
++ int jobid; /* Job ID */
++ cupsd_job_t *job; /* Current job */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ int event; /* Events? */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Start with "everything is OK" status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++
++ /*
++ * See if we have a job URI or a printer URI...
++ */
++
++ if (!strcmp(uri->name, "printer-uri"))
++ {
++ /*
++ * Got a printer URI; see if we also have a job-id attribute...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "job-id",
++ IPP_TAG_INTEGER)) == NULL)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Got a printer-uri attribute but no job-id!"));
++ return;
++ }
++
++ jobid = attr->values[0].integer;
++ }
++ else
++ {
++ /*
++ * Got a job URI; parse it to get the job ID...
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (strncmp(resource, "/jobs/", 6))
++ {
++ /*
++ * Not a valid URI!
++ */
++
++ send_ipp_status(con, IPP_BAD_REQUEST,
++ _("Bad job-uri attribute \"%s\"!"),
++ uri->values[0].string.text);
++ return;
++ }
++
++ jobid = atoi(resource + 6);
++ }
++
++ /*
++ * See if the job exists...
++ */
++
++ if ((job = cupsdFindJob(jobid)) == NULL)
++ {
++ /*
++ * Nope - return a "not found" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job has been completed...
++ */
++
++ if (job->state_value > IPP_JOB_STOPPED)
++ {
++ /*
++ * Return a "not-possible" error...
++ */
++
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job #%d is finished and cannot be altered!"), jobid);
++ return;
++ }
++
++ /*
++ * See if the job is owned by the requesting user...
++ */
++
++ if (!validate_user(job, con, job->username, username, sizeof(username)))
++ {
++ send_http_error(con, HTTP_UNAUTHORIZED);
++ return;
++ }
++
++ /*
++ * See what the user wants to change.
++ */
++
++ cupsdLoadJob(job);
++
++ event = 0;
++
++ for (attr = con->request->attrs; attr; attr = attr->next)
++ {
++ if (attr->group_tag != IPP_TAG_JOB || !attr->name)
++ continue;
++
++ if (!strcmp(attr->name, "attributes-charset") ||
++ !strcmp(attr->name, "attributes-natural-language") ||
++ !strcmp(attr->name, "document-compression") ||
++ !strcmp(attr->name, "document-format") ||
++ !strcmp(attr->name, "job-detailed-status-messages") ||
++ !strcmp(attr->name, "job-document-access-errors") ||
++ !strcmp(attr->name, "job-id") ||
++ !strcmp(attr->name, "job-k-octets") ||
++ !strcmp(attr->name, "job-originating-host-name") ||
++ !strcmp(attr->name, "job-originating-user-name") ||
++ !strcmp(attr->name, "job-printer-up-time") ||
++ !strcmp(attr->name, "job-printer-uri") ||
++ !strcmp(attr->name, "job-sheets") ||
++ !strcmp(attr->name, "job-state-message") ||
++ !strcmp(attr->name, "job-state-reasons") ||
++ !strcmp(attr->name, "job-uri") ||
++ !strcmp(attr->name, "number-of-documents") ||
++ !strcmp(attr->name, "number-of-intervening-jobs") ||
++ !strcmp(attr->name, "output-device-assigned") ||
++ !strncmp(attr->name, "date-time-at-", 13) ||
++ !strncmp(attr->name, "job-impressions", 15) ||
++ !strncmp(attr->name, "job-k-octets", 12) ||
++ !strncmp(attr->name, "job-media-sheets", 16) ||
++ !strncmp(attr->name, "time-at-", 8))
++ {
++ /*
++ * Read-only attrs!
++ */
++
++ send_ipp_status(con, IPP_ATTRIBUTES_NOT_SETTABLE,
++ _("%s cannot be changed."), attr->name);
++
++ if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
++ attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
++
++ continue;
++ }
++
++ if (!strcmp(attr->name, "job-priority"))
++ {
++ /*
++ * Change the job priority...
++ */
++
++ if (attr->value_tag != IPP_TAG_INTEGER)
++ {
++ send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value!"));
++
++ if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
++ attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
++ }
++ else if (job->state_value >= IPP_JOB_PROCESSING)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job is completed and cannot be changed."));
++ return;
++ }
++ else if (con->response->request.status.status_code == IPP_OK)
++ {
++ cupsdSetJobPriority(job, attr->values[0].integer);
++ event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
++ }
++ }
++ else if (!strcmp(attr->name, "job-state"))
++ {
++ /*
++ * Change the job state...
++ */
++
++ if (attr->value_tag != IPP_TAG_ENUM)
++ {
++ send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value!"));
++
++ if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
++ attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
++ }
++ else
++ {
++ switch (attr->values[0].integer)
++ {
++ case IPP_JOB_PENDING :
++ case IPP_JOB_HELD :
++ if (job->state_value > IPP_JOB_HELD)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job state cannot be changed."));
++ return;
++ }
++ else if (con->response->request.status.status_code == IPP_OK)
++ {
++ job->state->values[0].integer = attr->values[0].integer;
++ job->state_value = (ipp_jstate_t)attr->values[0].integer;
++
++ event |= CUPSD_EVENT_JOB_STATE;
++ }
++ break;
++
++ case IPP_JOB_PROCESSING :
++ case IPP_JOB_STOPPED :
++ if (job->state_value != attr->values[0].integer)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job state cannot be changed."));
++ return;
++ }
++ break;
++
++ case IPP_JOB_CANCELED :
++ case IPP_JOB_ABORTED :
++ case IPP_JOB_COMPLETED :
++ if (job->state_value > IPP_JOB_PROCESSING)
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Job state cannot be changed."));
++ return;
++ }
++ else if (con->response->request.status.status_code == IPP_OK)
++ cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
++ break;
++ }
++ }
++ }
++ else if (con->response->request.status.status_code != IPP_OK)
++ continue;
++ else if ((attr2 = ippFindAttribute(job->attrs, attr->name,
++ IPP_TAG_ZERO)) != NULL)
++ {
++ /*
++ * Some other value; first free the old value...
++ */
++
++ if (job->attrs->prev)
++ job->attrs->prev->next = attr2->next;
++ else
++ job->attrs->attrs = attr2->next;
++
++ if (job->attrs->last == attr2)
++ job->attrs->last = job->attrs->prev;
++
++ _ippFreeAttr(attr2);
++
++ /*
++ * Then copy the attribute...
++ */
++
++ copy_attribute(job->attrs, attr, 0);
++
++ /*
++ * See if the job-name or job-hold-until is being changed.
++ */
++
++ if (!strcmp(attr->name, "job-hold-until"))
++ {
++ cupsdSetJobHoldUntil(job, attr->values[0].string.text);
++
++ if (!strcmp(attr->values[0].string.text, "no-hold"))
++ cupsdReleaseJob(job);
++ else
++ cupsdHoldJob(job);
++
++ event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
++ }
++ }
++ else if (attr->value_tag == IPP_TAG_DELETEATTR)
++ {
++ /*
++ * Delete the attribute...
++ */
++
++ if ((attr2 = ippFindAttribute(job->attrs, attr->name,
++ IPP_TAG_ZERO)) != NULL)
++ {
++ if (job->attrs->prev)
++ job->attrs->prev->next = attr2->next;
++ else
++ job->attrs->attrs = attr2->next;
++
++ if (attr2 == job->attrs->last)
++ job->attrs->last = job->attrs->prev;
++
++ _ippFreeAttr(attr2);
++
++ event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
++ }
++ }
++ else
++ {
++ /*
++ * Add new option by copying it...
++ */
++
++ copy_attribute(job->attrs, attr, 0);
++
++ event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
++ }
++ }
++
++ /*
++ * Save the job...
++ */
++
++ cupsdSaveJob(job);
++
++ /*
++ * Send events as needed...
++ */
++
++ if (event & CUPSD_EVENT_JOB_STATE)
++ cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
++ job->state_value == IPP_JOB_HELD ?
++ "Job held by user." : "Job restarted by user.");
++
++ if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
++ cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
++ "Job options changed by user.");
++
++ /*
++ * Start jobs if possible...
++ */
++
++ cupsdCheckJobs();
++}
++
++
++/*
++ * 'set_printer_defaults()' - Set printer default options from a request.
++ */
++
++static void
++set_printer_defaults(
++ cupsd_client_t *con, /* I - Client connection */
++ cupsd_printer_t *printer) /* I - Printer */
++{
++ int i; /* Looping var */
++ ipp_attribute_t *attr; /* Current attribute */
++ int namelen; /* Length of attribute name */
++ char name[256], /* New attribute name */
++ value[256]; /* String version of integer attrs */
++
++
++ for (attr = con->request->attrs; attr; attr = attr->next)
++ {
++ /*
++ * Skip non-printer attributes...
++ */
++
++ if (attr->group_tag != IPP_TAG_PRINTER || !attr->name)
++ continue;
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_defaults: %s", attr->name);
++
++ if (!strcmp(attr->name, "job-sheets-default"))
++ {
++ /*
++ * Only allow keywords and names...
++ */
++
++ if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
++ continue;
++
++ /*
++ * Only allow job-sheets-default to be set when running without a
++ * system high classification level...
++ */
++
++ if (Classification)
++ continue;
++
++ cupsdSetString(&printer->job_sheets[0], attr->values[0].string.text);
++
++ if (attr->num_values > 1)
++ cupsdSetString(&printer->job_sheets[1], attr->values[1].string.text);
++ else
++ cupsdSetString(&printer->job_sheets[1], "none");
++ }
++ else if (!strcmp(attr->name, "requesting-user-name-allowed"))
++ {
++ cupsdFreePrinterUsers(printer);
++
++ printer->deny_users = 0;
++
++ if (attr->value_tag == IPP_TAG_NAME &&
++ (attr->num_values > 1 ||
++ strcmp(attr->values[0].string.text, "all")))
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ cupsdAddPrinterUser(printer, attr->values[i].string.text);
++ }
++ }
++ else if (!strcmp(attr->name, "requesting-user-name-denied"))
++ {
++ cupsdFreePrinterUsers(printer);
++
++ printer->deny_users = 1;
++
++ if (attr->value_tag == IPP_TAG_NAME &&
++ (attr->num_values > 1 ||
++ strcmp(attr->values[0].string.text, "none")))
++ {
++ for (i = 0; i < attr->num_values; i ++)
++ cupsdAddPrinterUser(printer, attr->values[i].string.text);
++ }
++ }
++ else if (!strcmp(attr->name, "job-quota-period"))
++ {
++ if (attr->value_tag != IPP_TAG_INTEGER)
++ continue;
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-quota-period to %d...",
++ attr->values[0].integer);
++ cupsdFreeQuotas(printer);
++
++ printer->quota_period = attr->values[0].integer;
++ }
++ else if (!strcmp(attr->name, "job-k-limit"))
++ {
++ if (attr->value_tag != IPP_TAG_INTEGER)
++ continue;
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-k-limit to %d...",
++ attr->values[0].integer);
++ cupsdFreeQuotas(printer);
++
++ printer->k_limit = attr->values[0].integer;
++ }
++ else if (!strcmp(attr->name, "job-page-limit"))
++ {
++ if (attr->value_tag != IPP_TAG_INTEGER)
++ continue;
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG, "Setting job-page-limit to %d...",
++ attr->values[0].integer);
++ cupsdFreeQuotas(printer);
++
++ printer->page_limit = attr->values[0].integer;
++ }
++ else if (!strcmp(attr->name, "printer-op-policy"))
++ {
++ cupsd_policy_t *p; /* Policy */
++
++
++ if (attr->value_tag != IPP_TAG_NAME)
++ continue;
++
++ if ((p = cupsdFindPolicy(attr->values[0].string.text)) != NULL)
++ {
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting printer-op-policy to \"%s\"...",
++ attr->values[0].string.text);
++ cupsdSetString(&printer->op_policy, attr->values[0].string.text);
++ printer->op_policy_ptr = p;
++ }
++ else
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Unknown printer-op-policy \"%s\"."),
++ attr->values[0].string.text);
++ return;
++ }
++ }
++ else if (!strcmp(attr->name, "printer-error-policy"))
++ {
++ if (attr->value_tag != IPP_TAG_NAME && attr->value_tag != IPP_TAG_KEYWORD)
++ continue;
++
++ if (strcmp(attr->values[0].string.text, "abort-job") &&
++ strcmp(attr->values[0].string.text, "retry-job") &&
++ strcmp(attr->values[0].string.text, "stop-printer"))
++ {
++ send_ipp_status(con, IPP_NOT_POSSIBLE,
++ _("Unknown printer-error-policy \"%s\"."),
++ attr->values[0].string.text);
++ return;
++ }
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting printer-error-policy to \"%s\"...",
++ attr->values[0].string.text);
++ cupsdSetString(&printer->error_policy, attr->values[0].string.text);
++ }
++ else if (!strcmp(attr->name, "document-format-default") ||
++ !strcmp(attr->name, "notify-lease-duration-default") ||
++ !strcmp(attr->name, "notify-events-default"))
++ continue;
++
++ /*
++ * Skip any other non-default attributes...
++ */
++
++ namelen = strlen(attr->name);
++ if (namelen < 9 || strcmp(attr->name + namelen - 8, "-default") ||
++ namelen > (sizeof(name) - 1) || attr->num_values != 1)
++ continue;
++
++ /*
++ * OK, anything else must be a user-defined default...
++ */
++
++ strlcpy(name, attr->name, sizeof(name));
++ name[namelen - 8] = '\0'; /* Strip "-default" */
++
++ switch (attr->value_tag)
++ {
++ case IPP_TAG_DELETEATTR :
++ printer->num_options = cupsRemoveOption(name,
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Deleting %s", attr->name);
++ break;
++
++ case IPP_TAG_NAME :
++ case IPP_TAG_KEYWORD :
++ case IPP_TAG_URI :
++ printer->num_options = cupsAddOption(name,
++ attr->values[0].string.text,
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting %s to \"%s\"...", attr->name,
++ attr->values[0].string.text);
++ break;
++
++ case IPP_TAG_BOOLEAN :
++ printer->num_options = cupsAddOption(name,
++ attr->values[0].boolean ?
++ "true" : "false",
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting %s to %s...", attr->name,
++ attr->values[0].boolean ? "true" : "false");
++ break;
++
++ case IPP_TAG_INTEGER :
++ case IPP_TAG_ENUM :
++ sprintf(value, "%d", attr->values[0].integer);
++ printer->num_options = cupsAddOption(name, value,
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting %s to %s...", attr->name, value);
++ break;
++
++ case IPP_TAG_RANGE :
++ sprintf(value, "%d-%d", attr->values[0].range.lower,
++ attr->values[0].range.upper);
++ printer->num_options = cupsAddOption(name, value,
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting %s to %s...", attr->name, value);
++ break;
++
++ case IPP_TAG_RESOLUTION :
++ sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
++ attr->values[0].resolution.yres,
++ attr->values[0].resolution.units == IPP_RES_PER_INCH ?
++ "dpi" : "dpc");
++ printer->num_options = cupsAddOption(name, value,
++ printer->num_options,
++ &(printer->options));
++ cupsdLogMessage(CUPSD_LOG_DEBUG,
++ "Setting %s to %s...", attr->name, value);
++ break;
++
++ default :
++ /* Do nothing for other values */
++ break;
++ }
++ }
++}
++
++
++/*
++ * 'start_printer()' - Start a printer.
++ */
++
++static void
++start_printer(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ const char *name; /* Printer name */
++ cupsd_printer_t *printer; /* Printer data */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Start the printer...
++ */
++
++ printer->state_message[0] = '\0';
++
++ cupsdStartPrinter(printer, 1);
++
++ if (dtype & CUPS_PRINTER_CLASS)
++ cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name,
++ get_username(con));
++ else
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name,
++ get_username(con));
++
++ cupsdCheckJobs();
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'stop_printer()' - Stop a printer.
++ */
++
++static void
++stop_printer(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ const char *name; /* Printer name */
++ cupsd_printer_t *printer; /* Printer data */
++ ipp_attribute_t *attr; /* printer-state-message attribute */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Stop the printer...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "printer-state-message",
++ IPP_TAG_TEXT)) == NULL)
++ strcpy(printer->state_message, "Paused");
++ else
++ {
++ strlcpy(printer->state_message, attr->values[0].string.text,
++ sizeof(printer->state_message));
++ }
++
++ cupsdStopPrinter(printer, 1);
++
++ if (dtype & CUPS_PRINTER_CLASS)
++ cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name,
++ get_username(con));
++ else
++ cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name,
++ get_username(con));
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'url_encode_attr()' - URL-encode a string attribute.
++ */
++
++static void
++url_encode_attr(ipp_attribute_t *attr, /* I - Attribute */
++ char *buffer,/* I - String buffer */
++ int bufsize)/* I - Size of buffer */
++{
++ int i; /* Looping var */
++ char *bufptr, /* Pointer into buffer */
++ *bufend, /* End of buffer */
++ *valptr; /* Pointer into value */
++
++
++ strlcpy(buffer, attr->name, bufsize);
++ bufptr = buffer + strlen(buffer);
++ bufend = buffer + bufsize - 1;
++
++ for (i = 0; i < attr->num_values; i ++)
++ {
++ if (bufptr >= bufend)
++ break;
++
++ if (i)
++ *bufptr++ = ',';
++ else
++ *bufptr++ = '=';
++
++ if (bufptr >= bufend)
++ break;
++
++ *bufptr++ = '\'';
++
++ for (valptr = attr->values[i].string.text;
++ *valptr && bufptr < bufend;
++ valptr ++)
++ if (*valptr == ' ')
++ {
++ if (bufptr >= (bufend - 2))
++ break;
++
++ *bufptr++ = '%';
++ *bufptr++ = '2';
++ *bufptr++ = '0';
++ }
++ else if (*valptr == '\'' || *valptr == '\\')
++ {
++ *bufptr++ = '\\';
++ *bufptr++ = *valptr;
++ }
++ else
++ *bufptr++ = *valptr;
++
++ if (bufptr >= bufend)
++ break;
++
++ *bufptr++ = '\'';
++ }
++
++ *bufptr = '\0';
++}
++
++
++/*
++ * 'user_allowed()' - See if a user is allowed to print to a queue.
++ */
++
++static int /* O - 0 if not allowed, 1 if allowed */
++user_allowed(cupsd_printer_t *p, /* I - Printer or class */
++ const char *username) /* I - Username */
++{
++ int i; /* Looping var */
++ struct passwd *pw; /* User password data */
++
++
++ if (p->num_users == 0)
++ return (1);
++
++ if (!strcmp(username, "root"))
++ return (1);
++
++ pw = getpwnam(username);
++ endpwent();
++
++ for (i = 0; i < p->num_users; i ++)
++ {
++ if (p->users[i][0] == '@')
++ {
++ /*
++ * Check group membership...
++ */
++
++ if (cupsdCheckGroup(username, pw, p->users[i] + 1))
++ break;
++ }
++ else if (!strcasecmp(username, p->users[i]))
++ break;
++ }
++
++ return ((i < p->num_users) != p->deny_users);
++}
++
++
++/*
++ * 'validate_job()' - Validate printer options and destination.
++ */
++
++static void
++validate_job(cupsd_client_t *con, /* I - Client connection */
++ ipp_attribute_t *uri) /* I - Printer URI */
++{
++ http_status_t status; /* Policy status */
++ ipp_attribute_t *attr; /* Current attribute */
++ ipp_attribute_t *format; /* Document-format attribute */
++ cups_ptype_t dtype; /* Destination type (printer or class) */
++ char method[HTTP_MAX_URI],
++ /* Method portion of URI */
++ username[HTTP_MAX_URI],
++ /* Username portion of URI */
++ host[HTTP_MAX_URI],
++ /* Host portion of URI */
++ resource[HTTP_MAX_URI];
++ /* Resource portion of URI */
++ int port; /* Port portion of URI */
++ char super[MIME_MAX_SUPER],
++ /* Supertype of file */
++ type[MIME_MAX_TYPE];
++ /* Subtype of file */
++ cupsd_printer_t *printer; /* Printer */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
++ con->http.fd, uri->values[0].string.text);
++
++ /*
++ * OK, see if the client is sending the document compressed - CUPS
++ * doesn't support compression yet...
++ */
++
++ if ((attr = ippFindAttribute(con->request, "compression",
++ IPP_TAG_KEYWORD)) != NULL &&
++ !strcmp(attr->values[0].string.text, "none"))
++ {
++ send_ipp_status(con, IPP_ATTRIBUTES,
++ _("Unsupported compression attribute %s!"),
++ attr->values[0].string.text);
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
++ "compression", NULL, attr->values[0].string.text);
++ return;
++ }
++
++ /*
++ * Is it a format we support?
++ */
++
++ if ((format = ippFindAttribute(con->request, "document-format",
++ IPP_TAG_MIMETYPE)) != NULL)
++ {
++ if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
++ {
++ send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"!"),
++ format->values[0].string.text);
++ return;
++ }
++
++ if ((strcmp(super, "application") || strcmp(type, "octet-stream")) &&
++ !mimeType(MimeDatabase, super, type))
++ {
++ cupsdLogMessage(CUPSD_LOG_INFO,
++ "Hint: Do you have the raw file printing rules enabled?");
++ send_ipp_status(con, IPP_DOCUMENT_FORMAT,
++ _("Unsupported format \"%s\"!"),
++ format->values[0].string.text);
++ ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
++ "document-format", NULL, format->values[0].string.text);
++ return;
++ }
++ }
++
++ /*
++ * Is the destination valid?
++ */
++
++ httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
++ sizeof(method), username, sizeof(username), host,
++ sizeof(host), &port, resource, sizeof(resource));
++
++ if (cupsdValidateDest(host, resource, &dtype, &printer) == NULL)
++ {
++ /*
++ * Bad URI...
++ */
++
++ send_ipp_status(con, IPP_NOT_FOUND,
++ _("The printer or class was not found."));
++ return;
++ }
++
++ /*
++ * Check policy...
++ */
++
++ if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
++ {
++ send_http_error(con, status);
++ return;
++ }
++
++ /*
++ * Everything was ok, so return OK status...
++ */
++
++ con->response->request.status.status_code = IPP_OK;
++}
++
++
++/*
++ * 'validate_name()' - Make sure the printer name only contains valid chars.
++ */
++
++static int /* O - 0 if name is no good, 1 if name is good */
++validate_name(const char *name) /* I - Name to check */
++{
++ const char *ptr; /* Pointer into name */
++
++
++ /*
++ * Scan the whole name...
++ */
++
++ for (ptr = name; *ptr; ptr ++)
++ if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
++ return (0);
++
++ /*
++ * All the characters are good; validate the length, too...
++ */
++
++ return ((ptr - name) < 128);
++}
++
++
++/*
++ * 'validate_user()' - Validate the user for the request.
++ */
++
++static int /* O - 1 if permitted, 0 otherwise */
++validate_user(cupsd_job_t *job, /* I - Job */
++ cupsd_client_t *con, /* I - Client connection */
++ const char *owner, /* I - Owner of job/resource */
++ char *username, /* O - Authenticated username */
++ int userlen) /* I - Length of username */
++{
++ cupsd_printer_t *printer; /* Printer for job */
++
++
++ cupsdLogMessage(CUPSD_LOG_DEBUG2,
++ "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, "
++ "userlen=%d)",
++ job ? job->id : 0, con->http.fd, owner ? owner : "(null)",
++ username, userlen);
++
++ /*
++ * Validate input...
++ */
++
++ if (!con || !owner || !username || userlen <= 0)
++ return (0);
++
++ /*
++ * Get the best authenticated username that is available.
++ */
++
++ strlcpy(username, get_username(con), userlen);
++
++ /*
++ * Check the username against the owner...
++ */
++
++ printer = cupsdFindDest(job->dest);
++
++ return (cupsdCheckPolicy(printer ? printer->op_policy_ptr : DefaultPolicyPtr,
++ con, owner) == HTTP_OK);
++}
++
++
++/*
++ * End of "$Id: ipp.c 6145 2006-12-06 20:10:16Z mike $".
++ */
+diff -urNad cupsys-1.2.8~/scheduler/log.c cupsys-1.2.8/scheduler/log.c
+--- cupsys-1.2.8~/scheduler/log.c 2007-03-13 15:02:07.000000000 +0000
++++ cupsys-1.2.8/scheduler/log.c 2007-03-13 15:02:07.000000000 +0000
+@@ -420,9 +420,10 @@
+ check_log_file(cups_file_t **lf, /* IO - Log file */
+ const char *logname) /* I - Log filename */
+ {
+- char backname[1024], /* Backup log filename */
+- filename[1024], /* Formatted log filename */
+- *ptr; /* Pointer into filename */
++ char backname[1024], /* Backup log filename */
++ filename[1024], /* Formatted log filename */
++ *ptr; /* Pointer into filename */
++ const char *logptr; /* Pointer into log filename */
+
+
+ /*
+@@ -454,17 +455,17 @@
+ else
+ filename[0] = '\0';
+
+- for (ptr = filename + strlen(filename);
+- *logname && ptr < (filename + sizeof(filename) - 1);
+- logname ++)
+- if (*logname == '%')
++ for (logptr = logname, ptr = filename + strlen(filename);
++ *logptr && ptr < (filename + sizeof(filename) - 1);
++ logptr ++)
++ if (*logptr == '%')
+ {
+ /*
+ * Format spec...
+ */
+
+- logname ++;
+- if (*logname == 's')
++ logptr ++;
++ if (*logptr == 's')
+ {
+ /*
+ * Insert the server name...
+@@ -479,11 +480,11 @@
+ * Otherwise just insert the character...
+ */
+
+- *ptr++ = *logname;
++ *ptr++ = *logptr;
+ }
+ }
+ else
+- *ptr++ = *logname;
++ *ptr++ = *logptr;
+
+ *ptr = '\0';
+ }
+diff -urNad cupsys-1.2.8~/scheduler/log.c.orig cupsys-1.2.8/scheduler/log.c.orig
+--- cupsys-1.2.8~/scheduler/log.c.orig 1970-01-01 01:00:00.000000000 +0100
++++ cupsys-1.2.8/scheduler/log.c.orig 2007-03-13 15:02:07.000000000 +0000
+@@ -0,0 +1,561 @@
++/*
++ * "$Id: log.c 6027 2006-10-11 21:04:58Z mike $"
++ *
++ * Log file routines for the Common UNIX Printing System (CUPS).
++ *
++ * Copyright 1997-2006 by Easy Software Products, all rights reserved.
++ *
++ * These coded instructions, statements, and computer programs are the
++ * property of Easy Software Products and are protected by Federal
++ * copyright law. Distribution and use rights are outlined in the file
++ * "LICENSE.txt" which should have been included with this file. If this
++ * file is missing or damaged please contact Easy Software Products
++ * at:
++ *
++ * Attn: CUPS Licensing Information
++ * Easy Software Products
++ * 44141 Airport View Drive, Suite 204
++ * Hollywood, Maryland 20636 USA
++ *
++ * Voice: (301) 373-9600
++ * EMail: cups-info at cups.org
++ * WWW: http://www.cups.org
++ *
++ * Contents:
++ *
++ * cupsdGetDateTime() - Returns a pointer to a date/time string.
++ * cupsdLogMessage() - Log a message to the error log file.
++ * cupsdLogPage() - Log a page to the page log file.
++ * cupsdLogRequest() - Log an HTTP request in Common Log Format.
++ * check_log_file() - Open/rotate a log file if it needs it.
++ */
++
++/*
++ * Include necessary headers...
++ */
++
++#include "cupsd.h"
++#include <stdarg.h>
++#include <syslog.h>
++
++
++/*
++ * Local functions...
++ */
++
++static int check_log_file(cups_file_t **, const char *);
++
++
++/*
++ * 'cupsdGetDateTime()' - Returns a pointer to a date/time string.
++ */
++
++char * /* O - Date/time string */
++cupsdGetDateTime(time_t t) /* I - Time value */
++{
++ struct tm *date; /* Date/time value */
++ static time_t last_time = -1; /* Last time value */
++ static char s[1024]; /* Date/time string */
++ static const char * const months[12] =/* Months */
++ {
++ "Jan",
++ "Feb",
++ "Mar",
++ "Apr",
++ "May",
++ "Jun",
++ "Jul",
++ "Aug",
++ "Sep",
++ "Oct",
++ "Nov",
++ "Dec"
++ };
++
++
++ if (t != last_time)
++ {
++ last_time = t;
++
++ /*
++ * Get the date and time from the UNIX time value, and then format it
++ * into a string. Note that we *can't* use the strftime() function since
++ * it is localized and will seriously confuse automatic programs if the
++ * month names are in the wrong language!
++ *
++ * Also, we use the "timezone" variable that contains the current timezone
++ * offset from GMT in seconds so that we are reporting local time in the
++ * log files. If you want GMT, set the TZ environment variable accordingly
++ * before starting the scheduler.
++ *
++ * (*BSD and Darwin store the timezone offset in the tm structure)
++ */
++
++ date = localtime(&t);
++
++ snprintf(s, sizeof(s), "[%02d/%s/%04d:%02d:%02d:%02d %+03ld%02ld]",
++ date->tm_mday, months[date->tm_mon], 1900 + date->tm_year,
++ date->tm_hour, date->tm_min, date->tm_sec,
++#ifdef HAVE_TM_GMTOFF
++ date->tm_gmtoff / 3600, (date->tm_gmtoff / 60) % 60);
++#else
++ timezone / 3600, (timezone / 60) % 60);
++#endif /* HAVE_TM_GMTOFF */
++ }
++
++ return (s);
++}
++
++
++/*
++ * 'cupsdLogMessage()' - Log a message to the error log file.
++ */
++
++int /* O - 1 on success, 0 on error */
++cupsdLogMessage(int level, /* I - Log level */
++ const char *message, /* I - printf-style message string */
++ ...) /* I - Additional args as needed */
++{
++ int len; /* Length of message */
++ va_list ap; /* Argument pointer */
++ static const char levels[] = /* Log levels... */
++ {
++ ' ',
++ 'X',
++ 'A',
++ 'C',
++ 'E',
++ 'W',
++ 'N',
++ 'I',
++ 'D',
++ 'd'
++ };
++#ifdef HAVE_VSYSLOG
++ static const int syslevels[] = /* SYSLOG levels... */
++ {
++ 0,
++ LOG_EMERG,
++ LOG_ALERT,
++ LOG_CRIT,
++ LOG_ERR,
++ LOG_WARNING,
++ LOG_NOTICE,
++ LOG_INFO,
++ LOG_DEBUG,
++ LOG_DEBUG
++ };
++#endif /* HAVE_VSYSLOG */
++ static int linesize = 0; /* Size of line for output file */
++ static char *line = NULL; /* Line for output file */
++
++
++ /*
++ * See if we want to log this message...
++ */
++
++ if (level > LogLevel || !ErrorLog)
++ return (1);
++
++#ifdef HAVE_VSYSLOG
++ /*
++ * See if we are logging errors via syslog...
++ */
++
++ if (!strcmp(ErrorLog, "syslog"))
++ {
++ va_start(ap, message);
++ vsyslog(syslevels[level], message, ap);
++ va_end(ap);
++
++ return (1);
++ }
++#endif /* HAVE_VSYSLOG */
++
++ /*
++ * Not using syslog; check the log file...
++ */
++
++ if (!check_log_file(&ErrorFile, ErrorLog))
++ return (0);
++
++ /*
++ * Print the log level and date/time...
++ */
++
++ cupsFilePrintf(ErrorFile, "%c %s ", levels[level], cupsdGetDateTime(time(NULL)));
++
++ /*
++ * Allocate the line buffer as needed...
++ */
++
++ if (!linesize)
++ {
++ linesize = 8192;
++ line = malloc(linesize);
++
++ if (!line)
++ {
++ cupsFilePrintf(ErrorFile,
++ "ERROR: Unable to allocate memory for line - %s\n",
++ strerror(errno));
++ cupsFileFlush(ErrorFile);
++
++ return (0);
++ }
++ }
++
++ /*
++ * Format the log message...
++ */
++
++ va_start(ap, message);
++ len = vsnprintf(line, linesize, message, ap);
++ va_end(ap);
++
++ /*
++ * Resize the buffer as needed...
++ */
++
++ if (len >= linesize)
++ {
++ len ++;
++
++ if (len < 8192)
++ len = 8192;
++ else if (len > 65536)
++ len = 65536;
++
++ line = realloc(line, len);
++
++ if (line)
++ linesize = len;
++ else
++ {
++ cupsFilePrintf(ErrorFile,
++ "ERROR: Unable to allocate memory for line - %s\n",
++ strerror(errno));
++ cupsFileFlush(ErrorFile);
++
++ return (0);
++ }
++
++ va_start(ap, message);
++ len = vsnprintf(line, linesize, message, ap);
++ va_end(ap);
++ }
++
++ if (len >= linesize)
++ len = linesize - 1;
++
++ /*
++ * Then the log message...
++ */
++
++ cupsFilePuts(ErrorFile, line);
++
++ /*
++ * Then a newline...
++ */
++
++ if (len > 0 && line[len - 1] != '\n')
++ cupsFilePutChar(ErrorFile, '\n');
++
++ /*
++ * Flush the line to the file and return...
++ */
++
++ cupsFileFlush(ErrorFile);
++
++ return (1);
++}
++
++
++/*
++ * 'cupsdLogPage()' - Log a page to the page log file.
++ */
++
++int /* O - 1 on success, 0 on error */
++cupsdLogPage(cupsd_job_t *job, /* I - Job being printed */
++ const char *page) /* I - Page being printed */
++{
++ ipp_attribute_t *billing, /* job-billing attribute */
++ *hostname; /* job-originating-host-name attribute */
++
++
++ billing = ippFindAttribute(job->attrs, "job-billing", IPP_TAG_ZERO);
++ hostname = ippFindAttribute(job->attrs, "job-originating-host-name",
++ IPP_TAG_ZERO);
++
++#ifdef HAVE_VSYSLOG
++ /*
++ * See if we are logging pages via syslog...
++ */
++
++ if (!strcmp(PageLog, "syslog"))
++ {
++ syslog(LOG_INFO, "PAGE %s %s %d %s %s %s", job->printer->name,
++ job->username ? job->username : "-",
++ job->id, page, billing ? billing->values[0].string.text : "-",
++ hostname->values[0].string.text);
++
++ return (1);
++ }
++#endif /* HAVE_VSYSLOG */
++
++ /*
++ * Not using syslog; check the log file...
++ */
++
++ if (!check_log_file(&PageFile, PageLog))
++ return (0);
++
++ /*
++ * Print a page log entry of the form:
++ *
++ * printer job-id user [DD/MON/YYYY:HH:MM:SS +TTTT] page num-copies \
++ * billing hostname
++ */
++
++ cupsFilePrintf(PageFile, "%s %s %d %s %s %s %s\n", job->printer->name,
++ job->username ? job->username : "-",
++ job->id, cupsdGetDateTime(time(NULL)), page,
++ billing ? billing->values[0].string.text : "-",
++ hostname->values[0].string.text);
++ cupsFileFlush(PageFile);
++
++ return (1);
++}
++
++
++/*
++ * 'cupsdLogRequest()' - Log an HTTP request in Common Log Format.
++ */
++
++int /* O - 1 on success, 0 on error */
++cupsdLogRequest(cupsd_client_t *con, /* I - Request to log */
++ http_status_t code) /* I - Response code */
++{
++ static const char * const states[] = /* HTTP client states... */
++ {
++ "WAITING",
++ "OPTIONS",
++ "GET",
++ "GET",
++ "HEAD",
++ "POST",
++ "POST",
++ "POST",
++ "PUT",
++ "PUT",
++ "DELETE",
++ "TRACE",
++ "CLOSE",
++ "STATUS"
++ };
++
++ /* Do not flood the log with CUPS-Get-Printers requests */
++ if (con->request && code == HTTP_OK && (
++ con->request->request.op.operation_id == CUPS_GET_PRINTERS ||
++ con->request->request.op.operation_id == CUPS_GET_DEFAULT ||
++ con->request->request.op.operation_id == IPP_GET_PRINTER_ATTRIBUTES))
++ return (1);
++
++#ifdef HAVE_VSYSLOG
++ /*
++ * See if we are logging accesses via syslog...
++ */
++
++ if (!strcmp(AccessLog, "syslog"))
++ {
++ syslog(LOG_INFO,
++ "REQUEST %s - %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
++ con->http.hostname, con->username[0] != '\0' ? con->username : "-",
++ states[con->operation], con->uri,
++ con->http.version / 100, con->http.version % 100,
++ code, CUPS_LLCAST con->bytes,
++ con->request ?
++ ippOpString(con->request->request.op.operation_id) : "-",
++ con->response ?
++ ippErrorString(con->response->request.status.status_code) : "-");
++
++ return (1);
++ }
++#endif /* HAVE_VSYSLOG */
++
++ /*
++ * Not using syslog; check the log file...
++ */
++
++ if (!check_log_file(&AccessFile, AccessLog))
++ return (0);
++
++ /*
++ * Write a log of the request in "common log format"...
++ */
++
++ cupsFilePrintf(AccessFile,
++ "%s - %s %s \"%s %s HTTP/%d.%d\" %d " CUPS_LLFMT " %s %s\n",
++ con->http.hostname, con->username[0] != '\0' ? con->username : "-",
++ cupsdGetDateTime(con->start), states[con->operation], con->uri,
++ con->http.version / 100, con->http.version % 100,
++ code, CUPS_LLCAST con->bytes,
++ con->request ?
++ ippOpString(con->request->request.op.operation_id) : "-",
++ con->response ?
++ ippErrorString(con->response->request.status.status_code) :
++ "-");
++
++ cupsFileFlush(AccessFile);
++
++ return (1);
++}
++
++
++/*
++ * 'check_log_file()' - Open/rotate a log file if it needs it.
++ */
++
++static int /* O - 1 if log file open */
++check_log_file(cups_file_t **lf, /* IO - Log file */
++ const char *logname) /* I - Log filename */
++{
++ char backname[1024], /* Backup log filename */
++ filename[1024], /* Formatted log filename */
++ *ptr; /* Pointer into filename */
++
++
++ /*
++ * See if we have a log file to check...
++ */
++
++ if (!lf || !logname || !logname[0])
++ return (1);
+
- globalParams->setPSDuplex(duplex);
- globalParams->setPSExpandSmaller(fit);
- globalParams->setPSShrinkLarger(fit);
-diff -urNad cupsys-1.2.8~/scheduler/classes.c cupsys-1.2.8/scheduler/classes.c
---- cupsys-1.2.8~/scheduler/classes.c 2007-03-11 17:45:03.000000000 +0000
-+++ cupsys-1.2.8/scheduler/classes.c 2007-03-11 17:45:03.000000000 +0000
-@@ -3,7 +3,7 @@
- *
- * Printer class routines for the Common UNIX Printing System (CUPS).
- *
-- * Copyright 1997-2006 by Easy Software Products, all rights reserved.
-+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
- *
- * These coded instructions, statements, and computer programs are the
- * property of Easy Software Products and are protected by Federal
-@@ -371,7 +371,21 @@
- {
- cupsdLogMessage(CUPSD_LOG_DEBUG, "Loading class %s...", value);
-
-- p = cupsdAddClass(value);
-+ /*
-+ * Since prior classes may have implicitly defined this class,
-+ * see if it already exists...
-+ */
++ /*
++ * Format the filename as needed...
++ */
+
-+ if ((p = cupsdFindDest(value)) != NULL)
-+ {
-+ p->type = CUPS_PRINTER_CLASS;
-+ cupsdSetStringf(&p->uri, "ipp://%s:%d/classes/%s", ServerName,
-+ LocalPort, value);
-+ cupsdSetString(&p->error_policy, "retry-job");
-+ }
-+ else
-+ p = cupsdAddClass(value);
++ if (!*lf ||
++ (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
++ MaxLogSize > 0))
++ {
++ /*
++ * Handle format strings...
++ */
+
- p->accepting = 1;
- p->state = IPP_PRINTER_IDLE;
-
-@@ -708,6 +722,7 @@
- time_t curtime; /* Current time */
- struct tm *curdate; /* Current date */
- cups_option_t *option; /* Current option */
-+ const char *ptr; /* Pointer into info/location */
-
-
- /*
-@@ -782,10 +797,40 @@
- cupsFilePrintf(fp, "<Class %s>\n", pclass->name);
-
- if (pclass->info)
-- cupsFilePrintf(fp, "Info %s\n", pclass->info);
++ filename[sizeof(filename) - 1] = '\0';
++
++ if (logname[0] != '/')
+ {
-+ if ((ptr = strchr(pclass->info, '#')) != NULL)
++ strlcpy(filename, ServerRoot, sizeof(filename));
++ strlcat(filename, "/", sizeof(filename));
++ }
++ else
++ filename[0] = '\0';
++
++ for (ptr = filename + strlen(filename);
++ *logname && ptr < (filename + sizeof(filename) - 1);
++ logname ++)
++ if (*logname == '%')
+ {
+ /*
-+ * Need to quote the first # in the info string...
++ * Format spec...
+ */
+
-+ cupsFilePuts(fp, "Info ");
-+ cupsFileWrite(fp, pclass->info, ptr - pclass->info);
-+ cupsFilePutChar(fp, '\\');
-+ cupsFilePuts(fp, ptr);
-+ cupsFilePutChar(fp, '\n');
++ logname ++;
++ if (*logname == 's')
++ {
++ /*
++ * Insert the server name...
++ */
++
++ strlcpy(ptr, ServerName, sizeof(filename) - (ptr - filename));
++ ptr += strlen(ptr);
++ }
++ else
++ {
++ /*
++ * Otherwise just insert the character...
++ */
++
++ *ptr++ = *logname;
++ }
+ }
+ else
-+ cupsFilePrintf(fp, "Info %s\n", pclass->info);
++ *ptr++ = *logname;
++
++ *ptr = '\0';
++ }
++
++ /*
++ * See if the log file is open...
++ */
++
++ if (!*lf)
++ {
++ /*
++ * Nope, open the log file...
++ */
++
++ if ((*lf = cupsFileOpen(filename, "a")) == NULL)
++ {
++ syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
++ strerror(errno));
++
++ return (0);
+ }
-
- if (pclass->location)
-- cupsFilePrintf(fp, "Location %s\n", pclass->location);
++
++ if (strncmp(filename, "/dev/", 5))
+ {
-+ if ((ptr = strchr(pclass->info, '#')) != NULL)
-+ {
-+ /*
-+ * Need to quote the first # in the location string...
-+ */
++ /*
++ * Change ownership and permissions of non-device logs...
++ */
+
-+ cupsFilePuts(fp, "Location ");
-+ cupsFileWrite(fp, pclass->location, ptr - pclass->location);
-+ cupsFilePutChar(fp, '\\');
-+ cupsFilePuts(fp, ptr);
-+ cupsFilePutChar(fp, '\n');
-+ }
-+ else
-+ cupsFilePrintf(fp, "Location %s\n", pclass->location);
++ fchown(cupsFileNumber(*lf), RunUser, Group);
++ fchmod(cupsFileNumber(*lf), LogFilePerm);
+ }
-
- if (pclass->state == IPP_PRINTER_STOPPED)
- {
++ }
++
++ /*
++ * Do we need to rotate the log?
++ */
++
++ if (strncmp(logname, "/dev/", 5) && cupsFileTell(*lf) > MaxLogSize &&
++ MaxLogSize > 0)
++ {
++ /*
++ * Rotate log file...
++ */
++
++ cupsFileClose(*lf);
++
++ strcpy(backname, filename);
++ strlcat(backname, ".O", sizeof(backname));
++
++ unlink(backname);
++ rename(filename, backname);
++
++ if ((*lf = cupsFileOpen(filename, "a")) == NULL)
++ {
++ syslog(LOG_ERR, "Unable to open log file \"%s\" - %s", filename,
++ strerror(errno));
++
++ return (0);
++ }
++
++ /*
++ * Change ownership and permissions of non-device logs...
++ */
++
++ fchown(cupsFileNumber(*lf), RunUser, Group);
++ fchmod(cupsFileNumber(*lf), LogFilePerm);
++ }
++
++ return (1);
++}
++
++
++/*
++ * End of "$Id: log.c 6027 2006-10-11 21:04:58Z mike $".
++ */
diff -urNad cupsys-1.2.8~/scheduler/printers.c cupsys-1.2.8/scheduler/printers.c
--- cupsys-1.2.8~/scheduler/printers.c 2006-09-19 21:11:08.000000000 +0100
-+++ cupsys-1.2.8/scheduler/printers.c 2007-03-11 17:45:03.000000000 +0000
++++ cupsys-1.2.8/scheduler/printers.c 2007-03-13 15:02:07.000000000 +0000
@@ -1286,6 +1286,7 @@
time_t curtime; /* Current time */
struct tm *curdate; /* Current date */
@@ -269,9 +13003,53 @@
if (printer->device_uri)
cupsFilePrintf(fp, "DeviceURI %s\n", printer->device_uri);
+diff -urNad cupsys-1.2.8~/scheduler/testlpd.c cupsys-1.2.8/scheduler/testlpd.c
+--- cupsys-1.2.8~/scheduler/testlpd.c 2006-08-23 20:39:39.000000000 +0100
++++ cupsys-1.2.8/scheduler/testlpd.c 2007-03-13 15:02:07.000000000 +0000
+@@ -184,6 +184,11 @@
+ status = status_long(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
+ else if (!strcmp(op, "status-short"))
+ status = status_short(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs);
++ else
++ {
++ printf("Unknown operation \"%s\"!\n", op);
++ status = 1;
++ }
+
+ /*
+ * Kill the test program...
+@@ -296,8 +301,8 @@
+ "Hlocalhost\n"
+ "P%s\n"
+ "J%s\n"
+- "ldfA%03.3dlocalhost\n"
+- "UdfA%03.3dlocalhost\n"
++ "ldfA%03dlocalhost\n"
++ "UdfA%03dlocalhost\n"
+ "N%s\n",
+ cupsUser(), jobname, sequence, sequence, jobname);
+
+@@ -307,7 +312,7 @@
+
+ bytes = strlen(control);
+
+- snprintf(command, sizeof(command), "\002%d cfA%03.3dlocalhost\n",
++ snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n",
+ bytes, sequence);
+
+ if ((status = do_command(outfd, infd, command)) != 0)
+@@ -344,7 +349,7 @@
+ * Send the data file...
+ */
+
+- snprintf(command, sizeof(command), "\003%d dfA%03.3dlocalhost\n",
++ snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n",
+ (int)fileinfo.st_size, sequence);
+
+ if ((status = do_command(outfd, infd, command)) != 0)
diff -urNad cupsys-1.2.8~/systemv/cupstestppd.c cupsys-1.2.8/systemv/cupstestppd.c
--- cupsys-1.2.8~/systemv/cupstestppd.c 2006-09-05 21:45:47.000000000 +0100
-+++ cupsys-1.2.8/systemv/cupstestppd.c 2007-03-11 17:45:07.000000000 +0000
++++ cupsys-1.2.8/systemv/cupstestppd.c 2007-03-13 15:02:07.000000000 +0000
@@ -3,7 +3,7 @@
*
* PPD test program for the Common UNIX Printing System (CUPS).
@@ -517,3 +13295,1505 @@
+/*
* End of "$Id: cupstestppd.c 5926 2006-09-05 20:45:47Z mike $".
*/
+diff -urNad cupsys-1.2.8~/templates/fr/add-class.tmpl cupsys-1.2.8/templates/fr/add-class.tmpl
+--- cupsys-1.2.8~/templates/fr/add-class.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/add-class.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -31,36 +31,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-
+-<H2 CLASS="title">Ajouter une classe</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Nom :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127"></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Lieu :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127"></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Description :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127"></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Membres :</TH>
+-<TD>
+-<SELECT NAME="MEMBER_URIS" SIZE="10" MULTIPLE>
+-{[member_uris]<OPTION VALUE="{member_uris}" {?member_selected}>{member_names}}
+-</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-add-class.gif" ALT="Ajouter une classe"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/add-printer.tmpl cupsys-1.2.8/templates/fr/add-printer.tmpl
+--- cupsys-1.2.8~/templates/fr/add-printer.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/add-printer.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,50 +1,23 @@
+ <FORM METHOD="POST" ACTION="/admin">
+ <INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
++{device_uri?<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">:}
+
+ <H2 CLASS="title">Ajouter une nouvelle imprimante</H2>
+
+ <TABLE>
+ <TR>
+ <TH CLASS="label">Nom :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127"><BR>
+-<SMALL>( Peut comporter tout caractère imprimable, "/", "#", et espace exceptés )</SMALL></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Lieu :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127"><BR>
+-<SMALL>( Lieu compréhensible pour un utilisateur, comme "Labo 1" )</SMALL></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Description :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127"><BR>
+-<SMALL>( Description compréhensible pour un utilisateur, comme "HP LaserJet recto/verso" )</SMALL></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-
+-<H2 CLASS="title">Ajouter une nouvelle imprimante</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Nom :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127"><BR>
++<TD><INPUT TYPE="TEXT" NAME="PRINTER_NAME" SIZE="40" MAXLENGTH="127" VALUE="{?template_name}"><BR>
+ <SMALL>( Peut comporter tout caractère imprimable, "/", "#", et espace exceptés )</SMALL></TD>
+ </TR>
+ <TR>
+ <TH CLASS="label">Lieu :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127"><BR>
++<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" SIZE="40" MAXLENGTH="127" VALUE="{?PRINTER_LOCATION}"><BR>
+ <SMALL>( Lieu compréhensible pour un utilisateur, comme "Labo 1" )</SMALL></TD>
+ </TR>
+ <TR>
+ <TH CLASS="label">Description :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127"><BR>
++<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" SIZE="40" MAXLENGTH="127" VALUE="{?PRINTER_INFO}"><BR>
+ <SMALL>( Description compréhensible pour un utilisateur, comme "HP LaserJet recto/verso" )</SMALL></TD>
+ </TR>
+ <TR>
+diff -urNad cupsys-1.2.8~/templates/fr/admin.tmpl cupsys-1.2.8/templates/fr/admin.tmpl
+--- cupsys-1.2.8~/templates/fr/admin.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/admin.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,88 +1,5 @@
+ <TABLE CELLPADDING="0" CELLSPACING="0" WIDTH="100%" SUMMARY="Tâches d'administration">
+-<TR><TD VALIGN="TOP" NOWRAP>
+-
+-<H2 CLASS="title">Imprimantes</H2>
+-
+-<P>
+-<A HREF="/admin?op=add-printer"><IMG
+-SRC="/images/button-add-printer.gif" ALT="Ajouter une imprimante" CLASS="button"></A>
+-<A HREF="/printers/"><IMG SRC="/images/button-manage-printers.gif"
+-ALT="Administrer les imprimantes" CLASS="button"></A>
+-{have_samba?<A HREF="/admin/?op=export-samba"><IMG
+-SRC="/images/button-export-samba.gif" ALT="Exporter les imprimantes vers SAMBA"
+-CLASS="button"></A>:}
+-</P>
+-
+-{#device_uri=0?:<P><B>Nouvelles imprimantes détectées:</B></P><UL>{[device_uri]
+-<LI><A HREF="/admin?op=add-printer&{device_options}"><IMG
+-SRC="/images/button-add-this-printer.gif" ALT="Ajouter cette imprimante" CLASS="button"
+-ALIGN="MIDDLE"></A>
+-{device_make_and_model} ({device_info})</LI>
+-}</UL>}
+-
+-<H2 CLASS="title">Classes</H2>
+-
+-<P>
+-<A HREF="/admin?op=add-class"><IMG SRC="/images/button-add-class.gif"
+-ALT="Ajouter une classe" CLASS="button"></A>
+-<A HREF="/classes/"><IMG SRC="/images/button-manage-classes.gif"
+-ALT="Administrer les classes" CLASS="button"></A>
+-</P>
+-
+-<H2 CLASS="title">Tâches d'impression</H2>
+-
+-<P>
+-<A HREF="/jobs/"><IMG SRC="/images/button-manage-jobs.gif" ALT="Administrer les
+-tâches" CLASS="button"></A>
+-</P>
+-
+-</TD><TD> </TD><TD VALIGN="TOP">
+-
+-<H2 CLASS="title">Serveur</H2>
+-
+-<P>
+-<A HREF="/admin?op=config-server"><IMG
+-SRC="/images/button-edit-configuration-file.gif" ALT="Éditer le fichier de
+-configuration" CLASS="button"></A>
+-<A HREF="/admin/log/access_log" TARGET="_blank"><IMG
+-SRC="/images/button-view-access-log.gif" ALT="Liste des accès"
+-CLASS="button"></A>
+-<A HREF="/admin/log/error_log" TARGET="_blank"><IMG
+-SRC="/images/button-view-error-log.gif" ALT="Liste des erreurs"
+-CLASS="button"></A>
+-<A HREF="/admin/log/page_log" TARGET="_blank"><IMG
+-SRC="/images/button-view-page-log.gif" ALT="Liste des pages"
+-CLASS="button"></A>
+-</P>
+-
+-{SETTINGS_ERROR?<P>{SETTINGS_MESSAGE}</P>
+-<BLOCKQUOTE>{SETTINGS_ERROR}</BLOCKQUOTE>:
+-
+-<FORM METHOD="POST" ACTION="/admin">
+-
+-<P><B>Paramètres de base du serveur :</B></P>
+-
+-<P><INPUT TYPE="HIDDEN" NAME="OP" VALUE="config-server">
+-<INPUT TYPE="CHECKBOX" NAME="REMOTE_PRINTERS" {?remote_printers}> Afficher les
+-imprimantes partagées par d'autres systèmes<BR>
+-<INPUT TYPE="CHECKBOX" NAME="SHARE_PRINTERS" {?share_printers}> Partager les
+-imprimantes publiques connectées à ce système<BR>
+-<INPUT TYPE="CHECKBOX" NAME="REMOTE_ADMIN" {?remote_admin}> Autoriser
+-l'administration à distance<BR>
+-<INPUT TYPE="CHECKBOX" NAME="USER_CANCEL_ANY" {?user_cancel_any}> Autoriser les
+-utilisateurs à annuler n'importe quelle tâche ( pas seulement les leurs )<BR>
+-<INPUT TYPE="CHECKBOX" NAME="DEBUG_LOGGING" {?debug_logging}> Enregistrer les
+-informations de <I>debug</I> pour la résolution de problèmes</P>
+-
+-<P><INPUT TYPE="IMAGE" SRC="/images/button-change-settings.gif" ALT="Modifier
+-les paramètres"></P>
+-
+-</FORM>}
+-
+-</TD></TR>
+-</TABLE>
+-<TABLE CELLPADDING="0" CELLSPACING="0" WIDTH="100%" SUMMARY="Tâches d'administration">
+-<TR><TD VALIGN="TOP" NOWRAP>
++<TR><TD VALIGN="TOP">
+
+ <H2 CLASS="title">Imprimantes</H2>
+
+@@ -119,7 +36,7 @@
+ tâches" CLASS="button"></A>
+ </P>
+
+-</TD><TD> </TD><TD VALIGN="TOP">
++</TD><TD> </TD><TD VALIGN="TOP">
+
+ <H2 CLASS="title">Serveur</H2>
+
+@@ -150,6 +67,7 @@
+ imprimantes partagées par d'autres systèmes<BR>
+ <INPUT TYPE="CHECKBOX" NAME="SHARE_PRINTERS" {?share_printers}> Partager les
+ imprimantes publiques connectées à ce système<BR>
++ <INPUT TYPE="CHECKBOX" NAME="REMOTE_ANY" {?remote_any}> Allow printing from the Internet<BR>
+ <INPUT TYPE="CHECKBOX" NAME="REMOTE_ADMIN" {?remote_admin}> Autoriser
+ l'administration à distance<BR>
+ <INPUT TYPE="CHECKBOX" NAME="USER_CANCEL_ANY" {?user_cancel_any}> Autoriser les
+diff -urNad cupsys-1.2.8~/templates/fr/choose-device.tmpl cupsys-1.2.8/templates/fr/choose-device.tmpl
+--- cupsys-1.2.8~/templates/fr/choose-device.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/choose-device.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -24,29 +24,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+-<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{?current_make_and_model}">
+-
+-<H2 CLASS="title">Matériel pour {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Matériel :</TH>
+-<TD>
+-<SELECT NAME="DEVICE_URI">
+-{[device_uri]<OPTION VALUE="{device_uri}{?device_make_and_model!Unknown?|{device_make_and_model}:}" {?current_device_uri={device_uri}?SELECTED:{current_device_scheme={device_uri}?SELECTED:}}>
+-{device_info} {?device_make_and_model!Unknown?({device_make_and_model}):}</OPTION>
+-}</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/choose-make.tmpl cupsys-1.2.8/templates/fr/choose-make.tmpl
+--- cupsys-1.2.8~/templates/fr/choose-make.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/choose-make.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -40,45 +40,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin" ENCTYPE="multipart/form-data">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+-<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+-<INPUT TYPE="HIDDEN" NAME="BAUDRATE" VALUE="{?baudrate}">
+-<INPUT TYPE="HIDDEN" NAME="BITS" VALUE="{?bits}">
+-<INPUT TYPE="HIDDEN" NAME="PARITY" VALUE="{?parity}">
+-<INPUT TYPE="HIDDEN" NAME="FLOW" VALUE="{?flow}">
+-
+-<H2 CLASS="title">Marque/Fabricant pour {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Marque :</TH>
+-<TD>
+-<SELECT NAME="PPD_MAKE" SIZE="10">
+-{[ppd_make]<OPTION VALUE="{ppd_make}" {?current_make={ppd_make}?SELECTED:}>{ppd_make}}
+-</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD> </TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Ou donnez un fichier PPD :</TH>
+-<TD><INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="262144"><INPUT
+-TYPE="FILE" NAME="PPD_FILE"></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-{op}.gif" ALT="{op=add-printer?Ajouter une imprimante:Modifier l'imprimante}"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/choose-model.tmpl cupsys-1.2.8/templates/fr/choose-model.tmpl
+--- cupsys-1.2.8~/templates/fr/choose-model.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/choose-model.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -32,37 +32,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin" ENCTYPE="multipart/form-data">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+-<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+-<INPUT TYPE="HIDDEN" NAME="BAUDRATE" VALUE="{?baudrate}">
+-<INPUT TYPE="HIDDEN" NAME="BITS" VALUE="{?bits}">
+-<INPUT TYPE="HIDDEN" NAME="PARITY" VALUE="{?parity}">
+-<INPUT TYPE="HIDDEN" NAME="FLOW" VALUE="{?flow}">
+-
+-<H2 CLASS="title">Modèle/Pilote pour {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Modèle:</TH>
+-<TD>
+-<SELECT NAME="PPD_NAME" SIZE="10">
+-{[ppd_name]<OPTION VALUE="{ppd_name}" {?current_make_and_model={ppd_make_and_model}?SELECTED:}>{ppd_make_and_model} ({ppd_natural_language})
+-}</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Ou donnez un fichier PPD :</TH>
+-<TD><INPUT TYPE="HIDDEN" NAME="MAX_FILE_SIZE" VALUE="262144"><INPUT
+-TYPE="FILE" NAME="PPD_FILE"></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-{op}.gif" ALT="{op=add-printer?Ajouter une imprimante:Modifier l'imprimante}"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/choose-serial.tmpl cupsys-1.2.8/templates/fr/choose-serial.tmpl
+--- cupsys-1.2.8~/templates/fr/choose-serial.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/choose-serial.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -45,50 +45,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+-<INPUT TYPE="HIDDEN" NAME="DEVICE_URI" VALUE="{device_uri}">
+-
+-<H2 CLASS="title">Paramètres du port série pour {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Baud/s :</TH>
+-<TD><SELECT NAME="BAUDRATE">
+-{[baudrates]<OPTION {?baudrate={baudrates}?SELECTED:}>{baudrates}}
+-</SELECT></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Parité :</TH>
+-<TD><SELECT NAME="PARITY">
+-<OPTION VALUE="none" {?parity=none?SELECTED:}>Aucune
+-<OPTION VALUE="even" {?parity=even?SELECTED:}>Paire
+-<OPTION VALUE="odd" {?parity=odd?SELECTED:}>Impaire
+-</SELECT></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Bits données :</TH>
+-<TD><SELECT NAME="BITS">
+-<OPTION {?bits=8?SELECTED:}>8
+-<OPTION {?bits=7?SELECTED:}>7
+-</SELECT></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Contrôle de flux :</TH>
+-<TD><SELECT NAME="FLOW">
+-<OPTION VALUE="none" {?flow=none?SELECTED:}>Auncun
+-<OPTION VALUE="soft" {?flow=soft?SELECTED:}>XON/XOFF ( Logiciel )
+-<OPTION VALUE="hard" {?flow=hard?SELECTED:}>RTS/CTS ( Matériel )
+-<OPTION VALUE="dtrdsr" {?flow=dtrdsr?SELECTED:}>DTR/DSR ( Matériel )
+-</SELECT></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/choose-uri.tmpl cupsys-1.2.8/templates/fr/choose-uri.tmpl
+--- cupsys-1.2.8~/templates/fr/choose-uri.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/choose-uri.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -40,45 +40,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_LOCATION" VALUE="{?printer_location}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_INFO" VALUE="{?printer_info}">
+-<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{?current_make_and_model}">
+-
+-<H2 CLASS="title">URI du matériel pour {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">URI du matériel :</TH>
+-<TD><INPUT TYPE="TEXT" SIZE="60" MAXLENGTH="1024" NAME="DEVICE_URI" VALUE="{device_uri}"></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD>Exemples :
+-<PRE>
+- http://nom_machine:631/ipp/
+- http://nom_machine:631/ipp/port1
+-
+- ipp://nom_machine/ipp/
+- ipp://nom_machine/ipp/port1
+-
+- lpd://nom_machine/queue
+-
+- socket://nom_machine
+- socket://nom_machine:9100
+-</PRE>
+-
+-<P>cf. <A HREF="/help/network.html" TARGET="_blank">"Network
+-Printers"</A> pour construire l'URI à employer avec votre imprimante.</P>
+-
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/class-added.tmpl cupsys-1.2.8/templates/fr/class-added.tmpl
+--- cupsys-1.2.8~/templates/fr/class-added.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/class-added.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>La classe <A HREF="/classes/{printer_name}">{printer_name}</A> a bien été ajoutée.
+-<P>La classe <A HREF="/classes/{printer_name}">{printer_name}</A> a bien été ajoutée.
+diff -urNad cupsys-1.2.8~/templates/fr/class-confirm.tmpl cupsys-1.2.8/templates/fr/class-confirm.tmpl
+--- cupsys-1.2.8~/templates/fr/class-confirm.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/class-confirm.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ HREF="/admin?op=delete-class&printer_name={printer_name}&confirm=yes"><IMG
+ SRC="/images/button-delete-class.gif" ALT="Supprimer la classe"
+ CLASS="button"></A></P>
+-<P><B>Attention :</B> Êtes vous sûr(e) de vouloir supprimer la classe
+-{printer_name}?</P>
+-
+-<P ALIGN="CENTER"><A
+-HREF="/admin?op=delete-class&printer_name={printer_name}&confirm=yes"><IMG
+-SRC="/images/button-delete-class.gif" ALT="Supprimer la classe"
+-CLASS="button"></A></P>
+diff -urNad cupsys-1.2.8~/templates/fr/class-deleted.tmpl cupsys-1.2.8/templates/fr/class-deleted.tmpl
+--- cupsys-1.2.8~/templates/fr/class-deleted.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/class-deleted.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>La classe {printer_name} a bien été supprimée.
+-<P>La classe {printer_name} a bien été supprimée.
+diff -urNad cupsys-1.2.8~/templates/fr/class-jobs-header.tmpl cupsys-1.2.8/templates/fr/class-jobs-header.tmpl
+--- cupsys-1.2.8~/templates/fr/class-jobs-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/class-jobs-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <H3 CLASS="title">Tâches d'impression</H3>
+-<H3 CLASS="title">Tâches d'impression</H3>
+diff -urNad cupsys-1.2.8~/templates/fr/class-modified.tmpl cupsys-1.2.8/templates/fr/class-modified.tmpl
+--- cupsys-1.2.8~/templates/fr/class-modified.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/class-modified.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>La classe <A HREF="/classes/{printer_name}">{printer_name}</A> a bien été modifiée.
+-<P>La classe <A HREF="/classes/{printer_name}">{printer_name}</A> a bien été modifiée.
+diff -urNad cupsys-1.2.8~/templates/fr/classes-header.tmpl cupsys-1.2.8/templates/fr/classes-header.tmpl
+--- cupsys-1.2.8~/templates/fr/classes-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/classes-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P ALIGN="CENTER">{total=0?Aucune classe:Affichage de {#printer_name} classe{#printer_name=1?:s} sur {total}}.</P>
+-<P ALIGN="CENTER">{total=0?Aucune classe:Affichage de {#printer_name} classe{#printer_name=1?:s} sur {total}}.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/classes.tmpl cupsys-1.2.8/templates/fr/classes.tmpl
+--- cupsys-1.2.8~/templates/fr/classes.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/classes.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -52,57 +52,3 @@
+ </TR>
+ </TABLE>
+ }}
+-{#printer_name=0?:
+-{[printer_name]
+-<H2 CLASS="title"><A HREF="{printer_uri_supported}">{printer_name}</A>{default_name={printer_name}? ( Imprimante par défaut ) :}
+-{?printer_state_message=?:<SPAN CLASS="message">"{printer_state_message}"</SPAN>}</H2>
+-
+-<TABLE WIDTH="100%" CLASS="button" CELLSPACING="0" CELLPADDING="0" SUMMARY="{printer_name}">
+-<TR>
+-<TD VALIGN=TOP><A HREF="{printer_uri_supported}">
+-<IMG SRC="/images/classes.gif" CLASS="button" ALT=""></A></TD>
+-<TD VALIGN=TOP><B>Description :</B> {printer_info}<BR>
+-<B>Lieu :</B> {printer_location}<BR>
+-<B>État de la classe:</B> {printer_state=3?ne fait rien:{printer_state=4?en cours d'impression:arrêtée}},
+-{printer_is_accepting_jobs=0?rejette les tâches:accepte les tâches}, {printer_is_shared=0?cachée:publique}.
+-{?member_uris=?:<BR>Membres : {member_uris}}
+-
+-<P>
+-<A HREF="{printer_uri_supported}?op=print-test-page">
+-<IMG SRC="/images/button-print-test-page.gif" ALT="Imprimer une page de test" CLASS="button"></A>
+-{printer_state=5?
+-<A HREF="{admin_uri}?op=start-class&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-start-class.gif" ALT="Démarrer la classe" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=stop-class&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-stop-class.gif" ALT="Arrêter la classe" CLASS="button"></A>
+-}
+-{printer_is_accepting_jobs=0?
+-<A HREF="{admin_uri}?op=accept-jobs&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-accept-jobs.gif" ALT="Accepter les tâches" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=reject-jobs&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-reject-jobs.gif" ALT="Rejeter les tâches" CLASS="button"></A>
+-}
+-<A HREF="{admin_uri}?op=purge-jobs&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-cancel-all-jobs.gif" ALT="Annuler toutes les tâches" CLASS="button"></A>
+-{printer_is_shared=0?
+-<A HREF="{admin_uri}?op=set-sharing&printer_name={%printer_name}&shared=1&is_class=Y">
+-<IMG SRC="/images/button-publish-printer.gif" ALT="Publier l'imprimante" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=set-sharing&printer_name={%printer_name}&shared=0&is_class=Y">
+-<IMG SRC="/images/button-unpublish-printer.gif" ALT="Cacher l'imprimante" CLASS="button"></A>
+-}
+-<A HREF="{admin_uri}?op=modify-class&printer_name={%printer_name}">
+-<IMG SRC="/images/button-modify-class.gif" ALT="Modifier la classe" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=delete-class&printer_name={%printer_name}">
+-<IMG SRC="/images/button-delete-class.gif" ALT="Supprimer la classe" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=set-as-default&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-set-as-default.gif" ALT="Choisir par défaut" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=set-allowed-users&printer_name={%printer_name}&is_class=Y">
+-<IMG SRC="/images/button-set-allowed-users.gif" ALT="Définir les autorisations" CLASS="button"></A>
+-</P>
+-</TD>
+-</TR>
+-</TABLE>
+-}}
+diff -urNad cupsys-1.2.8~/templates/fr/edit-config.tmpl.in cupsys-1.2.8/templates/fr/edit-config.tmpl.in
+--- cupsys-1.2.8~/templates/fr/edit-config.tmpl.in 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/edit-config.tmpl.in 2007-03-13 15:02:07.000000000 +0000
+@@ -9,6 +9,7 @@
+ "\\n" +
+ "# Groupe des utilisateurs UNIX pour les administrateurs CUPS...\\n" +
+ "SystemGroup @CUPS_SYSTEM_GROUPS@\\n" +
++"@CUPS_SYSTEM_AUTHKEY@\\n" +
+ "\\n" +
+ "\\n" +
+ "# N'accepter que les connexions de la machine locale.\\n" +
+@@ -19,98 +20,7 @@
+ "# Publier les imprimantes partagées sur le réseau local.\\n" +
+ "Browsing On\\n" +
+ "BrowseOrder allow,deny\\n" +
+-"BrowseAllow @LOCAL\\n" +
+-"\\n" +
+-"\\n" +
+-"# S'authentifier par défaut via les comptes UNIX...\\n" +
+-"DefaultAuthType Basic\\n" +
+-"\\n" +
+-"# Restreindre l'accès au serveur...\\n" +
+-"<Location />\\n" +
+-" Order allow,deny\\n" +
+-" Allow localhost\\n" +
+-"</Location>\\n" +
+-"\\n" +
+-"# Restreindre l'accès aux pages d'administration...\\n" +
+-"<Location /admin>\\n" +
+-"@ENCRYPTION_REQUIRED@\\n" +
+-" Order allow,deny\\n" +
+-" Allow localhost\\n" +
+-"</Location>\\n" +
+-"\\n" +
+-"# Restreindre l'accès au ficher de configuration...\\n" +
+-"<Location /admin/conf>\\n" +
+-" AuthType Basic\\n" +
+-" Require user @SYSTEM\\n" +
+-" Order allow,deny\\n" +
+-" Allow localhost\\n" +
+-"</Location>\\n" +
+-"\\n" +
+-"# Définir la politique par défaut des tâches d'impression...\\n" +
+-"<Policy default>\\n" +
+-" # Les opérations sur les tâches doivent être faites par leur propriétaire ou un adminstrateur...\\n" +
+-" <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job CUPS-Move-Job>\\n" +
+-" Require user @OWNER @SYSTEM\\n" +
+-" Order deny,allow\\n" +
+-" </Limit>\\n" +
+-"\\n" +
+-" # Toutes les opérations d'administration nécessite l'authentification d'un adminstrateur...\\n" +
+-" <Limit Pause-Printer Resume-Printer Set-Printer-Attributes Enable-Printer Disable-Printer Pause-Printer-After-Current-Job Hold-New-Jobs Release-Held-New-Jobs Deactivate-Printer Activate-Printer Restart-Printer Shutdown-Printer Startup-Printer Promote-Job Schedule-Job-After CUPS-Add-Printer CUPS-Delete-Printer CUPS-Add-Class CUPS-Delete-Class CUPS-Accept-Jobs CUPS-Reject-Jobs CUPS-Set-Default>\\n" +
+-" AuthType Basic\\n" +
+-" Require user @SYSTEM\\n" +
+-" Order deny,allow\\n" +
+-" </Limit>\\n" +
+-"\\n" +
+-" # Seuls le propriétaire et un administrateur peuvent annuler ou authentifier une tâche...\\n" +
+-" <Limit Cancel-Job CUPS-Authenticate-Job>\\n" +
+-" Require user @OWNER @SYSTEM\\n" +
+-" Order deny,allow\\n" +
+-" </Limit>\\n" +
+-"\\n" +
+-" <Limit All>\\n" +
+-" Order deny,allow\\n" +
+-" </Limit>\\n" +
+-"</Policy>\\n";
+-}
+-</SCRIPT>
+-
+-<H2 CLASS="title">Ficher de configuration du serveur</H2>
+-
+-<FORM NAME="cups" METHOD="POST" ACTION="/admin/">
+-
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="config-server">
+-
+-<TEXTAREA NAME="CUPSDCONF" COLS="80" ROWS="25">{CUPSDCONF}</TEXTAREA>
+-
+-<P><INPUT TYPE="IMAGE" SRC="/images/button-save-changes.gif"
+-ALT="Enregistrer les modifications"> <A
+-HREF="javascript:reset_config();"><IMG
+-SRC="/images/button-use-default-config.gif" CLASS="button"
+-ALT="Utiliser la configuration par défaut"></A></P>
+-
+-</FORM>
+-<SCRIPT TYPE="text/javascript">
+-function reset_config()
+-{
+- document.cups.CUPSDCONF.value =
+-"# Écrire des informations générales dans error_log - changez \\"info\\" en \\"debug\\"\\n" +
+-"# pour la résolution de problème...\\n" +
+-"LogLevel info\\n" +
+-"\\n" +
+-"\\n" +
+-"# Groupe des utilisateurs UNIX pour les administrateurs CUPS...\\n" +
+-"SystemGroup @CUPS_SYSTEM_GROUPS@\\n" +
+-"\\n" +
+-"\\n" +
+-"# N'accepter que les connexions de la machine locale.\\n" +
+-"Listen localhost:@DEFAULT_IPP_PORT@\\n" +
+-"@CUPS_LISTEN_DOMAINSOCKET@\\n" +
+-"\\n" +
+-"\\n" +
+-"# Publier les imprimantes partagées sur le réseau local.\\n" +
+-"Browsing On\\n" +
+-"BrowseOrder allow,deny\\n" +
+-"BrowseAllow @LOCAL\\n" +
++"BrowseAllow all\\n" +
+ "\\n" +
+ "\\n" +
+ "# S'authentifier par défaut via les comptes UNIX...\\n" +
+diff -urNad cupsys-1.2.8~/templates/fr/error-op.tmpl cupsys-1.2.8/templates/fr/error-op.tmpl
+--- cupsys-1.2.8~/templates/fr/error-op.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/error-op.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>Erreur :</P>
+
+ <BLOCKQUOTE>Opération inconnue : "{op}"!</BLOCKQUOTE>
+-<P>Erreur :</P>
+-
+-<BLOCKQUOTE>Opération inconnue : "{op}"!</BLOCKQUOTE>
+diff -urNad cupsys-1.2.8~/templates/fr/error.tmpl cupsys-1.2.8/templates/fr/error.tmpl
+--- cupsys-1.2.8~/templates/fr/error.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/error.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{?message?{message}:Erreur :}</P>
+
+ <BLOCKQUOTE>{error}</BLOCKQUOTE>
+-<P>{?message?{message}:Erreur :}</P>
+-
+-<BLOCKQUOTE>{error}</BLOCKQUOTE>
+diff -urNad cupsys-1.2.8~/templates/fr/header.tmpl.in cupsys-1.2.8/templates/fr/header.tmpl.in
+--- cupsys-1.2.8~/templates/fr/header.tmpl.in 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/header.tmpl.in 2007-03-13 15:02:07.000000000 +0000
+@@ -61,66 +61,3 @@
+ <TR CLASS="page">
+ <TD WIDTH="15"> </TD>
+ <TD COLSPAN="2" WIDTH="100%" STYLE="height: 100%;" VALIGN="TOP" CLASS="page">
+-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+-<HTML>
+-<HEAD>
+- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+- <TITLE>{title} - CUPS @CUPS_VERSION@@CUPS_REVISION@</TITLE>
+- <!-- Prevent caching of CGI content -->
+- <META HTTP-EQUIV="Expires" CONTENT="now">
+- <META HTTP-EQUIV="Pragma" CONTENT="no-cache">
+- {refresh_page?<META HTTP-EQUIV="Refresh" CONTENT="{refresh_page}">:}
+- <LINK REL="STYLESHEET" TYPE="text/css" HREF="/cups.css">
+- <LINK REL="SHORTCUT ICON" HREF="/favicon.ico" TYPE="image/x-icon">
+-</HEAD>
+-<BODY>
+-<TABLE WIDTH="100%" STYLE="height: 100%;" BORDER="0" CELLSPACING="0" CELLPADDING="0" SUMMARY="{title}">
+-<TR CLASS="HEADER">
+-<TD VALIGN="TOP" WIDTH="15" ROWSPAN="2"><IMG SRC="/images/top-left.gif" WIDTH="15" HEIGHT="80" ALT=""></TD>
+-<TD VALIGN="TOP" WIDTH="55" ROWSPAN="2"><IMG SRC="/images/top-middle.gif" WIDTH="55" HEIGHT="80" ALT=""></TD>
+-<TD WIDTH="100%" HEIGHT="60"><H1>{title}</H1></TD>
+-<TD ALIGN="RIGHT" VALIGN="TOP" WIDTH="15" ROWSPAN="2"><IMG
+-SRC="/images/top-right.gif" WIDTH="15" HEIGHT="15" ALT=""></TD>
+-</TR>
+-<TR CLASS="HEADER"><TD WIDTH="100%" VALIGN="BOTTOM" NOWRAP>
+-
+-<A CLASS="unsel" HREF="/"><IMG SRC="/images/tab-left.gif"
+-WIDTH="4" HEIGHT="4" ALIGN="TOP" BORDER="0"
+-ALT=""> Accueil <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+- <A CLASS="{SECTION=admin?:un}sel" HREF="/admin"><IMG
+-SRC="/images/tab-left.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""> Administration <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+- <A CLASS="{SECTION=classes?:un}sel" HREF="/classes/"><IMG
+-SRC="/images/tab-left.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""> Classes <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+- <A CLASS="{SECTION=help?:un}sel" HREF="/help/"><IMG
+-SRC="/images/tab-left.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""> Documentation/Aide <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+- <A CLASS="{SECTION=jobs?:un}sel" HREF="/jobs/"><IMG
+-SRC="/images/tab-left.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""> Tâches <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+- <A CLASS="{SECTION=printers?:un}sel" HREF="/printers/"><IMG
+-SRC="/images/tab-left.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""> Imprimantes <IMG
+-SRC="/images/tab-right.gif" WIDTH="4" HEIGHT="4" ALIGN="TOP"
+-BORDER="0" ALT=""></A>
+-
+-</TD></TR>
+-<TR CLASS="page">
+-<TD WIDTH="15"> </TD>
+-<TD COLSPAN="2" WIDTH="100%" STYLE="height: 100%;" VALIGN="TOP" CLASS="page">
+diff -urNad cupsys-1.2.8~/templates/fr/help-header.tmpl cupsys-1.2.8/templates/fr/help-header.tmpl
+--- cupsys-1.2.8~/templates/fr/help-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/help-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -50,55 +50,3 @@
+ utilisateurs, des réponses aux questions fréquentes, et un formulaire pour
+ soumettre des rapports de <I>bug</I> ou des demandes pour de nouvelles
+ fonctionnalités.</P>}
+-<FORM ACTION="/help/{?HELPFILE}" METHOD="GET">
+-{TOPIC?<INPUT TYPE="HIDDEN" NAME="TOPIC" VALUE="{TOPIC}">:}
+-
+-<P ALIGN="CENTER"><B>Rechercher dans
+-{HELPTITLE?{HELPTITLE}:{TOPIC?{TOPIC}:Tous les Documents}}:</B> <INPUT
+-TYPE="TEXT" NAME="QUERY" VALUE="{?QUERY}" SIZE="60"> <INPUT
+-TYPE="IMAGE" SRC="/images/button-search.gif" ALT="Search">
+-<A HREF="/help/{?HELPFILE}{QUERY?{TOPIC??TOPIC={TOPIC}:}:}"><IMG
+-SRC="/images/button-clear.gif" ALT="Clear" CLASS="button"></A></P>
+-
+-</FORM>
+-
+-<!-- Bookmarks -->
+-<DIV CLASS="sidebar">
+-<H3 CLASS="title">Documents d'aide en ligne</H3>
+-
+-<P CLASS="l0"><A HREF="/help/{QUERY??QUERY={QUERY}:}">Tous les documents</A></P>
+-<HR>
+-
+-{[BMTEXT]<P CLASS="l{BMINDENT}"><A HREF="{BMLINK}">{BMTEXT}</A></P>
+-}
+-</DIV>
+-
+-{QUERY?<P>Résultats de la recherche dans {HELPFILE?{HELPTITLE}:{TOPIC?{TOPIC}:Tous les Documents}}\:</P>
+-{QTEXT?<UL>
+-{[QTEXT]<LI><A HREF="{QLINK}">{QTEXT}</A>{QPTEXT? (in <I><A HREF="{QPLINK}">{QPTEXT}</A></I>):}</LI>}
+-{QTEXT?</UL>:}
+-:<P>Aucun résultat.</P>}
+-<HR NOSHADE>:}
+-{HELPTITLE?<H1>{HELPTITLE}
+-<A HREF="/help/{HELPFILE}?PRINTABLE=YES" TARGET="_blank"><IMG
+-SRC="/images/button-view-printable-version.gif" ALT="Version imprimable"
+-CLASS="button"></A></H1>:<H1>Pages d'aide de CUPS</H1>
+-
+-<P>Voici l'interface d'aide en ligne de CUPS. Entrez ci-dessus les mots à rechercher
+-ou cliquez sur un lien ci-contre pour afficher l'ade en ligne du document.</P>
+-
+-<P><SMALL><B>NDT:</B> Cette interface n'a pas été traduite en Français. Si vous souhaitez vous
+-atteler à cette tâche, merci d'aller au préalable sur le forum <A
+-HREF="http://www.cups.org/newsgroups.php?gcups.development">cups.development</A>
+-pour en informer les développeurs.</SMALL></P>
+-
+-<P>Si vous êtes un nouvel utilisateur de CUPS, lisez la page "<a
+-href="overview.html">Overview of CUPS</a>". Il est conseillé aux utilisateurs
+-habitués de lire la page "<a href="whatsnew.html">What's New in CUPS
+-1.2</a>".</P>
+-
+-<P>La <A HREF="http://www.cups.org/">page d'accueil de CUPS</A> donne aussi
+-accès à de nombreuses ressources, comme des forums de discussion pour les
+-utilisateurs, des réponses aux questions fréquentes, et un formulaire pour
+-soumettre des rapports de <I>bug</I> ou des demandes pour de nouvelles
+-fonctionnalités.</P>}
+diff -urNad cupsys-1.2.8~/templates/fr/help-printable.tmpl cupsys-1.2.8/templates/fr/help-printable.tmpl
+--- cupsys-1.2.8~/templates/fr/help-printable.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/help-printable.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -9,14 +9,3 @@
+ <BODY>
+
+ <H1>{HELPTITLE}</H1>
+-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
+-<HTML>
+-<HEAD>
+- <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
+- <TITLE>{HELPTITLE}</TITLE>
+- <LINK REL="STYLESHEET" TYPE="text/css" HREF="/cups-printable.css">
+- <LINK REL="SHORTCUT ICON" HREF="/favicon.ico" TYPE="image/x-icon">
+-</HEAD>
+-<BODY>
+-
+-<H1>{HELPTITLE}</H1>
+diff -urNad cupsys-1.2.8~/templates/fr/job-cancel.tmpl cupsys-1.2.8/templates/fr/job-cancel.tmpl
+--- cupsys-1.2.8~/templates/fr/job-cancel.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-cancel.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été annulée.
+-<P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été annulée.
+diff -urNad cupsys-1.2.8~/templates/fr/job-hold.tmpl cupsys-1.2.8/templates/fr/job-hold.tmpl
+--- cupsys-1.2.8~/templates/fr/job-hold.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-hold.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> est retenue en attente.
+-<P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> est retenue en attente.
+diff -urNad cupsys-1.2.8~/templates/fr/job-move.tmpl cupsys-1.2.8/templates/fr/job-move.tmpl
+--- cupsys-1.2.8~/templates/fr/job-move.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-move.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -20,25 +20,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/{SECTION}/{job_id?:{printer_name}}">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-{job_id?<INPUT TYPE="HIDDEN" NAME="JOB_ID" VALUE="{job_id}">:}
+-
+-<H2 CLASS="title">{job_id?Transférer la tâche n°{job_id}:Transférer toutes les tâches}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Nouvelle destination :</TH>
+-<TD>
+-<SELECT NAME="JOB_PRINTER_URI" SIZE="10">
+-{[job_printer_uri]<OPTION VALUE="{job_printer_uri}">{job_printer_name}}
+-</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-move-job{job_id?:s}.gif" ALT="Transférer {job_id?la tâche:les tâches}"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/job-moved.tmpl cupsys-1.2.8/templates/fr/job-moved.tmpl
+--- cupsys-1.2.8~/templates/fr/job-moved.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-moved.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,4 +1,2 @@
+ <P>{job_id?<A HREF="/jobs/{job_id}">La tâche n°{job_id}</A>:Toutes les tâches}
+ ont été transférées vers <A HREF="{job_printer_uri}">{job_printer_name}</A>.</P>
+-<P>{job_id?<A HREF="/jobs/{job_id}">La tâche n°{job_id}</A>:Toutes les tâches}
+-ont été transférées vers <A HREF="{job_printer_uri}">{job_printer_name}</A>.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/job-release.tmpl cupsys-1.2.8/templates/fr/job-release.tmpl
+--- cupsys-1.2.8~/templates/fr/job-release.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-release.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été libérée pour l'impression.
+-<P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été libérée pour l'impression.
+diff -urNad cupsys-1.2.8~/templates/fr/job-restart.tmpl cupsys-1.2.8/templates/fr/job-restart.tmpl
+--- cupsys-1.2.8~/templates/fr/job-restart.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/job-restart.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été redémarrée.
+-<P><A HREF="{job_printer_uri}">La tâche n°{job_id}</A> a été redémarrée.
+diff -urNad cupsys-1.2.8~/templates/fr/jobs-header.tmpl cupsys-1.2.8/templates/fr/jobs-header.tmpl
+--- cupsys-1.2.8~/templates/fr/jobs-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/jobs-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -14,19 +14,3 @@
+
+ <P ALIGN="CENTER">{total=0?Aucune tâche:Affichage de {#job_id}
+ tâche{#job_id=1?:s} {?which_jobs=?en cours:{which_jobs=all?:terminée{#job_id=1?:s}}} sur {total}}.</P>
+-<P>{?which_jobs=?<A
+-HREF="{?printer_name=?/jobs:{printer_uri_supported}}?which_jobs=completed"><IMG
+-SRC="/images/button-show-completed.gif" CLASS="button" ALT="Montrer les tâches terminées"></A>
+-<A HREF="{?printer_name=?/jobs:{printer_uri_supported}}?which_jobs=all"><IMG
+-SRC="/images/button-show-all.gif" CLASS="button" ALT="Montrer toutes les tâches">:{which_jobs=all?<A
+-HREF="{?printer_name=?/jobs:{printer_uri_supported}}?which_jobs=completed"><IMG
+-SRC="/images/button-show-completed.gif" CLASS="button" ALT="Montrer les tâches terminées"></A>
+-<A HREF="{?printer_name=?/jobs:{printer_uri_supported}}"><IMG
+-SRC="/images/button-show-active.gif" CLASS="button" ALT="Montrer les tâches en cours">:<A
+-HREF="{?printer_name=?/jobs:{printer_uri_supported}}"><IMG
+-SRC="/images/button-show-active.gif" CLASS="button" ALT="Montrer les tâches en cours"></A>
+-<A HREF="{?printer_name=?/jobs:{printer_uri_supported}}?which_jobs=all"><IMG
+-SRC="/images/button-show-all.gif" CLASS="button" ALT="Montrer toutes les tâches">}}</A></P>
+-
+-<P ALIGN="CENTER">{total=0?Aucune tâche:Affichage de {#job_id}
+-tâche{#job_id=1?:s} {?which_jobs=?en cours:{which_jobs=all?:terminée{#job_id=1?:s}}} sur {total}}.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/jobs.tmpl cupsys-1.2.8/templates/fr/jobs.tmpl
+--- cupsys-1.2.8~/templates/fr/jobs.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/jobs.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -40,45 +40,3 @@
+ }
+ </TABLE>
+ }
+-{#job_id=0?:
+-<TABLE CELLPADDING="0" CELLSPACING="0" CLASS="button" WIDTH="100%" SUMMARY="Liste de tâches">
+-<TR CLASS="data">
+-<TH>N° </TH>
+-<TH>Nom </TH>
+-<TH>Utilisateur </TH>
+-<TH>Taille </TH>
+-<TH>Pages </TH>
+-<TH>État </TH>
+-<TH>Contrôle </TH>
+-</TR>
+-
+-{[job_id]
+-<TR CLASS="data" VALIGN="TOP">
+-<TD><A HREF="{job_printer_uri}">{job_printer_name}</A>-{job_id} </TD>
+-<TD>{?job_name=?Inconnue:{job_name}} </TD>
+-<TD>{job_originating_user_name} </TD>
+-<TD>{job_k_octets}ko </TD>
+-<TD>{job_media_sheets_completed=0?Inconnue:{?job_media_sheets_completed}}</TD>
+-<TD>{job_state=3?en attente depuis le<BR>{time_at_creation}:{job_state=4?retenue depuis le<BR>{time_at_creation}:
+-{job_state=5?en cours depuis le<BR>{time_at_processing}:{job_state=6?arrêtée:
+-{job_state=7?annulée le<BR>{time_at_completed}:{job_state=8?abandonnée:terminée le<BR>{time_at_completed}}}}}}} </TD>
+-<TD>
+-{job_preserved>0?
+-<A HREF="/jobs/?op=restart-job&job_id={job_id}&job_printer_uri={job_printer_uri}">
+-<IMG SRC="/images/button-restart-job.gif" ALT="Redémarrer la tâche" CLASS="button"></A>:}
+-{job_state=4?
+-<A HREF="/jobs/?op=release-job&job_id={job_id}&job_printer_uri={job_printer_uri}">
+-<IMG SRC="/images/button-release-job.gif" ALT="Libérer la tâche" CLASS="button"></A>:}
+-{job_state=3?
+-<A HREF="/jobs/?op=hold-job&job_id={job_id}&job_printer_uri={job_printer_uri}">
+-<IMG SRC="/images/button-hold-job.gif" ALT="Retenir la tâche" CLASS="button"></A>:}
+-{job_state<7?
+-<A HREF="/jobs/?op=cancel-job&job_id={job_id}&job_printer_uri={job_printer_uri}">
+-<IMG SRC="/images/button-cancel-job.gif" ALT="Annuler la tâche" CLASS="button"></A>
+-<A HREF="/jobs/?op=move-job&job_id={job_id}"><IMG
+-SRC="/images/button-move-job.gif" ALT="Transférer la tâche" CLASS="button"></A>:}
+- </TD>
+-</TR>
+-}
+-</TABLE>
+-}
+diff -urNad cupsys-1.2.8~/templates/fr/maintenance.tmpl cupsys-1.2.8/templates/fr/maintenance.tmpl
+--- cupsys-1.2.8~/templates/fr/maintenance.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/maintenance.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,4 +1,2 @@
+ <P>Les commandes de maintenance ont été envoyées ; l'identifiant de tâche est <A
+ HREF="/printers/{printer_name}"> {printer_name}-{job_id}</A>.</P>
+-<P>Les commandes de maintenance ont été envoyées ; l'identifiant de tâche est <A
+-HREF="/printers/{printer_name}"> {printer_name}-{job_id}</A>.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/modify-class.tmpl cupsys-1.2.8/templates/fr/modify-class.tmpl
+--- cupsys-1.2.8~/templates/fr/modify-class.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/modify-class.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -32,37 +32,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-
+-<H2 CLASS="title">Modifier la classe {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Nom :</TH>
+-<TD><INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-{printer_name}</TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Lieu :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" VALUE="{?printer_location}" SIZE="40" MAXLENGTH="127"></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Description :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" VALUE="{?printer_info}" SIZE="40" MAXLENGTH="127"></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Membres :</TH>
+-<TD>
+-<SELECT NAME="MEMBER_URIS" SIZE="10" MULTIPLE>
+-{[member_uris]<OPTION VALUE="{member_uris}" {?member_selected}>{member_names}}
+-</SELECT>
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-modify-class.gif" ALT="Modifier la classe"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/modify-printer.tmpl cupsys-1.2.8/templates/fr/modify-printer.tmpl
+--- cupsys-1.2.8~/templates/fr/modify-printer.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/modify-printer.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -27,32 +27,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-{?device_uri=?:<INPUT TYPE="HIDDEN" NAME="CURRENT_DEVICE_URI" VALUE="{device_uri}">}
+-{?printer_make_and_model=?:<INPUT TYPE="HIDDEN" NAME="CURRENT_MAKE_AND_MODEL" VALUE="{printer_make_and_model}">}
+-
+-<H2 CLASS="title">Modifier l'imprimante {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Nom :</TH>
+-<TD><INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">{printer_name}</TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Lieu :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_LOCATION" VALUE="{?printer_location}" SIZE="40" MAXLENGTH="127"><BR>
+-<SMALL>( Lieu compréhensible pour un utilisateur, comme "Labo 1" )</SMALL></TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Description :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="PRINTER_INFO" VALUE="{?printer_info}" SIZE="40" MAXLENGTH="127"><BR>
+-<SMALL>( Description compréhensible pour un utilisateur, comme "HP LaserJet recto/verso" )</SMALL></TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-continue.gif" ALT="Poursuivre"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/norestart.tmpl cupsys-1.2.8/templates/fr/norestart.tmpl
+--- cupsys-1.2.8~/templates/fr/norestart.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/norestart.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <p>Le serveur n'a pas été redémarré parce que la configuration n'a pas été modifiée...</p>
+-<p>Le serveur n'a pas été redémarré parce que la configuration n'a pas été modifiée...</p>
+diff -urNad cupsys-1.2.8~/templates/fr/option-boolean.tmpl cupsys-1.2.8/templates/fr/option-boolean.tmpl
+--- cupsys-1.2.8~/templates/fr/option-boolean.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-boolean.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ {[choices]<INPUT TYPE="RADIO" NAME="{keyword}" {choices={defchoice}?CHECKED:} VALUE="{choices}">{text}}
+ </TD>
+ </TR>
+-<TR>
+-<TH CLASS="label"{conflicted=1? CLASS="conflict":}><A
+-NAME="{keyword}">{keytext}</A> :</TH>
+-<TD>
+-{[choices]<INPUT TYPE="RADIO" NAME="{keyword}" {choices={defchoice}?CHECKED:} VALUE="{choices}">{text}}
+-</TD>
+-</TR>
+diff -urNad cupsys-1.2.8~/templates/fr/option-conflict.tmpl cupsys-1.2.8/templates/fr/option-conflict.tmpl
+--- cupsys-1.2.8~/templates/fr/option-conflict.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-conflict.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ }</UL>
+
+ <P>Veuillez modifier une ou plusieurs des ces options pour résoudre les conflits.</P>
+-<P><B>Erreur :</B> Les options suivantes sont incompatibles entre elles :</P>
+-
+-<UL>
+-{[ckeyword]<LI><A HREF="#{ckeyword}">{ckeytext}</A></LI>
+-}</UL>
+-
+-<P>Veuillez modifier une ou plusieurs des ces options pour résoudre les conflits.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/option-header.tmpl cupsys-1.2.8/templates/fr/option-header.tmpl
+--- cupsys-1.2.8~/templates/fr/option-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <H2 CLASS="title">{printer_name} : {group}</H2>
+
+ <TABLE>
+-<H2 CLASS="title">{printer_name} : {group}</H2>
+-
+-<TABLE>
+diff -urNad cupsys-1.2.8~/templates/fr/option-pickmany.tmpl cupsys-1.2.8/templates/fr/option-pickmany.tmpl
+--- cupsys-1.2.8~/templates/fr/option-pickmany.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-pickmany.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ {[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+ </SELECT></TD>
+ </TR>
+-<TR>
+-<TH CLASS="label"{conflicted=1? CLASS="conflict":}><A
+-NAME="{keyword}">{keytext}</A> :</TH>
+-<TD><SELECT NAME="{keyword}" MULTIPLE SIZE="10">
+-{[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+-</SELECT></TD>
+-</TR>
+diff -urNad cupsys-1.2.8~/templates/fr/option-pickone.tmpl cupsys-1.2.8/templates/fr/option-pickone.tmpl
+--- cupsys-1.2.8~/templates/fr/option-pickone.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-pickone.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ {[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+ </SELECT></TD>
+ </TR>
+-<TR>
+-<TH CLASS="label"{conflicted=1? CLASS="conflict":}><A
+-NAME="{keyword}">{keytext}</A> :</TH>
+-<TD><SELECT NAME="{keyword}">
+-{[choices]<OPTION {choices={defchoice}?SELECTED:} VALUE="{choices}">{text}}
+-</SELECT></TD>
+-</TR>
+diff -urNad cupsys-1.2.8~/templates/fr/option-trailer.tmpl cupsys-1.2.8/templates/fr/option-trailer.tmpl
+--- cupsys-1.2.8~/templates/fr/option-trailer.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/option-trailer.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -4,9 +4,3 @@
+ ALT="Définir les options de l'imprimante"></TD>
+ </TR>
+ </TABLE>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-set-printer-options.gif"
+-ALT="Définir les options de l'imprimante"></TD>
+-</TR>
+-</TABLE>
+diff -urNad cupsys-1.2.8~/templates/fr/pager.tmpl cupsys-1.2.8/templates/fr/pager.tmpl
+--- cupsys-1.2.8~/templates/fr/pager.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/pager.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -9,14 +9,3 @@
+ SRC="/images/button-show-next.gif" ALT="Montrer les suivantes" CLASS="button"></A>: }</TD>
+ </TR>
+ </TABLE>
+-<TABLE WIDTH="100%" CLASS="pager" SUMMARY="Mise en page">
+-<TR>
+- <TD WIDTH="33%">{PREVURL?<A HREF="{PREVURL}"><IMG
+- SRC="/images/button-show-previous.gif" ALT="Montrer les précédentes" CLASS="button"></A>: }</TD>
+- <TD WIDTH="34%" ALIGN="CENTER">{ORDER=dec?<A
+- HREF="{THISURL}&ORDER=asc"><IMG
+- SRC="/images/button-sort-ascending.gif" ALT="Par ordre croissant" CLASS="button"></A>:<A HREF="{THISURL}&ORDER=dec"><IMG SRC="/images/button-sort-descending.gif" ALT="Par ordre décroissant" CLASS="button"></A>}</TD>
+- <TD WIDTH="33%" ALIGN="RIGHT">{NEXTURL?<A HREF="{NEXTURL}"><IMG
+- SRC="/images/button-show-next.gif" ALT="Montrer les suivantes" CLASS="button"></A>: }</TD>
+-</TR>
+-</TABLE>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-accept.tmpl cupsys-1.2.8/templates/fr/printer-accept.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-accept.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-accept.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{is_class?Class:Printer} <A
+ HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+ accepte désormais les tâches d'impression.</P>
+-<P>{is_class?Class:Printer} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-accepte désormais les tâches d'impression.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-added.tmpl cupsys-1.2.8/templates/fr/printer-added.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-added.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-added.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été ajoutée.
+-<P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été ajoutée.
+diff -urNad cupsys-1.2.8~/templates/fr/printer-configured.tmpl cupsys-1.2.8/templates/fr/printer-configured.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-configured.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-configured.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été configurée.
+-<P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été configurée.
+diff -urNad cupsys-1.2.8~/templates/fr/printer-confirm.tmpl cupsys-1.2.8/templates/fr/printer-confirm.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-confirm.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-confirm.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ HREF="/admin?op=delete-printer&printer_name={printer_name}&confirm=yes"><IMG
+ SRC="/images/button-delete-printer.gif" ALT="Supprimer l'imprimante"
+ CLASS="button"></A></P>
+-<P><B>Attention :</B> Êtes-vous sûr(e) de vouloir supprimer l'imprimante
+-{printer_name} ?</P>
+-
+-<P ALIGN="CENTER"><A
+-HREF="/admin?op=delete-printer&printer_name={printer_name}&confirm=yes"><IMG
+-SRC="/images/button-delete-printer.gif" ALT="Supprimer l'imprimante"
+-CLASS="button"></A></P>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-default.tmpl cupsys-1.2.8/templates/fr/printer-default.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-default.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-default.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -5,10 +5,3 @@
+ <BLOCKQUOTE><B>NB:</B> Pour un utilisateur qui a défini un paramètre par défaut
+ via la commande <TT>lpoptions</TT>, le paramètre du serveur sera
+ ignoré.</BLOCKQUOTE>
+-<P>{is_class?La classe:L'imprimante} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-a été définie comme imprimante par défaut sur le serveur.</P>
+-
+-<BLOCKQUOTE><B>NB:</B> Pour un utilisateur qui a défini un paramètre par défaut
+-via la commande <TT>lpoptions</TT>, le paramètre du serveur sera
+-ignoré.</BLOCKQUOTE>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-deleted.tmpl cupsys-1.2.8/templates/fr/printer-deleted.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-deleted.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-deleted.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>L'imprimante {printer_name} a bien été supprimée.
+-<P>L'imprimante {printer_name} a bien été supprimée.
+diff -urNad cupsys-1.2.8~/templates/fr/printer-jobs-header.tmpl cupsys-1.2.8/templates/fr/printer-jobs-header.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-jobs-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-jobs-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <H3 CLASS="title">Tâches d'impression</H3>
+-<H3 CLASS="title">Tâches d'impression</H3>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-modified.tmpl cupsys-1.2.8/templates/fr/printer-modified.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-modified.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-modified.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été modifiée.
+-<P>L'imprimante <A HREF="/printers/{printer_name}">{printer_name}</A> a bien été modifiée.
+diff -urNad cupsys-1.2.8~/templates/fr/printer-purge.tmpl cupsys-1.2.8/templates/fr/printer-purge.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-purge.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-purge.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{is_class?La classe:L'imprimante} <A
+ HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+ a été nettoyée de toute tâche... d'impression !</P>
+-<P>{is_class?La classe:L'imprimante} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-a été nettoyée de toute tâche... d'impression !</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-reject.tmpl cupsys-1.2.8/templates/fr/printer-reject.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-reject.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-reject.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{is_class?La classe:L'imprimante} <A
+ HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+ n'accepte plus les tâches d'impression.</P>
+-<P>{is_class?La classe:L'imprimante} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-n'accepte plus les tâches d'impression.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-start.tmpl cupsys-1.2.8/templates/fr/printer-start.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-start.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-start.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{is_class?La classe:L'imprimante} <A
+ HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+ a été démarrée.</P>
+-<P>{is_class?La classe:L'imprimante} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-a été démarrée.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printer-stop.tmpl cupsys-1.2.8/templates/fr/printer-stop.tmpl
+--- cupsys-1.2.8~/templates/fr/printer-stop.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printer-stop.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <P>{is_class?La classe:L'imprimante} <A
+ HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+ a été arrêtée.</P>
+-<P>{is_class?La classe:L'imprimante} <A
+-HREF="/{is_class?classes:printers}/{printer_name}">{printer_name}</A>
+-a été arrêtée.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printers-header.tmpl cupsys-1.2.8/templates/fr/printers-header.tmpl
+--- cupsys-1.2.8~/templates/fr/printers-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printers-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P ALIGN="CENTER">{total=0?Aucune imprimante:Affichage de {#printer_name} imprimante{#printer_name=1?:s}} sur {total}.</P>
+-<P ALIGN="CENTER">{total=0?Aucune imprimante:Affichage de {#printer_name} imprimante{#printer_name=1?:s}} sur {total}.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/printers.tmpl cupsys-1.2.8/templates/fr/printers.tmpl
+--- cupsys-1.2.8~/templates/fr/printers.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/printers.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -64,69 +64,3 @@
+ </TR>
+ </TABLE>
+ }}
+-{printer_type?:}{#printer_name=0?:
+-{[printer_name]
+-<H2 CLASS="title"><A
+-HREF="{printer_uri_supported}">{printer_name}</A>{default_name={printer_name}? (
+-imprimante par défaut ) :}
+-{?printer_state_message=?:<SPAN CLASS="message">"{printer_state_message}"</SPAN>}</H2>
+-
+-<TABLE WIDTH="100%" CLASS="button" CELLSPACING="0" CELLPADDING="0" SUMMARY="{printer_name}">
+-<TR>
+-<TD VALIGN="TOP"><A HREF="{printer_uri_supported}">
+-<IMG SRC="/images/printer-{printer_state=3?idle:{printer_state=4?processing:stopped}}.gif" CLASS="button" ALT=""></A>
+-</TD>
+-<TD VALIGN="TOP"><B>Description :</B> {printer_info}<BR>
+-<B>Lieu :</B> {printer_location}<BR>
+-<B>Marque et modèle :</B> {printer_make_and_model}<BR>
+-<B>État de l'imprimante :</B> {printer_state=3?ne fait rien:{printer_state=4?en cours d'impression:arrêtée}},
+-{printer_is_accepting_jobs=0?rejette les tâches:accepte les tâches}, {printer_is_shared=0?cachée:publique}.
+-{?device_uri=?:<BR><B>URI du matériel :</B> {device_uri}}
+-
+-<P>
+-<A HREF="{printer_uri_supported}?op=print-test-page">
+-<IMG SRC="/images/button-print-test-page.gif" ALT="Imprimer la page de test CUPS" CLASS="button"></A>
+-{?cupscommand=1?<A HREF="{printer_uri_supported}?op=clean-print-heads">
+-<IMG SRC="/images/button-clean-print-heads.gif" ALT="Nettoyer les têtes d'impression" CLASS="button"></A>
+-<A HREF="{printer_uri_supported}?op=print-self-test-page">
+-<IMG SRC="/images/button-print-self-test-page.gif" ALT="Imprimer la page de test de l'imprimante" CLASS="button"></A>:}
+-{printer_state=5?
+-<A HREF="{admin_uri}?op=start-printer&printer_name={%printer_name}">
+-<IMG SRC="/images/button-start-printer.gif" ALT="Démarrer l'imprimante" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=stop-printer&printer_name={%printer_name}">
+-<IMG SRC="/images/button-stop-printer.gif" ALT="Arrêter l'imprimante" CLASS="button"></A>
+-}
+-{printer_is_accepting_jobs=0?
+-<A HREF="{admin_uri}?op=accept-jobs&printer_name={%printer_name}">
+-<IMG SRC="/images/button-accept-jobs.gif" ALT="Accepter les tâches" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=reject-jobs&printer_name={%printer_name}">
+-<IMG SRC="/images/button-reject-jobs.gif" ALT="Rejeter les tâches" CLASS="button"></A>
+-}
+-<A HREF="{printer_uri_supported}?op=move-jobs&printer_name={%printer_name}">
+-<IMG SRC="/images/button-move-jobs.gif" ALT="Transférer toutes les tâches" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=purge-jobs&printer_name={%printer_name}">
+-<IMG SRC="/images/button-cancel-all-jobs.gif" ALT="Annuler toutes les tâches" CLASS="button"></A>
+-{printer_is_shared=0?
+-<A HREF="{admin_uri}?op=set-sharing&printer_name={%printer_name}&shared=1">
+-<IMG SRC="/images/button-publish-printer.gif" ALT="Publier l'imprimante" CLASS="button"></A>
+-:
+-<A HREF="{admin_uri}?op=set-sharing&printer_name={%printer_name}&shared=0">
+-<IMG SRC="/images/button-unpublish-printer.gif" ALT="Cacher l'imprimante" CLASS="button"></A>
+-}
+-<A HREF="{admin_uri}?op=modify-printer&printer_name={%printer_name}">
+-<IMG SRC="/images/button-modify-printer.gif" ALT="Modifier l'imprimante" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=set-printer-options&printer_name={%printer_name}">
+-<IMG SRC="/images/button-set-printer-options.gif" ALT="Définir les options de l'imprimante" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=delete-printer&printer_name={%printer_name}">
+-<IMG SRC="/images/button-delete-printer.gif" ALT="Supprimer l'imprimante" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=set-as-default&printer_name={%printer_name}">
+-<IMG SRC="/images/button-set-as-default.gif" ALT="Définir par défaut" CLASS="button"></A>
+-<A HREF="{admin_uri}?op=set-allowed-users&printer_name={%printer_name}">
+-<IMG SRC="/images/button-set-allowed-users.gif" ALT="Définir les autorisations" CLASS="button"></A>
+-</P>
+-</TD>
+-</TR>
+-</TABLE>
+-}}
+diff -urNad cupsys-1.2.8~/templates/fr/restart.tmpl cupsys-1.2.8/templates/fr/restart.tmpl
+--- cupsys-1.2.8~/templates/fr/restart.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/restart.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <p>Attendez s'il vous plaît, que le serveur redémarre...</p>
+-<p>Attendez s'il vous plaît, que le serveur redémarre...</p>
+diff -urNad cupsys-1.2.8~/templates/fr/samba-export.tmpl cupsys-1.2.8/templates/fr/samba-export.tmpl
+--- cupsys-1.2.8~/templates/fr/samba-export.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/samba-export.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -51,56 +51,3 @@
+ </TABLE>
+
+ </FORM>
+-<SCRIPT TYPE="text/javascript"><!--
+-function select_printers() {
+- var list = document.export_samba.EXPORT_NAME;
+- var sel = document.export_samba.EXPORT_ALL.checked;
+-
+- for (i = 0; i < list.length; i ++) {
+- list.options[i].selected = sel;
+- }
+-}
+---></SCRIPT>
+-
+-<FORM METHOD="POST" ACTION="/admin/" NAME="export_samba">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="export-samba">
+-
+-<H2 CLASS="title">Exporter des imprimantes vers SAMBA</H2>
+-
+-{error?<P>Impossible d'exporter les imprimantes vers SAMBA \:</P>
+-<BLOCKQUOTE>{error}</BLOCKQUOTE>
+-<P>Regardez le fichier <A HREF="/admin/log/error_log"
+-TARGET="_blank">error_log</A> pour plus d'informations.</P>:
+-<P>Cette page vous permet d'exporter des imprimantes vers SAMBA de sorte que des
+-clients Windows puissent y accéder via l'icône <VAR>Voisinage réseau</VAR> ou
+-<VAR>Favoris réseau</VAR> du bureau. Vous devez au préalable installer les
+-pilotes Windows d'imprimante PostScript : cf. la page <i>man</i> <A
+-HREF="/help/man-cupsaddsmb.html" TARGET="_blank">cupsaddsmb(8)</A>.</P>}
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Imprimantes :</TH>
+-<TD>
+-<SELECT NAME="EXPORT_NAME" SIZE="10" MULTIPLE>
+-{[printer_name]<OPTION VALUE="{printer_name}"{export_all? SELECTED:{printer_export? SELECTED:}}>{printer_name}}
+-</SELECT><BR>
+-<INPUT TYPE="CHECKBOX" NAME="EXPORT_ALL"{export_all? CHECKED:}
+-onChange="select_printers()"> Exporter toutes les imprimantes
+-</TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Utilisateur SAMBA :</TH>
+-<TD><INPUT TYPE="TEXT" NAME="USERNAME" VALUE="{?USERNAME}"> ( indispensable )</TD>
+-</TR>
+-<TR>
+-<TH CLASS="label">Mot-de-passe SAMBA :</TH>
+-<TD><INPUT TYPE="PASSWORD" NAME="PASSWORD" VALUE=""> ( indispensable )</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD><INPUT TYPE="IMAGE" SRC="/images/button-export-samba.gif"
+-ALT="Exporter les imprimantes vers SAMBA"></TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/samba-exported.tmpl cupsys-1.2.8/templates/fr/samba-exported.tmpl
+--- cupsys-1.2.8~/templates/fr/samba-exported.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/samba-exported.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ <P>Les imprimantes ont bien été exportées vers SAMBA.</P>
+-<P>Les imprimantes ont bien été exportées vers SAMBA.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/search.tmpl cupsys-1.2.8/templates/fr/search.tmpl
+--- cupsys-1.2.8~/templates/fr/search.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/search.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -11,16 +11,3 @@
+ SRC="/images/button-clear.gif" ALT="Nettoyer" CLASS="button"></A></P>
+
+ </FORM>
+-<FORM ACTION="/{SECTION}/{?SEARCH_DEST}" METHOD="GET">
+-{WHICH_JOBS?<INPUT TYPE="HIDDEN" NAME="WHICH_JOBS" VALUE="{WHICH_JOBS}">:}
+-{ORDER?<INPUT TYPE="HIDDEN" NAME="ORDER" VALUE="{ORDER}">:}
+-
+-<P ALIGN="CENTER"><B>Rechercher dans
+-{SEARCH_DEST?{SEARCH_DEST}:{SECTION=classes?les classes:{SECTION=jobs?les tâches:les imprimantes}}} :</B>
+-<INPUT TYPE="TEXT" NAME="QUERY" VALUE="{?QUERY}" SIZE="60"> <INPUT
+-TYPE="IMAGE" SRC="/images/button-search.gif" ALT="Rechercher">
+-<A
+-HREF="/{SECTION}/{?SEARCH_DEST}{WHICH_JOBS??WHICH_JOBS={WHICH_JOBS}{ORDER?&ORDER={ORDER}:}:{ORDER??ORDER={ORDER}:}}"><IMG
+-SRC="/images/button-clear.gif" ALT="Nettoyer" CLASS="button"></A></P>
+-
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/set-printer-options-header.tmpl cupsys-1.2.8/templates/fr/set-printer-options-header.tmpl
+--- cupsys-1.2.8~/templates/fr/set-printer-options-header.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/set-printer-options-header.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,6 +1,3 @@
+ <FORM METHOD="POST" ACTION="/admin">
+ <INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+ <INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{op}">
+diff -urNad cupsys-1.2.8~/templates/fr/set-printer-options-trailer.tmpl cupsys-1.2.8/templates/fr/set-printer-options-trailer.tmpl
+--- cupsys-1.2.8~/templates/fr/set-printer-options-trailer.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/set-printer-options-trailer.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,2 +1 @@
+ </FORM>
+-</FORM>
+diff -urNad cupsys-1.2.8~/templates/fr/test-page.tmpl cupsys-1.2.8/templates/fr/test-page.tmpl
+--- cupsys-1.2.8~/templates/fr/test-page.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/test-page.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -1,4 +1,2 @@
+ <P>La page de test a été envoyée ; l'identifiant de la tâche est <A HREF="/{SECTION}/{printer_name}">
+ {printer_name}-{job_id}</A>.</P>
+-<P>La page de test a été envoyée ; l'identifiant de la tâche est <A HREF="/{SECTION}/{printer_name}">
+-{printer_name}-{job_id}</A>.</P>
+diff -urNad cupsys-1.2.8~/templates/fr/trailer.tmpl cupsys-1.2.8/templates/fr/trailer.tmpl
+--- cupsys-1.2.8~/templates/fr/trailer.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/trailer.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -19,24 +19,3 @@
+ </TABLE>
+ </BODY>
+ </HTML>
+-</TD>
+-<TD WIDTH="15"> </TD>
+-</TR>
+-<TR CLASS="trailer">
+-<TD VALIGN="BOTTOM" WIDTH="15"><IMG SRC="/images/bottom-left.gif"
+-WIDTH="15" HEIGHT="15" ALT=""></TD>
+-<TD COLSPAN="2" WIDTH="100%" STYLE="padding: 5;">
+-
+-<P><SMALL>Le logiciel CUPS ( Common UNIX Printing System ) et son logo sont
+-propriété commerciale de <A HREF="http://www.easysw.com">Easy Software
+-Products</A>. CUPS est sous copyright 1997-2006 par Easy Software Products, Tous
+-Droits Réservés.</SMALL></P>
+-
+-</TD>
+-
+-<TD ALIGN="RIGHT" VALIGN="BOTTOM" WIDTH="15"><IMG SRC="/images/bottom-right.gif"
+-WIDTH="15" HEIGHT="15" ALT=""></TD>
+-</TR>
+-</TABLE>
+-</BODY>
+-</HTML>
+diff -urNad cupsys-1.2.8~/templates/fr/users.tmpl cupsys-1.2.8/templates/fr/users.tmpl
+--- cupsys-1.2.8~/templates/fr/users.tmpl 2007-02-05 20:25:50.000000000 +0000
++++ cupsys-1.2.8/templates/fr/users.tmpl 2007-03-13 15:02:07.000000000 +0000
+@@ -24,29 +24,3 @@
+ </TABLE>
+
+ </FORM>
+-<FORM METHOD="POST" ACTION="/admin">
+-<INPUT TYPE="HIDDEN" NAME="OP" VALUE="{OP}">
+-<INPUT TYPE="HIDDEN" NAME="PRINTER_NAME" VALUE="{printer_name}">
+-{IS_CLASS?<INPUT TYPE="HIDDEN" NAME="IS_CLASS" VALUE="{IS_CLASS}">:}
+-
+-<H2 CLASS="title">Utilisateurs autorisés à utiliser {printer_name}</H2>
+-
+-<TABLE>
+-<TR>
+-<TH CLASS="label">Utilisateurs :</TH>
+-<TD>
+-<INPUT TYPE='TEXT' NAME='users' SIZE='60' VALUE='{?requesting_user_name_allowed}{?requesting_user_name_denied}'>
+-<BR>
+-<INPUT TYPE='RADIO' NAME='type' VALUE='requesting-user-name-allowed' {requesting_user_name_allowed?checked:}>Autoriser ces utilisateurs à imprimer
+-<INPUT TYPE='RADIO' NAME='type' VALUE='requesting-user-name-denied' {requesting_user_name_denied?checked:}>Empêcher ces utilisateurs d'imprimer
+-</TD>
+-</TR>
+-<TR>
+-<TD></TD>
+-<TD>
+-<INPUT TYPE="IMAGE" SRC="/images/button-set-allowed-users.gif" ALT="Définir les autorisations">
+-</TD>
+-</TR>
+-</TABLE>
+-
+-</FORM>
Added: cupsys/branches/cups-1.2-ubuntu/debian/patches/98_search_mime_files_in_usr_share.dpatch
==============================================================================
--- (empty file)
+++ cupsys/branches/cups-1.2-ubuntu/debian/patches/98_search_mime_files_in_usr_share.dpatch Wed Mar 14 08:25:28 2007
@@ -0,0 +1,190 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## 98_search_mime_files_in_usr_share.dpatch by <till.kamppeter at gmail.com>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: No description.
+
+ at DPATCH@
+diff -urNad cupsys-1.2.8~/scheduler/conf.c cupsys-1.2.8/scheduler/conf.c
+--- cupsys-1.2.8~/scheduler/conf.c 2007-03-13 17:43:16.000000000 +0000
++++ cupsys-1.2.8/scheduler/conf.c 2007-03-13 17:48:52.000000000 +0000
+@@ -216,6 +216,7 @@
+ cups_file_t *fp; /* Configuration file */
+ int status; /* Return status */
+ char temp[1024], /* Temporary buffer */
++ temp2[1024], /* Another temporary buffer */
+ *slash; /* Directory separator */
+ cups_lang_t *language; /* Language */
+ struct passwd *user; /* Default user */
+@@ -973,19 +974,20 @@
+ */
+
+ snprintf(temp, sizeof(temp), "%s/filter", ServerBin);
++ snprintf(temp2, sizeof(temp2), "%s/mime:%s", DataDir, ServerRoot);
+
+- MimeDatabase = mimeLoad(ServerRoot, temp);
++ MimeDatabase = mimeLoad(temp2, temp);
+
+ if (!MimeDatabase)
+ {
+ cupsdLogMessage(CUPSD_LOG_EMERG,
+- "Unable to load MIME database from \'%s\'!", ServerRoot);
++ "Unable to load MIME database from \'%s\'!", temp2);
+ exit(errno);
+ }
+
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "Loaded MIME database from \'%s\': %d types, %d filters...",
+- ServerRoot, mimeNumTypes(MimeDatabase),
++ temp2, mimeNumTypes(MimeDatabase),
+ mimeNumFilters(MimeDatabase));
+
+ /*
+diff -urNad cupsys-1.2.8~/scheduler/mime.c cupsys-1.2.8/scheduler/mime.c
+--- cupsys-1.2.8~/scheduler/mime.c 2006-05-30 20:40:34.000000000 +0100
++++ cupsys-1.2.8/scheduler/mime.c 2007-03-13 17:45:01.000000000 +0000
+@@ -217,9 +217,12 @@
+
+ mime_t * /* O - Updated MIME database */
+ mimeMerge(mime_t *mime, /* I - MIME database to add to */
+- const char *pathname, /* I - Directory to load */
+- const char *filterpath) /* I - Directory to load */
++ const char *pathname, /* I - Directory/ies to load */
++ const char *filterpath) /* I - Filter directory */
+ {
++ char *paths, /* Pointers for parsing mime */
++ *dirname, /* directories */
++ *colon;
+ cups_dir_t *dir; /* Directory */
+ cups_dentry_t *dent; /* Directory entry */
+ char filename[1024]; /* Full filename of types/converts file */
+@@ -234,62 +237,88 @@
+ if (!pathname)
+ return (NULL);
+
+- if ((dir = cupsDirOpen(pathname)) == NULL)
+- return (NULL);
++ paths = strdup(pathname); /* copy path as we are writing into it */
++ dirname = paths;
++ colon = paths;
++ while (colon != NULL) { /* Go through each of the colon-separated
++ directories */
++ colon = strchr(dirname, ':');
++ if (colon != NULL) *colon = '\0';
+
+- /*
+- * If "mime" is NULL, make a new, blank database...
+- */
++ if ((dir = cupsDirOpen(dirname)) == NULL)
++ continue;
+
+- if (!mime)
+- mime = mimeNew();
+- if (!mime)
+- return (NULL);
++ /*
++ * If "mime" is NULL, make a new, blank database...
++ */
+
+- /*
+- * Read all the .types files...
+- */
++ if (!mime)
++ mime = mimeNew();
++ if (!mime)
++ return (NULL);
+
+- while ((dent = cupsDirRead(dir)) != NULL)
+- {
+- if (strlen(dent->filename) > 6 &&
+- !strcmp(dent->filename + strlen(dent->filename) - 6, ".types"))
+- {
+- /*
+- * Load a mime.types file...
+- */
++ /*
++ * Read all the .types files...
++ */
+
+- snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
+- load_types(mime, filename);
+- }
+- }
++ while ((dent = cupsDirRead(dir)) != NULL)
++ {
++ if (strlen(dent->filename) > 6 &&
++ !strcmp(dent->filename + strlen(dent->filename) - 6, ".types"))
++ {
++ /*
++ * Load a mime.types file...
++ */
++
++ snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->filename);
++ load_types(mime, filename);
++ }
++ }
+
+- cupsDirRewind(dir);
++ cupsDirClose(dir);
++ if (colon != NULL) dirname = colon + 1;
++ }
++ free(paths); /* We need to copy the original path again for reading
++ the convs */
+
+- /*
+- * Read all the .convs files...
+- */
++ /*
++ * Read all the .convs files...
++ */
+
+ filtercache = cupsArrayNew((cups_array_func_t)compare_fcache, NULL);
+
+- while ((dent = cupsDirRead(dir)) != NULL)
+- {
+- if (strlen(dent->filename) > 6 &&
+- !strcmp(dent->filename + strlen(dent->filename) - 6, ".convs"))
+- {
+- /*
+- * Load a mime.convs file...
+- */
++ paths = strdup(pathname); /* copy path as we are writing into it */
++ dirname = paths;
++ colon = paths;
++ while (colon != NULL) { /* Go through each of the colon-separated
++ directories */
++ colon = strchr(dirname, ':');
++ if (colon != NULL) *colon = '\0';
+
+- snprintf(filename, sizeof(filename), "%s/%s", pathname, dent->filename);
+- load_convs(mime, filename, filterpath, filtercache);
+- }
++ if ((dir = cupsDirOpen(dirname)) == NULL)
++ continue;
++
++ while ((dent = cupsDirRead(dir)) != NULL)
++ {
++ if (strlen(dent->filename) > 6 &&
++ !strcmp(dent->filename + strlen(dent->filename) - 6, ".convs"))
++ {
++ /*
++ * Load a mime.convs file...
++ */
++
++ snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->filename);
++ load_convs(mime, filename, filterpath, filtercache);
++ }
++ }
++
++ cupsDirClose(dir);
++ if (colon != NULL) dirname = colon + 1;
+ }
++ free(paths);
+
+ delete_fcache(filtercache);
+
+- cupsDirClose(dir);
+-
+ return (mime);
+ }
+
More information about the Pkg-cups-devel
mailing list