~ubuntu-branches/ubuntu/utopic/cups/utopic

« back to all changes in this revision

Viewing changes to .pc/revert-upstream-change-to-FINAL_CONTENT_TYPE-to-fix-printing-to-remote-CUPS-servers.patch/scheduler/job.c

  • Committer: Package Import Robot
  • Author(s): Didier Raboud, Till Kamppeter, Steve Langasek, Didier Raboud
  • Date: 2014-01-03 18:42:39 UTC
  • mfrom: (99.2.3 sid)
  • Revision ID: package-import@ubuntu.com-20140103184239-85wju2l7weie4dgo
Tags: 1.7.0-1
* New 1.7.0 upstream release

[ Till Kamppeter ]
* Refresh most patches with quilt
* Removed usb-backend-do-not-crash-if-usb-disabled-in-bios and
  cupsd-no-crash-on-avahi-threaded-poll-shutdown patches as they got
  applied upstream
* Removed drop-arch-specifics-from-doc patch as it is not needed
  anymore
* Updated drop_unnecessary_dependencies, manpage-hyphen-minus,
  manpage-translations and ppd-poll-with-client-conf patches manually
  to apply to the new CUPS version
* Added error counting exception from
  usb-backend-do-not-crash-if-usb-disabled-in-bios to
  tests-ignore-warnings
* Install the newly added ippfind utility and its manpage in
  cups-client
* Added pwg.h to libcups2-dev package
* Call dh_auto_clean only if the file Makedefs is present, to avoid a
  FTBFS
* Added color management extensions from Joe Simon's GSoC 2013
  project.
* Patch cups-files.conf to activate CUPS daemon syncing of files when
  closing, so that config files (like printers.conf) do not
  mysteriously disappear (LP: #1157972)
* In the AppArmor profile, allow execution of programs in
  /etc/cups/interfaces/, needed to make CUPS working with queues based
  on System V interface scripts, especially PPD-less queues
  auto-generated by cups-browsed from cups-filters 1.0.41 on.
* Silenced AppArmor noise from udev.conf in syslog (LP: #1229766)

[ Steve Langasek ]
* Add cups-filters (>= 1.0.42) as alternative to foomatic-filters
  (which is deprecated) in package relationships

[ Didier Raboud ]
* Remove Roger Leigh from uploaders on his request with thanks for his
  past work!
* Switch avahi LSB Should-Start dependency to be avahi-daemon; also
  bump package relationship to >= 0.6.31-3~ (Closes: #731608)
* Refresh the manpage translation files
* Move the USB backend quirk rules file to cups-server-common
* Add 38 new 1.7.0 libcups2 symbols
* Mark one C++ libcupsppdc1 symbol as optional as it isn't exported in
  1.7.0 anymore
* Import Fedora patches:
  - to avoid sign-extending CRCs in gz decompression
  - to build with full read-only relocations
  - to fix job history logging (upstream patch)
  - to set the internal default for SyncOnClose to Yes, instead of
    only configuring it to Yes
  - to fix a stringpool corruption issue
  - to prevent USB timeouts causing incorrect print output
* Import Fedora patch updates:
  - to dont-use-dbus-from-two-threads patch so it removes a call to
    avahi_threaded_poll_stop()
  - to avoid_stale_lockfile_in_dbus_notifier patch to call _exit when
    handling SIGTERM
* Move manpage-translations patch at the very end of the patch series
  to have it include all our patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * "$Id: job.c 11147 2013-07-17 02:54:31Z msweet $"
 
3
 *
 
4
 *   Job management routines for the CUPS scheduler.
 
5
 *
 
6
 *   Copyright 2007-2012 by Apple Inc.
 
7
 *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
 
8
 *
 
9
 *   These coded instructions, statements, and computer programs are the
 
10
 *   property of Apple Inc. and are protected by Federal copyright
 
11
 *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
 
12
 *   which should have been included with this file.  If this file is
 
13
 *   file is missing or damaged, see the license at "http://www.cups.org/".
 
14
 *
 
15
 * Contents:
 
16
 *
 
17
 *   cupsdAddJob()              - Add a new job to the job queue.
 
18
 *   cupsdCancelJobs()          - Cancel all jobs for the given
 
19
 *                                destination/user.
 
20
 *   cupsdCheckJobs()           - Check the pending jobs and start any if the
 
21
 *                                destination is available.
 
22
 *   cupsdCleanJobs()           - Clean out old jobs.
 
23
 *   cupsdContinueJob()         - Continue printing with the next file in a
 
24
 *                                job.
 
25
 *   cupsdDeleteJob()           - Free all memory used by a job.
 
26
 *   cupsdFreeAllJobs()         - Free all jobs from memory.
 
27
 *   cupsdFindJob()             - Find the specified job.
 
28
 *   cupsdGetPrinterJobCount()  - Get the number of pending, processing, or
 
29
 *                                held jobs in a printer or class.
 
30
 *   cupsdGetUserJobCount()     - Get the number of pending, processing, or
 
31
 *                                held jobs for a user.
 
32
 *   cupsdLoadAllJobs()         - Load all jobs from disk.
 
33
 *   cupsdLoadJob()             - Load a single job.
 
34
 *   cupsdMoveJob()             - Move the specified job to a different
 
35
 *                                destination.
 
36
 *   cupsdReleaseJob()          - Release the specified job.
 
37
 *   cupsdRestartJob()          - Restart the specified job.
 
38
 *   cupsdSaveAllJobs()         - Save a summary of all jobs to disk.
 
39
 *   cupsdSaveJob()             - Save a job to disk.
 
40
 *   cupsdSetJobHoldUntil()     - Set the hold time for a job.
 
41
 *   cupsdSetJobPriority()      - Set the priority of a job, moving it up/down
 
42
 *                                in the list as needed.
 
43
 *   cupsdSetJobState()         - Set the state of the specified print job.
 
44
 *   cupsdStopAllJobs()         - Stop all print jobs.
 
45
 *   cupsdUnloadCompletedJobs() - Flush completed job history from memory.
 
46
 *   cupsdUpdateJobs()          - Update the history/file files for all jobs.
 
47
 *   compare_active_jobs()      - Compare the job IDs and priorities of two
 
48
 *                                jobs.
 
49
 *   compare_jobs()             - Compare the job IDs of two jobs.
 
50
 *   dump_job_history()         - Dump any debug messages for a job.
 
51
 *   free_job_history()         - Free any log history.
 
52
 *   finalize_job()             - Cleanup after job filter processes and
 
53
 *                                support data.
 
54
 *   get_options()              - Get a string containing the job options.
 
55
 *   ipp_length()               - Compute the size of the buffer needed to hold
 
56
 *                                the textual IPP attributes.
 
57
 *   load_job_cache()           - Load jobs from the job.cache file.
 
58
 *   load_next_job_id()         - Load the NextJobId value from the job.cache
 
59
 *                                file.
 
60
 *   load_request_root()        - Load jobs from the RequestRoot directory.
 
61
 *   remove_job_files()         - Remove the document files for a job.
 
62
 *   remove_job_history()       - Remove the control file for a job.
 
63
 *   set_time()                 - Set one of the "time-at-xyz" attributes.
 
64
 *   start_job()                - Start a print job.
 
65
 *   stop_job()                 - Stop a print job.
 
66
 *   unload_job()               - Unload a job from memory.
 
67
 *   update_job()               - Read a status update from a job's filters.
 
68
 *   update_job_attrs()         - Update the job-printer-* attributes.
 
69
 */
 
70
 
 
71
/*
 
72
 * Include necessary headers...
 
73
 */
 
74
 
 
75
#include "cupsd.h"
 
76
#include <grp.h>
 
77
#include <cups/backend.h>
 
78
#include <cups/dir.h>
 
79
#ifdef __APPLE__
 
80
#  include <IOKit/pwr_mgt/IOPMLib.h>
 
81
#  ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
 
82
#    include <IOKit/pwr_mgt/IOPMLibPrivate.h>
 
83
#  endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
 
84
#endif /* __APPLE__ */
 
85
 
 
86
 
 
87
/*
 
88
 * Design Notes for Job Management
 
89
 * -------------------------------
 
90
 *
 
91
 * STATE CHANGES
 
92
 *
 
93
 *     pending       Do nothing/check jobs
 
94
 *     pending-held  Send SIGTERM to filters and backend
 
95
 *     processing    Do nothing/start job
 
96
 *     stopped       Send SIGKILL to filters and backend
 
97
 *     canceled      Send SIGTERM to filters and backend
 
98
 *     aborted       Finalize
 
99
 *     completed     Finalize
 
100
 *
 
101
 *     Finalize clears the printer <-> job association, deletes the status
 
102
 *     buffer, closes all of the pipes, etc. and doesn't get run until all of
 
103
 *     the print processes are finished.
 
104
 *
 
105
 * UNLOADING OF JOBS (cupsdUnloadCompletedJobs)
 
106
 *
 
107
 *     We unload the job attributes when they are not needed to reduce overall
 
108
 *     memory consumption.  We don't unload jobs where job->state_value <
 
109
 *     IPP_JOB_STOPPED, job->printer != NULL, or job->access_time is recent.
 
110
 *
 
111
 * STARTING OF JOBS (start_job)
 
112
 *
 
113
 *     When a job is started, a status buffer, several pipes, a security
 
114
 *     profile, and a backend process are created for the life of that job.
 
115
 *     These are shared for every file in a job.  For remote print jobs, the
 
116
 *     IPP backend is provided with every file in the job and no filters are
 
117
 *     run.
 
118
 *
 
119
 *     The job->printer member tracks which printer is printing a job, which
 
120
 *     can be different than the destination in job->dest for classes.  The
 
121
 *     printer object also has a job pointer to track which job is being
 
122
 *     printed.
 
123
 *
 
124
 * PRINTING OF JOB FILES (cupsdContinueJob)
 
125
 *
 
126
 *     Each file in a job is filtered by 0 or more programs.  After getting the
 
127
 *     list of filters needed and the total cost, the job is either passed or
 
128
 *     put back to the processing state until the current FilterLevel comes down
 
129
 *     enough to allow printing.
 
130
 *
 
131
 *     If we can print, we build a string for the print options and run each of
 
132
 *     the filters, piping the output from one into the next.
 
133
 *
 
134
 * JOB STATUS UPDATES (update_job)
 
135
 *
 
136
 *     The update_job function gets called whenever there are pending messages
 
137
 *     on the status pipe.  These generally are updates to the marker-*,
 
138
 *     printer-state-message, or printer-state-reasons attributes.  On EOF,
 
139
 *     finalize_job is called to clean up.
 
140
 *
 
141
 * FINALIZING JOBS (finalize_job)
 
142
 *
 
143
 *     When all filters and the backend are done, we set the job state to
 
144
 *     completed (no errors), aborted (filter errors or abort-job policy),
 
145
 *     pending-held (auth required or retry-job policy), or pending
 
146
 *     (retry-current-job or stop-printer policies) as appropriate.
 
147
 *
 
148
 *     Then we close the pipes and free the status buffers and profiles.
 
149
 *
 
150
 * JOB FILE COMPLETION (process_children in main.c)
 
151
 *
 
152
 *     For multiple-file jobs, process_children (in main.c) sees that all
 
153
 *     filters have exited and calls in to print the next file if there are
 
154
 *     more files in the job, otherwise it waits for the backend to exit and
 
155
 *     update_job to do the cleanup.
 
156
 */
 
157
 
 
158
 
 
159
/*
 
160
 * Local globals...
 
161
 */
 
162
 
 
163
static mime_filter_t    gziptoany_filter =
 
164
                        {
 
165
                          NULL,         /* Source type */
 
166
                          NULL,         /* Destination type */
 
167
                          0,            /* Cost */
 
168
                          "gziptoany"   /* Filter program to run */
 
169
                        };
 
170
 
 
171
 
 
172
/*
 
173
 * Local functions...
 
174
 */
 
175
 
 
176
static int      compare_active_jobs(void *first, void *second, void *data);
 
177
static int      compare_jobs(void *first, void *second, void *data);
 
178
static void     dump_job_history(cupsd_job_t *job);
 
179
static void     finalize_job(cupsd_job_t *job, int set_job_state);
 
180
static void     free_job_history(cupsd_job_t *job);
 
181
static char     *get_options(cupsd_job_t *job, int banner_page, char *copies,
 
182
                             size_t copies_size, char *title,
 
183
                             size_t title_size);
 
184
static size_t   ipp_length(ipp_t *ipp);
 
185
static void     load_job_cache(const char *filename);
 
186
static void     load_next_job_id(const char *filename);
 
187
static void     load_request_root(void);
 
188
static void     remove_job_files(cupsd_job_t *job);
 
189
static void     remove_job_history(cupsd_job_t *job);
 
190
static void     set_time(cupsd_job_t *job, const char *name);
 
191
static void     start_job(cupsd_job_t *job, cupsd_printer_t *printer);
 
192
static void     stop_job(cupsd_job_t *job, cupsd_jobaction_t action);
 
193
static void     unload_job(cupsd_job_t *job);
 
194
static void     update_job(cupsd_job_t *job);
 
195
static void     update_job_attrs(cupsd_job_t *job, int do_message);
 
196
 
 
197
 
 
198
/*
 
199
 * 'cupsdAddJob()' - Add a new job to the job queue.
 
200
 */
 
201
 
 
202
cupsd_job_t *                           /* O - New job record */
 
203
cupsdAddJob(int        priority,        /* I - Job priority */
 
204
            const char *dest)           /* I - Job destination */
 
205
{
 
206
  cupsd_job_t   *job;                   /* New job record */
 
207
 
 
208
 
 
209
  if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
 
210
    return (NULL);
 
211
 
 
212
  job->id              = NextJobId ++;
 
213
  job->priority        = priority;
 
214
  job->back_pipes[0]   = -1;
 
215
  job->back_pipes[1]   = -1;
 
216
  job->print_pipes[0]  = -1;
 
217
  job->print_pipes[1]  = -1;
 
218
  job->side_pipes[0]   = -1;
 
219
  job->side_pipes[1]   = -1;
 
220
  job->status_pipes[0] = -1;
 
221
  job->status_pipes[1] = -1;
 
222
 
 
223
  cupsdSetString(&job->dest, dest);
 
224
 
 
225
 /*
 
226
  * Add the new job to the "all jobs" and "active jobs" lists...
 
227
  */
 
228
 
 
229
  cupsArrayAdd(Jobs, job);
 
230
  cupsArrayAdd(ActiveJobs, job);
 
231
 
 
232
  return (job);
 
233
}
 
234
 
 
235
 
 
236
/*
 
237
 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user.
 
238
 */
 
239
 
 
240
void
 
241
cupsdCancelJobs(const char *dest,       /* I - Destination to cancel */
 
242
                const char *username,   /* I - Username or NULL */
 
243
                int        purge)       /* I - Purge jobs? */
 
244
{
 
245
  cupsd_job_t   *job;                   /* Current job */
 
246
 
 
247
 
 
248
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
249
       job;
 
250
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
251
  {
 
252
    if ((!job->dest || !job->username) && !cupsdLoadJob(job))
 
253
      continue;
 
254
 
 
255
    if ((!dest || !strcmp(job->dest, dest)) &&
 
256
        (!username || !strcmp(job->username, username)))
 
257
    {
 
258
     /*
 
259
      * Cancel all jobs matching this destination/user...
 
260
      */
 
261
 
 
262
      if (purge)
 
263
        cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE,
 
264
                         "Job purged by user.");
 
265
      else if (job->state_value < IPP_JOB_CANCELED)
 
266
        cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
 
267
                         "Job canceled by user.");
 
268
    }
 
269
  }
 
270
 
 
271
  cupsdCheckJobs();
 
272
}
 
273
 
 
274
 
 
275
/*
 
276
 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
 
277
 *                      is available.
 
278
 */
 
279
 
 
280
void
 
281
cupsdCheckJobs(void)
 
282
{
 
283
  cupsd_job_t           *job;           /* Current job in queue */
 
284
  cupsd_printer_t       *printer,       /* Printer destination */
 
285
                        *pclass;        /* Printer class destination */
 
286
  ipp_attribute_t       *attr;          /* Job attribute */
 
287
  time_t                curtime;        /* Current time */
 
288
 
 
289
 
 
290
  curtime = time(NULL);
 
291
 
 
292
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
293
                  "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d, "
 
294
                  "curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping,
 
295
                  NeedReload, (long)curtime);
 
296
 
 
297
  for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
 
298
       job;
 
299
       job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
 
300
  {
 
301
    cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
302
                    "cupsdCheckJobs: Job %d - dest=\"%s\", printer=%p, "
 
303
                    "state=%d, cancel_time=%ld, hold_until=%ld, kill_time=%ld, "
 
304
                    "pending_cost=%d, pending_timeout=%ld", job->id, job->dest,
 
305
                    job->printer, job->state_value, (long)job->cancel_time,
 
306
                    (long)job->hold_until, (long)job->kill_time,
 
307
                    job->pending_cost, (long)job->pending_timeout);
 
308
 
 
309
   /*
 
310
    * Kill jobs if they are unresponsive...
 
311
    */
 
312
 
 
313
    if (job->kill_time && job->kill_time <= curtime)
 
314
    {
 
315
      cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Stopping unresponsive job.",
 
316
                      job->id);
 
317
 
 
318
      stop_job(job, CUPSD_JOB_FORCE);
 
319
      continue;
 
320
    }
 
321
 
 
322
   /*
 
323
    * Cancel stuck jobs...
 
324
    */
 
325
 
 
326
    if (job->cancel_time && job->cancel_time <= curtime)
 
327
    {
 
328
      cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
 
329
                       "Canceling stuck job after %d seconds.", MaxJobTime);
 
330
      continue;
 
331
    }
 
332
 
 
333
   /*
 
334
    * Start held jobs if they are ready...
 
335
    */
 
336
 
 
337
    if (job->state_value == IPP_JOB_HELD &&
 
338
        job->hold_until &&
 
339
        job->hold_until < curtime)
 
340
    {
 
341
      if (job->pending_timeout)
 
342
      {
 
343
       /*
 
344
        * This job is pending; check that we don't have an active Send-Document
 
345
        * operation in progress on any of the client connections, then timeout
 
346
        * the job so we can start printing...
 
347
        */
 
348
 
 
349
        cupsd_client_t  *con;           /* Current client connection */
 
350
 
 
351
        for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
 
352
             con;
 
353
             con = (cupsd_client_t *)cupsArrayNext(Clients))
 
354
          if (con->request &&
 
355
              con->request->request.op.operation_id == IPP_SEND_DOCUMENT)
 
356
            break;
 
357
 
 
358
        if (con)
 
359
          continue;
 
360
 
 
361
        if (cupsdTimeoutJob(job))
 
362
          continue;
 
363
      }
 
364
 
 
365
      cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
 
366
                       "Job submission timed out.");
 
367
    }
 
368
 
 
369
   /*
 
370
    * Continue jobs that are waiting on the FilterLimit...
 
371
    */
 
372
 
 
373
    if (job->pending_cost > 0 &&
 
374
        ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
 
375
      cupsdContinueJob(job);
 
376
 
 
377
   /*
 
378
    * Start pending jobs if the destination is available...
 
379
    */
 
380
 
 
381
    if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
 
382
#ifndef kIOPMAssertionTypeDenySystemSleep
 
383
        !Sleeping &&
 
384
#endif /* !kIOPMAssertionTypeDenySystemSleep */
 
385
        !DoingShutdown && !job->printer)
 
386
    {
 
387
      printer = cupsdFindDest(job->dest);
 
388
      pclass  = NULL;
 
389
 
 
390
      while (printer && (printer->type & CUPS_PRINTER_CLASS))
 
391
      {
 
392
       /*
 
393
        * If the class is remote, just pass it to the remote server...
 
394
        */
 
395
 
 
396
        pclass = printer;
 
397
 
 
398
        if (pclass->state == IPP_PRINTER_STOPPED)
 
399
          printer = NULL;
 
400
        else if (pclass->type & CUPS_PRINTER_REMOTE)
 
401
          break;
 
402
        else
 
403
          printer = cupsdFindAvailablePrinter(printer->name);
 
404
      }
 
405
 
 
406
      if (!printer && !pclass)
 
407
      {
 
408
       /*
 
409
        * Whoa, the printer and/or class for this destination went away;
 
410
        * cancel the job...
 
411
        */
 
412
 
 
413
        cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
 
414
                         "Job aborted because the destination printer/class "
 
415
                         "has gone away.");
 
416
      }
 
417
      else if (printer && !printer->holding_new_jobs)
 
418
      {
 
419
       /*
 
420
        * See if the printer is available or remote and not printing a job;
 
421
        * if so, start the job...
 
422
        */
 
423
 
 
424
        if (pclass)
 
425
        {
 
426
         /*
 
427
          * Add/update a job-actual-printer-uri attribute for this job
 
428
          * so that we know which printer actually printed the job...
 
429
          */
 
430
 
 
431
          if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
 
432
                                       IPP_TAG_URI)) != NULL)
 
433
            cupsdSetString(&attr->values[0].string.text, printer->uri);
 
434
          else
 
435
            ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
 
436
                         "job-actual-printer-uri", NULL, printer->uri);
 
437
 
 
438
          job->dirty = 1;
 
439
          cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
440
        }
 
441
 
 
442
        if (!printer->job && printer->state == IPP_PRINTER_IDLE)
 
443
        {
 
444
         /*
 
445
          * Start the job...
 
446
          */
 
447
 
 
448
          start_job(job, printer);
 
449
        }
 
450
      }
 
451
    }
 
452
  }
 
453
}
 
454
 
 
455
 
 
456
/*
 
457
 * 'cupsdCleanJobs()' - Clean out old jobs.
 
458
 */
 
459
 
 
460
void
 
461
cupsdCleanJobs(void)
 
462
{
 
463
  cupsd_job_t   *job;                   /* Current job */
 
464
  time_t        curtime;                /* Current time */
 
465
 
 
466
 
 
467
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
468
                  "cupsdCleanJobs: MaxJobs=%d, JobHistory=%d, JobFiles=%d",
 
469
                  MaxJobs, JobHistory, JobFiles);
 
470
 
 
471
  if (MaxJobs <= 0 && JobHistory == INT_MAX && JobFiles == INT_MAX)
 
472
    return;
 
473
 
 
474
  curtime          = time(NULL);
 
475
  JobHistoryUpdate = 0;
 
476
 
 
477
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
478
       job;
 
479
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
480
  {
 
481
    if (job->state_value >= IPP_JOB_CANCELED && !job->printer)
 
482
    {
 
483
     /*
 
484
      * Expire old jobs (or job files)...
 
485
      */
 
486
 
 
487
      if ((MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs) ||
 
488
          (job->history_time && job->history_time <= curtime))
 
489
      {
 
490
        cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing from history.");
 
491
        cupsdDeleteJob(job, CUPSD_JOB_PURGE);
 
492
      }
 
493
      else if (job->file_time && job->file_time <= curtime)
 
494
      {
 
495
        cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing document files.");
 
496
        remove_job_files(job);
 
497
 
 
498
        if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
 
499
          JobHistoryUpdate = job->history_time;
 
500
      }
 
501
      else
 
502
      {
 
503
        if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
 
504
          JobHistoryUpdate = job->history_time;
 
505
 
 
506
        if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
 
507
          JobHistoryUpdate = job->file_time;
 
508
      }
 
509
    }
 
510
  }
 
511
 
 
512
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCleanJobs: JobHistoryUpdate=%ld",
 
513
                  (long)JobHistoryUpdate);
 
514
}
 
515
 
 
516
 
 
517
/*
 
518
 * 'cupsdContinueJob()' - Continue printing with the next file in a job.
 
519
 */
 
520
 
 
521
void
 
522
cupsdContinueJob(cupsd_job_t *job)      /* I - Job */
 
523
{
 
524
  int                   i;              /* Looping var */
 
525
  int                   slot;           /* Pipe slot */
 
526
  cups_array_t          *filters = NULL,/* Filters for job */
 
527
                        *prefilters;    /* Filters with prefilters */
 
528
  mime_filter_t         *filter,        /* Current filter */
 
529
                        *prefilter,     /* Prefilter */
 
530
                        port_monitor;   /* Port monitor filter */
 
531
  char                  scheme[255];    /* Device URI scheme */
 
532
  ipp_attribute_t       *attr;          /* Current attribute */
 
533
  const char            *ptr,           /* Pointer into value */
 
534
                        *abort_message; /* Abort message */
 
535
  ipp_jstate_t          abort_state = IPP_JOB_STOPPED;
 
536
                                        /* New job state on abort */
 
537
  struct stat           backinfo;       /* Backend file information */
 
538
  int                   backroot;       /* Run backend as root? */
 
539
  int                   pid;            /* Process ID of new filter process */
 
540
  int                   banner_page;    /* 1 if banner page, 0 otherwise */
 
541
  int                   filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
 
542
                                        /* Pipes used between filters */
 
543
  int                   envc;           /* Number of environment variables */
 
544
  struct stat           fileinfo;       /* Job file information */
 
545
  char                  **argv = NULL,  /* Filter command-line arguments */
 
546
                        filename[1024], /* Job filename */
 
547
                        command[1024],  /* Full path to command */
 
548
                        jobid[255],     /* Job ID string */
 
549
                        title[IPP_MAX_NAME],
 
550
                                        /* Job title string */
 
551
                        copies[255],    /* # copies string */
 
552
                        *options,       /* Options string */
 
553
                        *envp[MAX_ENV + 21],
 
554
                                        /* Environment variables */
 
555
                        charset[255],   /* CHARSET env variable */
 
556
                        class_name[255],/* CLASS env variable */
 
557
                        classification[1024],
 
558
                                        /* CLASSIFICATION env variable */
 
559
                        content_type[1024],
 
560
                                        /* CONTENT_TYPE env variable */
 
561
                        device_uri[1024],
 
562
                                        /* DEVICE_URI env variable */
 
563
                        final_content_type[1024] = "",
 
564
                                        /* FINAL_CONTENT_TYPE env variable */
 
565
                        lang[255],      /* LANG env variable */
 
566
#ifdef __APPLE__
 
567
                        apple_language[255],
 
568
                                        /* APPLE_LANGUAGE env variable */
 
569
#endif /* __APPLE__ */
 
570
                        auth_info_required[255],
 
571
                                        /* AUTH_INFO_REQUIRED env variable */
 
572
                        ppd[1024],      /* PPD env variable */
 
573
                        printer_info[255],
 
574
                                        /* PRINTER_INFO env variable */
 
575
                        printer_location[255],
 
576
                                        /* PRINTER_LOCATION env variable */
 
577
                        printer_name[255],
 
578
                                        /* PRINTER env variable */
 
579
                        *printer_state_reasons = NULL,
 
580
                                        /* PRINTER_STATE_REASONS env var */
 
581
                        rip_max_cache[255];
 
582
                                        /* RIP_MAX_CACHE env variable */
 
583
 
 
584
 
 
585
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
586
                  "cupsdContinueJob(job=%p(%d)): current_file=%d, num_files=%d",
 
587
                  job, job->id, job->current_file, job->num_files);
 
588
 
 
589
 /*
 
590
  * Figure out what filters are required to convert from
 
591
  * the source to the destination type...
 
592
  */
 
593
 
 
594
  FilterLevel -= job->cost;
 
595
 
 
596
  job->cost         = 0;
 
597
  job->pending_cost = 0;
 
598
 
 
599
  memset(job->filters, 0, sizeof(job->filters));
 
600
 
 
601
  if (job->printer->raw)
 
602
  {
 
603
   /*
 
604
    * Remote jobs and raw queues go directly to the printer without
 
605
    * filtering...
 
606
    */
 
607
 
 
608
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
 
609
  }
 
610
  else
 
611
  {
 
612
   /*
 
613
    * Local jobs get filtered...
 
614
    */
 
615
 
 
616
    snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
 
617
             job->id, job->current_file + 1);
 
618
    if (stat(filename, &fileinfo))
 
619
      fileinfo.st_size = 0;
 
620
 
 
621
    filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file],
 
622
                          fileinfo.st_size, job->printer->filetype,
 
623
                          &(job->cost));
 
624
 
 
625
    if (!filters)
 
626
    {
 
627
      cupsdLogJob(job, CUPSD_LOG_ERROR,
 
628
                  "Unable to convert file %d to printable format.",
 
629
                  job->current_file);
 
630
 
 
631
      abort_message = "Aborting job because it cannot be printed.";
 
632
      abort_state   = IPP_JOB_ABORTED;
 
633
 
 
634
      ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
 
635
      goto abort_job;
 
636
    }
 
637
 
 
638
   /*
 
639
    * Figure out the final content type...
 
640
    */
 
641
 
 
642
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "%d filters for job:",
 
643
                cupsArrayCount(filters));
 
644
    for (filter = (mime_filter_t *)cupsArrayFirst(filters);
 
645
         filter;
 
646
         filter = (mime_filter_t *)cupsArrayNext(filters))
 
647
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s (%s/%s to %s/%s, cost %d)",
 
648
                  filter->filter,
 
649
                  filter->src ? filter->src->super : "???",
 
650
                  filter->src ? filter->src->type : "???",
 
651
                  filter->dst ? filter->dst->super : "???",
 
652
                  filter->dst ? filter->dst->type : "???",
 
653
                  filter->cost);
 
654
 
 
655
    if (!job->printer->remote)
 
656
    {
 
657
      for (filter = (mime_filter_t *)cupsArrayLast(filters);
 
658
           filter && filter->dst;
 
659
           filter = (mime_filter_t *)cupsArrayPrev(filters))
 
660
        if (strcmp(filter->dst->super, "printer") ||
 
661
            strcmp(filter->dst->type, job->printer->name))
 
662
          break;
 
663
 
 
664
      if (filter && filter->dst)
 
665
      {
 
666
        if ((ptr = strchr(filter->dst->type, '/')) != NULL)
 
667
          snprintf(final_content_type, sizeof(final_content_type),
 
668
                   "FINAL_CONTENT_TYPE=%s", ptr + 1);
 
669
        else
 
670
          snprintf(final_content_type, sizeof(final_content_type),
 
671
                   "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
 
672
                   filter->dst->type);
 
673
      }
 
674
      else
 
675
        snprintf(final_content_type, sizeof(final_content_type),
 
676
                 "FINAL_CONTENT_TYPE=printer/%s", job->printer->name);
 
677
    }
 
678
 
 
679
   /*
 
680
    * Remove NULL ("-") filters...
 
681
    */
 
682
 
 
683
    for (filter = (mime_filter_t *)cupsArrayFirst(filters);
 
684
         filter;
 
685
         filter = (mime_filter_t *)cupsArrayNext(filters))
 
686
      if (!strcmp(filter->filter, "-"))
 
687
        cupsArrayRemove(filters, filter);
 
688
 
 
689
    if (cupsArrayCount(filters) == 0)
 
690
    {
 
691
      cupsArrayDelete(filters);
 
692
      filters = NULL;
 
693
    }
 
694
 
 
695
   /*
 
696
    * If this printer has any pre-filters, insert the required pre-filter
 
697
    * in the filters array...
 
698
    */
 
699
 
 
700
    if (job->printer->prefiltertype && filters)
 
701
    {
 
702
      prefilters = cupsArrayNew(NULL, NULL);
 
703
 
 
704
      for (filter = (mime_filter_t *)cupsArrayFirst(filters);
 
705
           filter;
 
706
           filter = (mime_filter_t *)cupsArrayNext(filters))
 
707
      {
 
708
        if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src,
 
709
                                          job->printer->prefiltertype)))
 
710
        {
 
711
          cupsArrayAdd(prefilters, prefilter);
 
712
          job->cost += prefilter->cost;
 
713
        }
 
714
 
 
715
        cupsArrayAdd(prefilters, filter);
 
716
      }
 
717
 
 
718
      cupsArrayDelete(filters);
 
719
      filters = prefilters;
 
720
    }
 
721
  }
 
722
 
 
723
 /*
 
724
  * Set a minimum cost of 100 for all jobs so that FilterLimit
 
725
  * works with raw queues and other low-cost paths.
 
726
  */
 
727
 
 
728
  if (job->cost < 100)
 
729
    job->cost = 100;
 
730
 
 
731
 /*
 
732
  * See if the filter cost is too high...
 
733
  */
 
734
 
 
735
  if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
 
736
      FilterLimit > 0)
 
737
  {
 
738
   /*
 
739
    * Don't print this job quite yet...
 
740
    */
 
741
 
 
742
    cupsArrayDelete(filters);
 
743
 
 
744
    cupsdLogJob(job, CUPSD_LOG_INFO,
 
745
                "Holding because filter limit has been reached.");
 
746
    cupsdLogJob(job, CUPSD_LOG_DEBUG2,
 
747
                "cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
 
748
                job->current_file, job->cost, FilterLevel,
 
749
                FilterLimit);
 
750
 
 
751
    job->pending_cost = job->cost;
 
752
    job->cost         = 0;
 
753
    return;
 
754
  }
 
755
 
 
756
  FilterLevel += job->cost;
 
757
 
 
758
 /*
 
759
  * Add decompression/raw filter as needed...
 
760
  */
 
761
 
 
762
  if ((!job->printer->raw && job->compressions[job->current_file]) ||
 
763
      (!filters && !job->printer->remote &&
 
764
       (job->num_files > 1 || !strncmp(job->printer->device_uri, "file:", 5))))
 
765
  {
 
766
   /*
 
767
    * Add gziptoany filter to the front of the list...
 
768
    */
 
769
 
 
770
    if (!filters)
 
771
      filters = cupsArrayNew(NULL, NULL);
 
772
 
 
773
    if (!cupsArrayInsert(filters, &gziptoany_filter))
 
774
    {
 
775
      cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
776
                  "Unable to add decompression filter - %s", strerror(errno));
 
777
 
 
778
      cupsArrayDelete(filters);
 
779
 
 
780
      abort_message = "Stopping job because the scheduler ran out of memory.";
 
781
 
 
782
      goto abort_job;
 
783
    }
 
784
  }
 
785
 
 
786
 /*
 
787
  * Add port monitor, if any...
 
788
  */
 
789
 
 
790
  if (job->printer->port_monitor)
 
791
  {
 
792
   /*
 
793
    * Add port monitor to the end of the list...
 
794
    */
 
795
 
 
796
    if (!filters)
 
797
      filters = cupsArrayNew(NULL, NULL);
 
798
 
 
799
    port_monitor.src  = NULL;
 
800
    port_monitor.dst  = NULL;
 
801
    port_monitor.cost = 0;
 
802
 
 
803
    snprintf(port_monitor.filter, sizeof(port_monitor.filter),
 
804
             "%s/monitor/%s", ServerBin, job->printer->port_monitor);
 
805
 
 
806
    if (!cupsArrayAdd(filters, &port_monitor))
 
807
    {
 
808
      cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
809
                  "Unable to add port monitor - %s", strerror(errno));
 
810
 
 
811
      abort_message = "Stopping job because the scheduler ran out of memory.";
 
812
 
 
813
      goto abort_job;
 
814
    }
 
815
  }
 
816
 
 
817
 /*
 
818
  * Make sure we don't go over the "MAX_FILTERS" limit...
 
819
  */
 
820
 
 
821
  if (cupsArrayCount(filters) > MAX_FILTERS)
 
822
  {
 
823
    cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
824
                "Too many filters (%d > %d), unable to print.",
 
825
                cupsArrayCount(filters), MAX_FILTERS);
 
826
 
 
827
    abort_message = "Aborting job because it needs too many filters to print.";
 
828
    abort_state   = IPP_JOB_ABORTED;
 
829
 
 
830
    ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
 
831
 
 
832
    goto abort_job;
 
833
  }
 
834
 
 
835
 /*
 
836
  * Determine if we are printing a banner page or not...
 
837
  */
 
838
 
 
839
  if (job->job_sheets == NULL)
 
840
  {
 
841
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "No job-sheets attribute.");
 
842
    if ((job->job_sheets =
 
843
         ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
 
844
      cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
845
                  "... but someone added one without setting job_sheets.");
 
846
  }
 
847
  else if (job->job_sheets->num_values == 1)
 
848
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s",
 
849
                job->job_sheets->values[0].string.text);
 
850
  else
 
851
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
 
852
                job->job_sheets->values[0].string.text,
 
853
                job->job_sheets->values[1].string.text);
 
854
 
 
855
  if (job->printer->type & CUPS_PRINTER_REMOTE)
 
856
    banner_page = 0;
 
857
  else if (job->job_sheets == NULL)
 
858
    banner_page = 0;
 
859
  else if (_cups_strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
 
860
           job->current_file == 0)
 
861
    banner_page = 1;
 
862
  else if (job->job_sheets->num_values > 1 &&
 
863
           _cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
 
864
           job->current_file == (job->num_files - 1))
 
865
    banner_page = 1;
 
866
  else
 
867
    banner_page = 0;
 
868
 
 
869
  if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
 
870
                             sizeof(title))) == NULL)
 
871
  {
 
872
    abort_message = "Stopping job because the scheduler ran out of memory.";
 
873
 
 
874
    goto abort_job;
 
875
  }
 
876
 
 
877
 /*
 
878
  * Build the command-line arguments for the filters.  Each filter
 
879
  * has 6 or 7 arguments:
 
880
  *
 
881
  *     argv[0] = printer
 
882
  *     argv[1] = job ID
 
883
  *     argv[2] = username
 
884
  *     argv[3] = title
 
885
  *     argv[4] = # copies
 
886
  *     argv[5] = options
 
887
  *     argv[6] = filename (optional; normally stdin)
 
888
  *
 
889
  * This allows legacy printer drivers that use the old System V
 
890
  * printing interface to be used by CUPS.
 
891
  *
 
892
  * For remote jobs, we send all of the files in the argument list.
 
893
  */
 
894
 
 
895
  if (job->printer->remote)
 
896
    argv = calloc(7 + job->num_files, sizeof(char *));
 
897
  else
 
898
    argv = calloc(8, sizeof(char *));
 
899
 
 
900
  if (!argv)
 
901
  {
 
902
    cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
 
903
                    strerror(errno));
 
904
 
 
905
    abort_message = "Stopping job because the scheduler ran out of memory.";
 
906
 
 
907
    goto abort_job;
 
908
  }
 
909
 
 
910
  sprintf(jobid, "%d", job->id);
 
911
 
 
912
  argv[0] = job->printer->name;
 
913
  argv[1] = jobid;
 
914
  argv[2] = job->username;
 
915
  argv[3] = title;
 
916
  argv[4] = copies;
 
917
  argv[5] = options;
 
918
 
 
919
  if (job->printer->remote && job->num_files > 1)
 
920
  {
 
921
    for (i = 0; i < job->num_files; i ++)
 
922
    {
 
923
      snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
 
924
               job->id, i + 1);
 
925
      argv[6 + i] = strdup(filename);
 
926
    }
 
927
  }
 
928
  else
 
929
  {
 
930
    snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
 
931
             job->id, job->current_file + 1);
 
932
    argv[6] = filename;
 
933
  }
 
934
 
 
935
  for (i = 0; argv[i]; i ++)
 
936
    cupsdLogJob(job, CUPSD_LOG_DEBUG, "argv[%d]=\"%s\"", i, argv[i]);
 
937
 
 
938
 /*
 
939
  * Create environment variable strings for the filters...
 
940
  */
 
941
 
 
942
  attr = ippFindAttribute(job->attrs, "attributes-natural-language",
 
943
                          IPP_TAG_LANGUAGE);
 
944
 
 
945
#ifdef __APPLE__
 
946
  strlcpy(apple_language, "APPLE_LANGUAGE=", sizeof(apple_language));
 
947
  _cupsAppleLanguage(attr->values[0].string.text,
 
948
                     apple_language + 15, sizeof(apple_language) - 15);
 
949
#endif /* __APPLE__ */
 
950
 
 
951
  switch (strlen(attr->values[0].string.text))
 
952
  {
 
953
    default :
 
954
       /*
 
955
        * This is an unknown or badly formatted language code; use
 
956
        * the POSIX locale...
 
957
        */
 
958
 
 
959
        strlcpy(lang, "LANG=C", sizeof(lang));
 
960
        break;
 
961
 
 
962
    case 2 :
 
963
       /*
 
964
        * Just the language code (ll)...
 
965
        */
 
966
 
 
967
        snprintf(lang, sizeof(lang), "LANG=%s.UTF-8",
 
968
                 attr->values[0].string.text);
 
969
        break;
 
970
 
 
971
    case 5 :
 
972
       /*
 
973
        * Language and country code (ll-cc)...
 
974
        */
 
975
 
 
976
        snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF-8",
 
977
                 attr->values[0].string.text[0],
 
978
                 attr->values[0].string.text[1],
 
979
                 toupper(attr->values[0].string.text[3] & 255),
 
980
                 toupper(attr->values[0].string.text[4] & 255));
 
981
        break;
 
982
  }
 
983
 
 
984
  if ((attr = ippFindAttribute(job->attrs, "document-format",
 
985
                               IPP_TAG_MIMETYPE)) != NULL &&
 
986
      (ptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
 
987
    snprintf(charset, sizeof(charset), "CHARSET=%s", ptr + 8);
 
988
  else
 
989
    strlcpy(charset, "CHARSET=utf-8", sizeof(charset));
 
990
 
 
991
  snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
 
992
           job->filetypes[job->current_file]->super,
 
993
           job->filetypes[job->current_file]->type);
 
994
  snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
 
995
           job->printer->device_uri);
 
996
  snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot,
 
997
           job->printer->name);
 
998
  snprintf(printer_info, sizeof(printer_name), "PRINTER_INFO=%s",
 
999
           job->printer->info ? job->printer->info : "");
 
1000
  snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
 
1001
           job->printer->location ? job->printer->location : "");
 
1002
  snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
 
1003
  if (job->printer->num_reasons > 0)
 
1004
  {
 
1005
    char        *psrptr;                /* Pointer into PRINTER_STATE_REASONS */
 
1006
    size_t      psrlen;                 /* Size of PRINTER_STATE_REASONS */
 
1007
 
 
1008
    for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++)
 
1009
      psrlen += strlen(job->printer->reasons[i]) + 1;
 
1010
 
 
1011
    if ((printer_state_reasons = malloc(psrlen)) != NULL)
 
1012
    {
 
1013
     /*
 
1014
      * All of these strcpy's are safe because we allocated the psr string...
 
1015
      */
 
1016
 
 
1017
      strlcpy(printer_state_reasons, "PRINTER_STATE_REASONS=", psrlen);
 
1018
      for (psrptr = printer_state_reasons + 22, i = 0;
 
1019
           i < job->printer->num_reasons;
 
1020
           i ++)
 
1021
      {
 
1022
        if (i)
 
1023
          *psrptr++ = ',';
 
1024
        strlcpy(psrptr, job->printer->reasons[i],
 
1025
                psrlen - (psrptr - printer_state_reasons));
 
1026
        psrptr += strlen(psrptr);
 
1027
      }
 
1028
    }
 
1029
  }
 
1030
  snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
 
1031
 
 
1032
  if (job->printer->num_auth_info_required == 1)
 
1033
    snprintf(auth_info_required, sizeof(auth_info_required),
 
1034
             "AUTH_INFO_REQUIRED=%s",
 
1035
             job->printer->auth_info_required[0]);
 
1036
  else if (job->printer->num_auth_info_required == 2)
 
1037
    snprintf(auth_info_required, sizeof(auth_info_required),
 
1038
             "AUTH_INFO_REQUIRED=%s,%s",
 
1039
             job->printer->auth_info_required[0],
 
1040
             job->printer->auth_info_required[1]);
 
1041
  else if (job->printer->num_auth_info_required == 3)
 
1042
    snprintf(auth_info_required, sizeof(auth_info_required),
 
1043
             "AUTH_INFO_REQUIRED=%s,%s,%s",
 
1044
             job->printer->auth_info_required[0],
 
1045
             job->printer->auth_info_required[1],
 
1046
             job->printer->auth_info_required[2]);
 
1047
  else if (job->printer->num_auth_info_required == 4)
 
1048
    snprintf(auth_info_required, sizeof(auth_info_required),
 
1049
             "AUTH_INFO_REQUIRED=%s,%s,%s,%s",
 
1050
             job->printer->auth_info_required[0],
 
1051
             job->printer->auth_info_required[1],
 
1052
             job->printer->auth_info_required[2],
 
1053
             job->printer->auth_info_required[3]);
 
1054
  else
 
1055
    strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
 
1056
            sizeof(auth_info_required));
 
1057
 
 
1058
  envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
 
1059
 
 
1060
  envp[envc ++] = charset;
 
1061
  envp[envc ++] = lang;
 
1062
#ifdef __APPLE__
 
1063
  envp[envc ++] = apple_language;
 
1064
#endif /* __APPLE__ */
 
1065
  envp[envc ++] = ppd;
 
1066
  envp[envc ++] = rip_max_cache;
 
1067
  envp[envc ++] = content_type;
 
1068
  envp[envc ++] = device_uri;
 
1069
  envp[envc ++] = printer_info;
 
1070
  envp[envc ++] = printer_location;
 
1071
  envp[envc ++] = printer_name;
 
1072
  envp[envc ++] = printer_state_reasons ? printer_state_reasons :
 
1073
                                          "PRINTER_STATE_REASONS=none";
 
1074
  envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
 
1075
                                "CUPS_FILETYPE=document";
 
1076
 
 
1077
  if (final_content_type[0])
 
1078
    envp[envc ++] = final_content_type;
 
1079
 
 
1080
  if (Classification && !banner_page)
 
1081
  {
 
1082
    if ((attr = ippFindAttribute(job->attrs, "job-sheets",
 
1083
                                 IPP_TAG_NAME)) == NULL)
 
1084
      snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
 
1085
               Classification);
 
1086
    else if (attr->num_values > 1 &&
 
1087
             strcmp(attr->values[1].string.text, "none") != 0)
 
1088
      snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
 
1089
               attr->values[1].string.text);
 
1090
    else
 
1091
      snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
 
1092
               attr->values[0].string.text);
 
1093
 
 
1094
    envp[envc ++] = classification;
 
1095
  }
 
1096
 
 
1097
  if (job->dtype & CUPS_PRINTER_CLASS)
 
1098
  {
 
1099
    snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
 
1100
    envp[envc ++] = class_name;
 
1101
  }
 
1102
 
 
1103
  envp[envc ++] = auth_info_required;
 
1104
 
 
1105
  for (i = 0;
 
1106
       i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
 
1107
       i ++)
 
1108
    if (job->auth_env[i])
 
1109
      envp[envc ++] = job->auth_env[i];
 
1110
    else
 
1111
      break;
 
1112
 
 
1113
  if (job->auth_uid)
 
1114
    envp[envc ++] = job->auth_uid;
 
1115
 
 
1116
  envp[envc] = NULL;
 
1117
 
 
1118
  for (i = 0; i < envc; i ++)
 
1119
    if (!strncmp(envp[i], "AUTH_", 5))
 
1120
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"AUTH_%c****\"", i,
 
1121
                  envp[i][5]);
 
1122
    else if (strncmp(envp[i], "DEVICE_URI=", 11))
 
1123
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"%s\"", i, envp[i]);
 
1124
    else
 
1125
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"DEVICE_URI=%s\"", i,
 
1126
                  job->printer->sanitized_device_uri);
 
1127
 
 
1128
  if (job->printer->remote)
 
1129
    job->current_file = job->num_files;
 
1130
  else
 
1131
    job->current_file ++;
 
1132
 
 
1133
 /*
 
1134
  * Now create processes for all of the filters...
 
1135
  */
 
1136
 
 
1137
  for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
 
1138
       filter;
 
1139
       i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
 
1140
  {
 
1141
    if (filter->filter[0] != '/')
 
1142
      snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
 
1143
               filter->filter);
 
1144
    else
 
1145
      strlcpy(command, filter->filter, sizeof(command));
 
1146
 
 
1147
    if (i < (cupsArrayCount(filters) - 1))
 
1148
    {
 
1149
      if (cupsdOpenPipe(filterfds[slot]))
 
1150
      {
 
1151
        abort_message = "Stopping job because the scheduler could not create "
 
1152
                        "the filter pipes.";
 
1153
 
 
1154
        goto abort_job;
 
1155
      }
 
1156
    }
 
1157
    else
 
1158
    {
 
1159
      if (job->current_file == 1 ||
 
1160
          (job->printer->pc && job->printer->pc->single_file))
 
1161
      {
 
1162
        if (strncmp(job->printer->device_uri, "file:", 5) != 0)
 
1163
        {
 
1164
          if (cupsdOpenPipe(job->print_pipes))
 
1165
          {
 
1166
            abort_message = "Stopping job because the scheduler could not "
 
1167
                            "create the backend pipes.";
 
1168
 
 
1169
            goto abort_job;
 
1170
          }
 
1171
        }
 
1172
        else
 
1173
        {
 
1174
          job->print_pipes[0] = -1;
 
1175
          if (!strcmp(job->printer->device_uri, "file:/dev/null") ||
 
1176
              !strcmp(job->printer->device_uri, "file:///dev/null"))
 
1177
            job->print_pipes[1] = -1;
 
1178
          else
 
1179
          {
 
1180
            if (!strncmp(job->printer->device_uri, "file:/dev/", 10))
 
1181
              job->print_pipes[1] = open(job->printer->device_uri + 5,
 
1182
                                         O_WRONLY | O_EXCL);
 
1183
            else if (!strncmp(job->printer->device_uri, "file:///dev/", 12))
 
1184
              job->print_pipes[1] = open(job->printer->device_uri + 7,
 
1185
                                         O_WRONLY | O_EXCL);
 
1186
            else if (!strncmp(job->printer->device_uri, "file:///", 8))
 
1187
              job->print_pipes[1] = open(job->printer->device_uri + 7,
 
1188
                                         O_WRONLY | O_CREAT | O_TRUNC, 0600);
 
1189
            else
 
1190
              job->print_pipes[1] = open(job->printer->device_uri + 5,
 
1191
                                         O_WRONLY | O_CREAT | O_TRUNC, 0600);
 
1192
 
 
1193
            if (job->print_pipes[1] < 0)
 
1194
            {
 
1195
              abort_message = "Stopping job because the scheduler could not "
 
1196
                              "open the output file.";
 
1197
 
 
1198
              goto abort_job;
 
1199
            }
 
1200
 
 
1201
            fcntl(job->print_pipes[1], F_SETFD,
 
1202
                  fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
 
1203
          }
 
1204
        }
 
1205
      }
 
1206
 
 
1207
      filterfds[slot][0] = job->print_pipes[0];
 
1208
      filterfds[slot][1] = job->print_pipes[1];
 
1209
    }
 
1210
 
 
1211
    pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
 
1212
                            filterfds[slot][1], job->status_pipes[1],
 
1213
                            job->back_pipes[0], job->side_pipes[0], 0,
 
1214
                            job->profile, job, job->filters + i);
 
1215
 
 
1216
    cupsdClosePipe(filterfds[!slot]);
 
1217
 
 
1218
    if (pid == 0)
 
1219
    {
 
1220
      cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
 
1221
                  filter->filter, strerror(errno));
 
1222
 
 
1223
      abort_message = "Stopping job because the scheduler could not execute a "
 
1224
                      "filter.";
 
1225
 
 
1226
      goto abort_job;
 
1227
    }
 
1228
 
 
1229
    cupsdLogJob(job, CUPSD_LOG_INFO, "Started filter %s (PID %d)", command,
 
1230
                pid);
 
1231
 
 
1232
    argv[6] = NULL;
 
1233
    slot    = !slot;
 
1234
  }
 
1235
 
 
1236
  cupsArrayDelete(filters);
 
1237
  filters = NULL;
 
1238
 
 
1239
 /*
 
1240
  * Finally, pipe the final output into a backend process if needed...
 
1241
  */
 
1242
 
 
1243
  if (strncmp(job->printer->device_uri, "file:", 5) != 0)
 
1244
  {
 
1245
    if (job->current_file == 1 || job->printer->remote ||
 
1246
        (job->printer->pc && job->printer->pc->single_file))
 
1247
    {
 
1248
      sscanf(job->printer->device_uri, "%254[^:]", scheme);
 
1249
      snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme);
 
1250
 
 
1251
     /*
 
1252
      * See if the backend needs to run as root...
 
1253
      */
 
1254
 
 
1255
      if (RunUser)
 
1256
        backroot = 0;
 
1257
      else if (stat(command, &backinfo))
 
1258
        backroot = 0;
 
1259
      else
 
1260
        backroot = !(backinfo.st_mode & (S_IWGRP | S_IXGRP | S_IWOTH | S_IXOTH));
 
1261
 
 
1262
      argv[0] = job->printer->sanitized_device_uri;
 
1263
 
 
1264
      filterfds[slot][0] = -1;
 
1265
      filterfds[slot][1] = -1;
 
1266
 
 
1267
      pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
 
1268
                              filterfds[slot][1], job->status_pipes[1],
 
1269
                              job->back_pipes[1], job->side_pipes[1],
 
1270
                              backroot, job->profile, job, &(job->backend));
 
1271
 
 
1272
      if (pid == 0)
 
1273
      {
 
1274
        abort_message = "Stopping job because the sheduler could not execute "
 
1275
                        "the backend.";
 
1276
 
 
1277
        goto abort_job;
 
1278
      }
 
1279
      else
 
1280
      {
 
1281
        cupsdLogJob(job, CUPSD_LOG_INFO, "Started backend %s (PID %d)",
 
1282
                    command, pid);
 
1283
      }
 
1284
    }
 
1285
 
 
1286
    if (job->current_file == job->num_files ||
 
1287
        (job->printer->pc && job->printer->pc->single_file))
 
1288
      cupsdClosePipe(job->print_pipes);
 
1289
 
 
1290
    if (job->current_file == job->num_files)
 
1291
    {
 
1292
      cupsdClosePipe(job->back_pipes);
 
1293
      cupsdClosePipe(job->side_pipes);
 
1294
 
 
1295
      close(job->status_pipes[1]);
 
1296
      job->status_pipes[1] = -1;
 
1297
    }
 
1298
  }
 
1299
  else
 
1300
  {
 
1301
    filterfds[slot][0] = -1;
 
1302
    filterfds[slot][1] = -1;
 
1303
 
 
1304
    if (job->current_file == job->num_files ||
 
1305
        (job->printer->pc && job->printer->pc->single_file))
 
1306
      cupsdClosePipe(job->print_pipes);
 
1307
 
 
1308
    if (job->current_file == job->num_files)
 
1309
    {
 
1310
      close(job->status_pipes[1]);
 
1311
      job->status_pipes[1] = -1;
 
1312
    }
 
1313
  }
 
1314
 
 
1315
  cupsdClosePipe(filterfds[slot]);
 
1316
 
 
1317
  if (job->printer->remote && job->num_files > 1)
 
1318
  {
 
1319
    for (i = 0; i < job->num_files; i ++)
 
1320
      free(argv[i + 6]);
 
1321
  }
 
1322
 
 
1323
  free(argv);
 
1324
  if (printer_state_reasons)
 
1325
    free(printer_state_reasons);
 
1326
 
 
1327
  cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
 
1328
                 job);
 
1329
 
 
1330
  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
 
1331
                job->id);
 
1332
 
 
1333
  return;
 
1334
 
 
1335
 
 
1336
 /*
 
1337
  * If we get here, we need to abort the current job and close out all
 
1338
  * files and pipes...
 
1339
  */
 
1340
 
 
1341
  abort_job:
 
1342
 
 
1343
  FilterLevel -= job->cost;
 
1344
  job->cost = 0;
 
1345
 
 
1346
  for (slot = 0; slot < 2; slot ++)
 
1347
    cupsdClosePipe(filterfds[slot]);
 
1348
 
 
1349
  cupsArrayDelete(filters);
 
1350
 
 
1351
  if (argv)
 
1352
  {
 
1353
    if (job->printer->remote && job->num_files > 1)
 
1354
    {
 
1355
      for (i = 0; i < job->num_files; i ++)
 
1356
        free(argv[i + 6]);
 
1357
    }
 
1358
 
 
1359
    free(argv);
 
1360
  }
 
1361
 
 
1362
  if (printer_state_reasons)
 
1363
    free(printer_state_reasons);
 
1364
 
 
1365
  cupsdClosePipe(job->print_pipes);
 
1366
  cupsdClosePipe(job->back_pipes);
 
1367
  cupsdClosePipe(job->side_pipes);
 
1368
 
 
1369
  cupsdRemoveSelect(job->status_pipes[0]);
 
1370
  cupsdClosePipe(job->status_pipes);
 
1371
  cupsdStatBufDelete(job->status_buffer);
 
1372
  job->status_buffer = NULL;
 
1373
 
 
1374
 /*
 
1375
  * Update the printer and job state.
 
1376
  */
 
1377
 
 
1378
  cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
 
1379
  cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
 
1380
  update_job_attrs(job, 0);
 
1381
 
 
1382
  if (job->history)
 
1383
    free_job_history(job);
 
1384
 
 
1385
  cupsArrayRemove(PrintingJobs, job);
 
1386
 
 
1387
 /*
 
1388
  * Clear the printer <-> job association...
 
1389
  */
 
1390
 
 
1391
  job->printer->job = NULL;
 
1392
  job->printer      = NULL;
 
1393
}
 
1394
 
 
1395
 
 
1396
/*
 
1397
 * 'cupsdDeleteJob()' - Free all memory used by a job.
 
1398
 */
 
1399
 
 
1400
void
 
1401
cupsdDeleteJob(cupsd_job_t       *job,  /* I - Job */
 
1402
               cupsd_jobaction_t action)/* I - Action */
 
1403
{
 
1404
  int   i;                              /* Looping var */
 
1405
 
 
1406
 
 
1407
  if (job->printer)
 
1408
    finalize_job(job, 1);
 
1409
 
 
1410
  if (action == CUPSD_JOB_PURGE)
 
1411
    remove_job_history(job);
 
1412
 
 
1413
  cupsdClearString(&job->username);
 
1414
  cupsdClearString(&job->dest);
 
1415
  for (i = 0;
 
1416
       i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
 
1417
       i ++)
 
1418
    cupsdClearString(job->auth_env + i);
 
1419
  cupsdClearString(&job->auth_uid);
 
1420
 
 
1421
  if (action == CUPSD_JOB_PURGE)
 
1422
    remove_job_files(job);
 
1423
  else if (job->num_files > 0)
 
1424
  {
 
1425
    free(job->compressions);
 
1426
    free(job->filetypes);
 
1427
 
 
1428
    job->num_files = 0;
 
1429
  }
 
1430
 
 
1431
  if (job->history)
 
1432
    free_job_history(job);
 
1433
 
 
1434
  unload_job(job);
 
1435
 
 
1436
  cupsArrayRemove(Jobs, job);
 
1437
  cupsArrayRemove(ActiveJobs, job);
 
1438
  cupsArrayRemove(PrintingJobs, job);
 
1439
 
 
1440
  free(job);
 
1441
}
 
1442
 
 
1443
 
 
1444
/*
 
1445
 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
 
1446
 */
 
1447
 
 
1448
void
 
1449
cupsdFreeAllJobs(void)
 
1450
{
 
1451
  cupsd_job_t   *job;                   /* Current job */
 
1452
 
 
1453
 
 
1454
  if (!Jobs)
 
1455
    return;
 
1456
 
 
1457
  cupsdHoldSignals();
 
1458
 
 
1459
  cupsdStopAllJobs(CUPSD_JOB_FORCE, 0);
 
1460
  cupsdSaveAllJobs();
 
1461
 
 
1462
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
1463
       job;
 
1464
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
1465
    cupsdDeleteJob(job, CUPSD_JOB_DEFAULT);
 
1466
 
 
1467
  cupsdReleaseSignals();
 
1468
}
 
1469
 
 
1470
 
 
1471
/*
 
1472
 * 'cupsdFindJob()' - Find the specified job.
 
1473
 */
 
1474
 
 
1475
cupsd_job_t *                           /* O - Job data */
 
1476
cupsdFindJob(int id)                    /* I - Job ID */
 
1477
{
 
1478
  cupsd_job_t   key;                    /* Search key */
 
1479
 
 
1480
 
 
1481
  key.id = id;
 
1482
 
 
1483
  return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
 
1484
}
 
1485
 
 
1486
 
 
1487
/*
 
1488
 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
 
1489
 *                               or held jobs in a printer or class.
 
1490
 */
 
1491
 
 
1492
int                                     /* O - Job count */
 
1493
cupsdGetPrinterJobCount(
 
1494
    const char *dest)                   /* I - Printer or class name */
 
1495
{
 
1496
  int           count;                  /* Job count */
 
1497
  cupsd_job_t   *job;                   /* Current job */
 
1498
 
 
1499
 
 
1500
  for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
 
1501
       job;
 
1502
       job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
 
1503
    if (job->dest && !_cups_strcasecmp(job->dest, dest))
 
1504
      count ++;
 
1505
 
 
1506
  return (count);
 
1507
}
 
1508
 
 
1509
 
 
1510
/*
 
1511
 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
 
1512
 *                            or held jobs for a user.
 
1513
 */
 
1514
 
 
1515
int                                     /* O - Job count */
 
1516
cupsdGetUserJobCount(
 
1517
    const char *username)               /* I - Username */
 
1518
{
 
1519
  int           count;                  /* Job count */
 
1520
  cupsd_job_t   *job;                   /* Current job */
 
1521
 
 
1522
 
 
1523
  for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
 
1524
       job;
 
1525
       job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
 
1526
    if (!_cups_strcasecmp(job->username, username))
 
1527
      count ++;
 
1528
 
 
1529
  return (count);
 
1530
}
 
1531
 
 
1532
 
 
1533
/*
 
1534
 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
 
1535
 */
 
1536
 
 
1537
void
 
1538
cupsdLoadAllJobs(void)
 
1539
{
 
1540
  char          filename[1024];         /* Full filename of job.cache file */
 
1541
  struct stat   fileinfo,               /* Information on job.cache file */
 
1542
                dirinfo;                /* Information on RequestRoot dir */
 
1543
 
 
1544
 
 
1545
 
 
1546
 /*
 
1547
  * Create the job arrays as needed...
 
1548
  */
 
1549
 
 
1550
  if (!Jobs)
 
1551
    Jobs = cupsArrayNew(compare_jobs, NULL);
 
1552
 
 
1553
  if (!ActiveJobs)
 
1554
    ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
 
1555
 
 
1556
  if (!PrintingJobs)
 
1557
    PrintingJobs = cupsArrayNew(compare_jobs, NULL);
 
1558
 
 
1559
 /*
 
1560
  * See whether the job.cache file is older than the RequestRoot directory...
 
1561
  */
 
1562
 
 
1563
  snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
 
1564
 
 
1565
  if (stat(filename, &fileinfo))
 
1566
  {
 
1567
    fileinfo.st_mtime = 0;
 
1568
 
 
1569
    if (errno != ENOENT)
 
1570
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1571
                      "Unable to get file information for \"%s\" - %s",
 
1572
                      filename, strerror(errno));
 
1573
  }
 
1574
 
 
1575
  if (stat(RequestRoot, &dirinfo))
 
1576
  {
 
1577
    dirinfo.st_mtime = 0;
 
1578
 
 
1579
    if (errno != ENOENT)
 
1580
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1581
                      "Unable to get directory information for \"%s\" - %s",
 
1582
                      RequestRoot, strerror(errno));
 
1583
  }
 
1584
 
 
1585
 /*
 
1586
  * Load the most recent source for job data...
 
1587
  */
 
1588
 
 
1589
  if (dirinfo.st_mtime > fileinfo.st_mtime)
 
1590
  {
 
1591
    load_request_root();
 
1592
 
 
1593
    load_next_job_id(filename);
 
1594
  }
 
1595
  else
 
1596
    load_job_cache(filename);
 
1597
 
 
1598
 /*
 
1599
  * Clean out old jobs as needed...
 
1600
  */
 
1601
 
 
1602
  if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs)
 
1603
    cupsdCleanJobs();
 
1604
}
 
1605
 
 
1606
 
 
1607
/*
 
1608
 * 'cupsdLoadJob()' - Load a single job.
 
1609
 */
 
1610
 
 
1611
int                                     /* O - 1 on success, 0 on failure */
 
1612
cupsdLoadJob(cupsd_job_t *job)          /* I - Job */
 
1613
{
 
1614
  int                   i;              /* Looping var */
 
1615
  char                  jobfile[1024];  /* Job filename */
 
1616
  cups_file_t           *fp;            /* Job file */
 
1617
  int                   fileid;         /* Current file ID */
 
1618
  ipp_attribute_t       *attr;          /* Job attribute */
 
1619
  const char            *dest;          /* Destination name */
 
1620
  cupsd_printer_t       *destptr;       /* Pointer to destination */
 
1621
  mime_type_t           **filetypes;    /* New filetypes array */
 
1622
  int                   *compressions;  /* New compressions array */
 
1623
 
 
1624
 
 
1625
  if (job->attrs)
 
1626
  {
 
1627
    if (job->state_value > IPP_JOB_STOPPED)
 
1628
      job->access_time = time(NULL);
 
1629
 
 
1630
    return (1);
 
1631
  }
 
1632
 
 
1633
  if ((job->attrs = ippNew()) == NULL)
 
1634
  {
 
1635
    cupsdLogJob(job, CUPSD_LOG_ERROR, "Ran out of memory for job attributes.");
 
1636
    return (0);
 
1637
  }
 
1638
 
 
1639
 /*
 
1640
  * Load job attributes...
 
1641
  */
 
1642
 
 
1643
  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id);
 
1644
 
 
1645
  snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
 
1646
  if ((fp = cupsdOpenConfFile(jobfile)) == NULL)
 
1647
    goto error;
 
1648
 
 
1649
  if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
 
1650
  {
 
1651
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
1652
                    "[Job %d] Unable to read job control file \"%s\".", job->id,
 
1653
                    jobfile);
 
1654
    cupsFileClose(fp);
 
1655
    goto error;
 
1656
  }
 
1657
 
 
1658
  cupsFileClose(fp);
 
1659
 
 
1660
 /*
 
1661
  * Copy attribute data to the job object...
 
1662
  */
 
1663
 
 
1664
  if (!ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER))
 
1665
  {
 
1666
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
1667
                    "[Job %d] Missing or bad time-at-creation attribute in "
 
1668
                    "control file.", job->id);
 
1669
    goto error;
 
1670
  }
 
1671
 
 
1672
  if ((job->state = ippFindAttribute(job->attrs, "job-state",
 
1673
                                     IPP_TAG_ENUM)) == NULL)
 
1674
  {
 
1675
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
1676
                    "[Job %d] Missing or bad job-state attribute in control "
 
1677
                    "file.", job->id);
 
1678
    goto error;
 
1679
  }
 
1680
 
 
1681
  job->state_value  = (ipp_jstate_t)job->state->values[0].integer;
 
1682
  job->file_time    = 0;
 
1683
  job->history_time = 0;
 
1684
 
 
1685
  if (job->state_value >= IPP_JOB_CANCELED &&
 
1686
      (attr = ippFindAttribute(job->attrs, "time-at-completed",
 
1687
                               IPP_TAG_INTEGER)) != NULL)
 
1688
  {
 
1689
    if (JobHistory < INT_MAX)
 
1690
      job->history_time = attr->values[0].integer + JobHistory;
 
1691
    else
 
1692
      job->history_time = INT_MAX;
 
1693
 
 
1694
    if (job->history_time < time(NULL))
 
1695
      goto error;                       /* Expired, remove from history */
 
1696
 
 
1697
    if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
 
1698
      JobHistoryUpdate = job->history_time;
 
1699
 
 
1700
    if (JobFiles < INT_MAX)
 
1701
      job->file_time = attr->values[0].integer + JobFiles;
 
1702
    else
 
1703
      job->file_time = INT_MAX;
 
1704
 
 
1705
    if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
 
1706
      JobHistoryUpdate = job->file_time;
 
1707
 
 
1708
    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdLoadJob: JobHistoryUpdate=%ld",
 
1709
                    (long)JobHistoryUpdate);
 
1710
  }
 
1711
 
 
1712
  if (!job->dest)
 
1713
  {
 
1714
    if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
 
1715
                                 IPP_TAG_URI)) == NULL)
 
1716
    {
 
1717
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1718
                      "[Job %d] No job-printer-uri attribute in control file.",
 
1719
                      job->id);
 
1720
      goto error;
 
1721
    }
 
1722
 
 
1723
    if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype),
 
1724
                                  &destptr)) == NULL)
 
1725
    {
 
1726
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1727
                      "[Job %d] Unable to queue job for destination \"%s\".",
 
1728
                      job->id, attr->values[0].string.text);
 
1729
      goto error;
 
1730
    }
 
1731
 
 
1732
    cupsdSetString(&job->dest, dest);
 
1733
  }
 
1734
  else if ((destptr = cupsdFindDest(job->dest)) == NULL)
 
1735
  {
 
1736
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
1737
                    "[Job %d] Unable to queue job for destination \"%s\".",
 
1738
                    job->id, job->dest);
 
1739
    goto error;
 
1740
  }
 
1741
 
 
1742
  if ((job->reasons = ippFindAttribute(job->attrs, "job-state-reasons",
 
1743
                                       IPP_TAG_KEYWORD)) == NULL)
 
1744
  {
 
1745
    const char  *reason;                /* job-state-reason keyword */
 
1746
 
 
1747
    cupsdLogMessage(CUPSD_LOG_DEBUG,
 
1748
                    "[Job %d] Adding missing job-state-reasons attribute to "
 
1749
                    " control file.", job->id);
 
1750
 
 
1751
    switch (job->state_value)
 
1752
    {
 
1753
      default :
 
1754
      case IPP_JOB_PENDING :
 
1755
          if (destptr->state == IPP_PRINTER_STOPPED)
 
1756
            reason = "printer-stopped";
 
1757
          else
 
1758
            reason = "none";
 
1759
          break;
 
1760
 
 
1761
      case IPP_JOB_HELD :
 
1762
          if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
 
1763
                                       IPP_TAG_ZERO)) != NULL &&
 
1764
              (attr->value_tag == IPP_TAG_NAME ||
 
1765
               attr->value_tag == IPP_TAG_NAMELANG ||
 
1766
               attr->value_tag == IPP_TAG_KEYWORD) &&
 
1767
              strcmp(attr->values[0].string.text, "no-hold"))
 
1768
            reason = "job-hold-until-specified";
 
1769
          else
 
1770
            reason = "job-incoming";
 
1771
          break;
 
1772
 
 
1773
      case IPP_JOB_PROCESSING :
 
1774
          reason = "job-printing";
 
1775
          break;
 
1776
 
 
1777
      case IPP_JOB_STOPPED :
 
1778
          reason = "job-stopped";
 
1779
          break;
 
1780
 
 
1781
      case IPP_JOB_CANCELED :
 
1782
          reason = "job-canceled-by-user";
 
1783
          break;
 
1784
 
 
1785
      case IPP_JOB_ABORTED :
 
1786
          reason = "aborted-by-system";
 
1787
          break;
 
1788
 
 
1789
      case IPP_JOB_COMPLETED :
 
1790
          reason = "job-completed-successfully";
 
1791
          break;
 
1792
    }
 
1793
 
 
1794
    job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
1795
                                "job-state-reasons", NULL, reason);
 
1796
  }
 
1797
  else if (job->state_value == IPP_JOB_PENDING)
 
1798
  {
 
1799
    if (destptr->state == IPP_PRINTER_STOPPED)
 
1800
      ippSetString(job->attrs, &job->reasons, 0, "printer-stopped");
 
1801
    else
 
1802
      ippSetString(job->attrs, &job->reasons, 0, "none");
 
1803
  }
 
1804
 
 
1805
  job->sheets     = ippFindAttribute(job->attrs, "job-media-sheets-completed",
 
1806
                                     IPP_TAG_INTEGER);
 
1807
  job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
 
1808
 
 
1809
  if (!job->priority)
 
1810
  {
 
1811
    if ((attr = ippFindAttribute(job->attrs, "job-priority",
 
1812
                                 IPP_TAG_INTEGER)) == NULL)
 
1813
    {
 
1814
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1815
                      "[Job %d] Missing or bad job-priority attribute in "
 
1816
                      "control file.", job->id);
 
1817
      goto error;
 
1818
    }
 
1819
 
 
1820
    job->priority = attr->values[0].integer;
 
1821
  }
 
1822
 
 
1823
  if (!job->username)
 
1824
  {
 
1825
    if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
 
1826
                                 IPP_TAG_NAME)) == NULL)
 
1827
    {
 
1828
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1829
                      "[Job %d] Missing or bad job-originating-user-name "
 
1830
                      "attribute in control file.", job->id);
 
1831
      goto error;
 
1832
    }
 
1833
 
 
1834
    cupsdSetString(&job->username, attr->values[0].string.text);
 
1835
  }
 
1836
 
 
1837
 /*
 
1838
  * Set the job hold-until time and state...
 
1839
  */
 
1840
 
 
1841
  if (job->state_value == IPP_JOB_HELD)
 
1842
  {
 
1843
    if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
 
1844
                                 IPP_TAG_KEYWORD)) == NULL)
 
1845
      attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
 
1846
 
 
1847
    if (attr)
 
1848
      cupsdSetJobHoldUntil(job, attr->values[0].string.text, CUPSD_JOB_DEFAULT);
 
1849
    else
 
1850
    {
 
1851
      job->state->values[0].integer = IPP_JOB_PENDING;
 
1852
      job->state_value              = IPP_JOB_PENDING;
 
1853
    }
 
1854
  }
 
1855
  else if (job->state_value == IPP_JOB_PROCESSING)
 
1856
  {
 
1857
    job->state->values[0].integer = IPP_JOB_PENDING;
 
1858
    job->state_value              = IPP_JOB_PENDING;
 
1859
  }
 
1860
 
 
1861
  if (!job->num_files)
 
1862
  {
 
1863
   /*
 
1864
    * Find all the d##### files...
 
1865
    */
 
1866
 
 
1867
    for (fileid = 1; fileid < 10000; fileid ++)
 
1868
    {
 
1869
      snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
 
1870
               job->id, fileid);
 
1871
 
 
1872
      if (access(jobfile, 0))
 
1873
        break;
 
1874
 
 
1875
      cupsdLogMessage(CUPSD_LOG_DEBUG,
 
1876
                      "[Job %d] Auto-typing document file \"%s\"...", job->id,
 
1877
                      jobfile);
 
1878
 
 
1879
      if (fileid > job->num_files)
 
1880
      {
 
1881
        if (job->num_files == 0)
 
1882
        {
 
1883
          compressions = (int *)calloc(fileid, sizeof(int));
 
1884
          filetypes    = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
 
1885
        }
 
1886
        else
 
1887
        {
 
1888
          compressions = (int *)realloc(job->compressions,
 
1889
                                        sizeof(int) * fileid);
 
1890
          filetypes    = (mime_type_t **)realloc(job->filetypes,
 
1891
                                                 sizeof(mime_type_t *) *
 
1892
                                                 fileid);
 
1893
        }
 
1894
 
 
1895
        if (compressions)
 
1896
          job->compressions = compressions;
 
1897
 
 
1898
        if (filetypes)
 
1899
          job->filetypes = filetypes;
 
1900
 
 
1901
        if (!compressions || !filetypes)
 
1902
        {
 
1903
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
1904
                          "[Job %d] Ran out of memory for job file types.",
 
1905
                          job->id);
 
1906
 
 
1907
          ippDelete(job->attrs);
 
1908
          job->attrs = NULL;
 
1909
 
 
1910
          if (job->compressions)
 
1911
          {
 
1912
            free(job->compressions);
 
1913
            job->compressions = NULL;
 
1914
          }
 
1915
 
 
1916
          if (job->filetypes)
 
1917
          {
 
1918
            free(job->filetypes);
 
1919
            job->filetypes = NULL;
 
1920
          }
 
1921
 
 
1922
          job->num_files = 0;
 
1923
          return (0);
 
1924
        }
 
1925
 
 
1926
        job->num_files = fileid;
 
1927
      }
 
1928
 
 
1929
      job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL,
 
1930
                                                job->compressions + fileid - 1);
 
1931
 
 
1932
      if (!job->filetypes[fileid - 1])
 
1933
        job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
 
1934
                                              "vnd.cups-raw");
 
1935
    }
 
1936
  }
 
1937
 
 
1938
 /*
 
1939
  * Load authentication information as needed...
 
1940
  */
 
1941
 
 
1942
  if (job->state_value < IPP_JOB_STOPPED)
 
1943
  {
 
1944
    snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
 
1945
 
 
1946
    for (i = 0;
 
1947
         i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
 
1948
         i ++)
 
1949
      cupsdClearString(job->auth_env + i);
 
1950
    cupsdClearString(&job->auth_uid);
 
1951
 
 
1952
    if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
 
1953
    {
 
1954
      int       bytes,                  /* Size of auth data */
 
1955
                linenum = 1;            /* Current line number */
 
1956
      char      line[65536],            /* Line from file */
 
1957
                *value,                 /* Value from line */
 
1958
                data[65536];            /* Decoded data */
 
1959
 
 
1960
 
 
1961
      if (cupsFileGets(fp, line, sizeof(line)) &&
 
1962
          !strcmp(line, "CUPSD-AUTH-V3"))
 
1963
      {
 
1964
        i = 0;
 
1965
        while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
 
1966
        {
 
1967
         /*
 
1968
          * Decode value...
 
1969
          */
 
1970
 
 
1971
          if (strcmp(line, "negotiate") && strcmp(line, "uid"))
 
1972
          {
 
1973
            bytes = sizeof(data);
 
1974
            httpDecode64_2(data, &bytes, value);
 
1975
          }
 
1976
 
 
1977
         /*
 
1978
          * Assign environment variables...
 
1979
          */
 
1980
 
 
1981
          if (!strcmp(line, "uid"))
 
1982
          {
 
1983
            cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", value);
 
1984
            continue;
 
1985
          }
 
1986
          else if (i >= (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])))
 
1987
            break;
 
1988
 
 
1989
          if (!strcmp(line, "username"))
 
1990
            cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data);
 
1991
          else if (!strcmp(line, "domain"))
 
1992
            cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data);
 
1993
          else if (!strcmp(line, "password"))
 
1994
            cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data);
 
1995
          else if (!strcmp(line, "negotiate"))
 
1996
            cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", value);
 
1997
          else
 
1998
            continue;
 
1999
 
 
2000
          i ++;
 
2001
        }
 
2002
      }
 
2003
 
 
2004
      cupsFileClose(fp);
 
2005
    }
 
2006
  }
 
2007
 
 
2008
  job->access_time = time(NULL);
 
2009
  return (1);
 
2010
 
 
2011
 /*
 
2012
  * If we get here then something bad happened...
 
2013
  */
 
2014
 
 
2015
  error:
 
2016
 
 
2017
  ippDelete(job->attrs);
 
2018
  job->attrs = NULL;
 
2019
 
 
2020
  remove_job_history(job);
 
2021
  remove_job_files(job);
 
2022
 
 
2023
  return (0);
 
2024
}
 
2025
 
 
2026
 
 
2027
/*
 
2028
 * 'cupsdMoveJob()' - Move the specified job to a different destination.
 
2029
 */
 
2030
 
 
2031
void
 
2032
cupsdMoveJob(cupsd_job_t     *job,      /* I - Job */
 
2033
             cupsd_printer_t *p)        /* I - Destination printer or class */
 
2034
{
 
2035
  ipp_attribute_t       *attr;          /* job-printer-uri attribute */
 
2036
  const char            *olddest;       /* Old destination */
 
2037
  cupsd_printer_t       *oldp;          /* Old pointer */
 
2038
 
 
2039
 
 
2040
 /*
 
2041
  * Don't move completed jobs...
 
2042
  */
 
2043
 
 
2044
  if (job->state_value > IPP_JOB_STOPPED)
 
2045
    return;
 
2046
 
 
2047
 /*
 
2048
  * Get the old destination...
 
2049
  */
 
2050
 
 
2051
  olddest = job->dest;
 
2052
 
 
2053
  if (job->printer)
 
2054
    oldp = job->printer;
 
2055
  else
 
2056
    oldp = cupsdFindDest(olddest);
 
2057
 
 
2058
 /*
 
2059
  * Change the destination information...
 
2060
  */
 
2061
 
 
2062
  if (job->state_value > IPP_JOB_HELD)
 
2063
    cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
 
2064
                     "Stopping job prior to move.");
 
2065
 
 
2066
  cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
 
2067
                "Job #%d moved from %s to %s.", job->id, olddest,
 
2068
                p->name);
 
2069
 
 
2070
  cupsdSetString(&job->dest, p->name);
 
2071
  job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
 
2072
 
 
2073
  if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
 
2074
                               IPP_TAG_URI)) != NULL)
 
2075
    cupsdSetString(&(attr->values[0].string.text), p->uri);
 
2076
 
 
2077
  cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
 
2078
                "Job #%d moved from %s to %s.", job->id, olddest,
 
2079
                p->name);
 
2080
 
 
2081
  job->dirty = 1;
 
2082
  cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
2083
}
 
2084
 
 
2085
 
 
2086
/*
 
2087
 * 'cupsdReleaseJob()' - Release the specified job.
 
2088
 */
 
2089
 
 
2090
void
 
2091
cupsdReleaseJob(cupsd_job_t *job)       /* I - Job */
 
2092
{
 
2093
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob(job=%p(%d))", job,
 
2094
                  job->id);
 
2095
 
 
2096
  if (job->state_value == IPP_JOB_HELD)
 
2097
  {
 
2098
   /*
 
2099
    * Add trailing banner as needed...
 
2100
    */
 
2101
 
 
2102
    if (job->pending_timeout)
 
2103
      cupsdTimeoutJob(job);
 
2104
 
 
2105
    cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
 
2106
                     "Job released by user.");
 
2107
  }
 
2108
}
 
2109
 
 
2110
 
 
2111
/*
 
2112
 * 'cupsdRestartJob()' - Restart the specified job.
 
2113
 */
 
2114
 
 
2115
void
 
2116
cupsdRestartJob(cupsd_job_t *job)       /* I - Job */
 
2117
{
 
2118
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob(job=%p(%d))", job,
 
2119
                  job->id);
 
2120
 
 
2121
  if (job->state_value == IPP_JOB_STOPPED || job->num_files)
 
2122
    cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
 
2123
                     "Job restarted by user.");
 
2124
}
 
2125
 
 
2126
 
 
2127
/*
 
2128
 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
 
2129
 */
 
2130
 
 
2131
void
 
2132
cupsdSaveAllJobs(void)
 
2133
{
 
2134
  int           i;                      /* Looping var */
 
2135
  cups_file_t   *fp;                    /* job.cache file */
 
2136
  char          filename[1024],         /* job.cache filename */
 
2137
                temp[1024];             /* Temporary string */
 
2138
  cupsd_job_t   *job;                   /* Current job */
 
2139
  time_t        curtime;                /* Current time */
 
2140
  struct tm     *curdate;               /* Current date */
 
2141
 
 
2142
 
 
2143
  snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
 
2144
  if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
 
2145
    return;
 
2146
 
 
2147
  cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache...");
 
2148
 
 
2149
 /*
 
2150
  * Write a small header to the file...
 
2151
  */
 
2152
 
 
2153
  curtime = time(NULL);
 
2154
  curdate = localtime(&curtime);
 
2155
  strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
 
2156
 
 
2157
  cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n");
 
2158
  cupsFilePrintf(fp, "# Written by cupsd\n", temp);
 
2159
  cupsFilePrintf(fp, "NextJobId %d\n", NextJobId);
 
2160
 
 
2161
 /*
 
2162
  * Write each job known to the system...
 
2163
  */
 
2164
 
 
2165
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
2166
       job;
 
2167
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
2168
  {
 
2169
    cupsFilePrintf(fp, "<Job %d>\n", job->id);
 
2170
    cupsFilePrintf(fp, "State %d\n", job->state_value);
 
2171
    cupsFilePrintf(fp, "Priority %d\n", job->priority);
 
2172
    cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
 
2173
    cupsFilePrintf(fp, "Username %s\n", job->username);
 
2174
    cupsFilePrintf(fp, "Destination %s\n", job->dest);
 
2175
    cupsFilePrintf(fp, "DestType %d\n", job->dtype);
 
2176
    cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
 
2177
    for (i = 0; i < job->num_files; i ++)
 
2178
      cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
 
2179
                     job->filetypes[i]->type, job->compressions[i]);
 
2180
    cupsFilePuts(fp, "</Job>\n");
 
2181
  }
 
2182
 
 
2183
  cupsdCloseCreatedConfFile(fp, filename);
 
2184
}
 
2185
 
 
2186
 
 
2187
/*
 
2188
 * 'cupsdSaveJob()' - Save a job to disk.
 
2189
 */
 
2190
 
 
2191
void
 
2192
cupsdSaveJob(cupsd_job_t *job)          /* I - Job */
 
2193
{
 
2194
  char          filename[1024];         /* Job control filename */
 
2195
  cups_file_t   *fp;                    /* Job file */
 
2196
 
 
2197
 
 
2198
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
 
2199
                  job, job->id, job->attrs);
 
2200
 
 
2201
  snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
 
2202
 
 
2203
  if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL)
 
2204
    return;
 
2205
 
 
2206
  fchown(cupsFileNumber(fp), RunUser, Group);
 
2207
 
 
2208
  job->attrs->state = IPP_IDLE;
 
2209
 
 
2210
  if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
 
2211
                 job->attrs) != IPP_DATA)
 
2212
  {
 
2213
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
2214
                    "[Job %d] Unable to write job control file.", job->id);
 
2215
    cupsFileClose(fp);
 
2216
    return;
 
2217
  }
 
2218
 
 
2219
  if (!cupsdCloseCreatedConfFile(fp, filename))
 
2220
  {
 
2221
   /*
 
2222
    * Remove backup file and mark this job as clean...
 
2223
    */
 
2224
 
 
2225
    strlcat(filename, ".O", sizeof(filename));
 
2226
    unlink(filename);
 
2227
 
 
2228
    job->dirty = 0;
 
2229
  }
 
2230
}
 
2231
 
 
2232
 
 
2233
/*
 
2234
 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job.
 
2235
 */
 
2236
 
 
2237
void
 
2238
cupsdSetJobHoldUntil(cupsd_job_t *job,  /* I - Job */
 
2239
                     const char  *when, /* I - When to resume */
 
2240
                     int         update)/* I - Update job-hold-until attr? */
 
2241
{
 
2242
  time_t        curtime;                /* Current time */
 
2243
  struct tm     *curdate;               /* Current date */
 
2244
  int           hour;                   /* Hold hour */
 
2245
  int           minute;                 /* Hold minute */
 
2246
  int           second = 0;             /* Hold second */
 
2247
 
 
2248
 
 
2249
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
2250
                  "cupsdSetJobHoldUntil(job=%p(%d), when=\"%s\", update=%d)",
 
2251
                  job, job->id, when, update);
 
2252
 
 
2253
  if (update)
 
2254
  {
 
2255
   /*
 
2256
    * Update the job-hold-until attribute...
 
2257
    */
 
2258
 
 
2259
    ipp_attribute_t *attr;              /* job-hold-until attribute */
 
2260
 
 
2261
    if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
 
2262
                                 IPP_TAG_KEYWORD)) == NULL)
 
2263
      attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
 
2264
 
 
2265
    if (attr)
 
2266
      cupsdSetString(&(attr->values[0].string.text), when);
 
2267
    else
 
2268
      attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
2269
                          "job-hold-until", NULL, when);
 
2270
 
 
2271
    if (attr)
 
2272
    {
 
2273
      if (isdigit(when[0] & 255))
 
2274
        attr->value_tag = IPP_TAG_NAME;
 
2275
      else
 
2276
        attr->value_tag = IPP_TAG_KEYWORD;
 
2277
 
 
2278
      job->dirty = 1;
 
2279
      cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
2280
    }
 
2281
 
 
2282
    ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
 
2283
  }
 
2284
 
 
2285
 /*
 
2286
  * Update the hold time...
 
2287
  */
 
2288
 
 
2289
  job->cancel_time = 0;
 
2290
 
 
2291
  if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required"))
 
2292
  {
 
2293
   /*
 
2294
    * Hold indefinitely...
 
2295
    */
 
2296
 
 
2297
    job->hold_until = 0;
 
2298
 
 
2299
    if (MaxHoldTime > 0)
 
2300
      job->cancel_time = time(NULL) + MaxHoldTime;
 
2301
  }
 
2302
  else if (!strcmp(when, "day-time"))
 
2303
  {
 
2304
   /*
 
2305
    * Hold to 6am the next morning unless local time is < 6pm.
 
2306
    */
 
2307
 
 
2308
    curtime = time(NULL);
 
2309
    curdate = localtime(&curtime);
 
2310
 
 
2311
    if (curdate->tm_hour < 18)
 
2312
      job->hold_until = curtime;
 
2313
    else
 
2314
      job->hold_until = curtime +
 
2315
                        ((29 - curdate->tm_hour) * 60 + 59 -
 
2316
                         curdate->tm_min) * 60 + 60 - curdate->tm_sec;
 
2317
  }
 
2318
  else if (!strcmp(when, "evening") || !strcmp(when, "night"))
 
2319
  {
 
2320
   /*
 
2321
    * Hold to 6pm unless local time is > 6pm or < 6am.
 
2322
    */
 
2323
 
 
2324
    curtime = time(NULL);
 
2325
    curdate = localtime(&curtime);
 
2326
 
 
2327
    if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
 
2328
      job->hold_until = curtime;
 
2329
    else
 
2330
      job->hold_until = curtime +
 
2331
                        ((17 - curdate->tm_hour) * 60 + 59 -
 
2332
                         curdate->tm_min) * 60 + 60 - curdate->tm_sec;
 
2333
  }
 
2334
  else if (!strcmp(when, "second-shift"))
 
2335
  {
 
2336
   /*
 
2337
    * Hold to 4pm unless local time is > 4pm.
 
2338
    */
 
2339
 
 
2340
    curtime = time(NULL);
 
2341
    curdate = localtime(&curtime);
 
2342
 
 
2343
    if (curdate->tm_hour >= 16)
 
2344
      job->hold_until = curtime;
 
2345
    else
 
2346
      job->hold_until = curtime +
 
2347
                        ((15 - curdate->tm_hour) * 60 + 59 -
 
2348
                         curdate->tm_min) * 60 + 60 - curdate->tm_sec;
 
2349
  }
 
2350
  else if (!strcmp(when, "third-shift"))
 
2351
  {
 
2352
   /*
 
2353
    * Hold to 12am unless local time is < 8am.
 
2354
    */
 
2355
 
 
2356
    curtime = time(NULL);
 
2357
    curdate = localtime(&curtime);
 
2358
 
 
2359
    if (curdate->tm_hour < 8)
 
2360
      job->hold_until = curtime;
 
2361
    else
 
2362
      job->hold_until = curtime +
 
2363
                        ((23 - curdate->tm_hour) * 60 + 59 -
 
2364
                         curdate->tm_min) * 60 + 60 - curdate->tm_sec;
 
2365
  }
 
2366
  else if (!strcmp(when, "weekend"))
 
2367
  {
 
2368
   /*
 
2369
    * Hold to weekend unless we are in the weekend.
 
2370
    */
 
2371
 
 
2372
    curtime = time(NULL);
 
2373
    curdate = localtime(&curtime);
 
2374
 
 
2375
    if (curdate->tm_wday == 0 || curdate->tm_wday == 6)
 
2376
      job->hold_until = curtime;
 
2377
    else
 
2378
      job->hold_until = curtime +
 
2379
                        (((5 - curdate->tm_wday) * 24 +
 
2380
                          (17 - curdate->tm_hour)) * 60 + 59 -
 
2381
                           curdate->tm_min) * 60 + 60 - curdate->tm_sec;
 
2382
  }
 
2383
  else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
 
2384
  {
 
2385
   /*
 
2386
    * Hold to specified GMT time (HH:MM or HH:MM:SS)...
 
2387
    */
 
2388
 
 
2389
    curtime = time(NULL);
 
2390
    curdate = gmtime(&curtime);
 
2391
 
 
2392
    job->hold_until = curtime +
 
2393
                      ((hour - curdate->tm_hour) * 60 + minute -
 
2394
                       curdate->tm_min) * 60 + second - curdate->tm_sec;
 
2395
 
 
2396
   /*
 
2397
    * Hold until next day as needed...
 
2398
    */
 
2399
 
 
2400
    if (job->hold_until < curtime)
 
2401
      job->hold_until += 24 * 60 * 60;
 
2402
  }
 
2403
 
 
2404
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until=%d",
 
2405
                  (int)job->hold_until);
 
2406
}
 
2407
 
 
2408
 
 
2409
/*
 
2410
 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
 
2411
 *                           the list as needed.
 
2412
 */
 
2413
 
 
2414
void
 
2415
cupsdSetJobPriority(
 
2416
    cupsd_job_t *job,                   /* I - Job ID */
 
2417
    int         priority)               /* I - New priority (0 to 100) */
 
2418
{
 
2419
  ipp_attribute_t       *attr;          /* Job attribute */
 
2420
 
 
2421
 
 
2422
 /*
 
2423
  * Don't change completed jobs...
 
2424
  */
 
2425
 
 
2426
  if (job->state_value >= IPP_JOB_PROCESSING)
 
2427
    return;
 
2428
 
 
2429
 /*
 
2430
  * Set the new priority and re-add the job into the active list...
 
2431
  */
 
2432
 
 
2433
  cupsArrayRemove(ActiveJobs, job);
 
2434
 
 
2435
  job->priority = priority;
 
2436
 
 
2437
  if ((attr = ippFindAttribute(job->attrs, "job-priority",
 
2438
                               IPP_TAG_INTEGER)) != NULL)
 
2439
    attr->values[0].integer = priority;
 
2440
  else
 
2441
    ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
 
2442
                  priority);
 
2443
 
 
2444
  cupsArrayAdd(ActiveJobs, job);
 
2445
 
 
2446
  job->dirty = 1;
 
2447
  cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
2448
}
 
2449
 
 
2450
 
 
2451
/*
 
2452
 * 'cupsdSetJobState()' - Set the state of the specified print job.
 
2453
 */
 
2454
 
 
2455
void
 
2456
cupsdSetJobState(
 
2457
    cupsd_job_t       *job,             /* I - Job to cancel */
 
2458
    ipp_jstate_t      newstate,         /* I - New job state */
 
2459
    cupsd_jobaction_t action,           /* I - Action to take */
 
2460
    const char        *message,         /* I - Message to log */
 
2461
    ...)                                /* I - Additional arguments as needed */
 
2462
{
 
2463
  int                   i;              /* Looping var */
 
2464
  ipp_jstate_t          oldstate;       /* Old state */
 
2465
  char                  filename[1024]; /* Job filename */
 
2466
  ipp_attribute_t       *attr;          /* Job attribute */
 
2467
 
 
2468
 
 
2469
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
2470
                  "cupsdSetJobState(job=%p(%d), state=%d, newstate=%d, "
 
2471
                  "action=%d, message=\"%s\")", job, job->id, job->state_value,
 
2472
                  newstate, action, message ? message : "(null)");
 
2473
 
 
2474
 
 
2475
 /*
 
2476
  * Make sure we have the job attributes...
 
2477
  */
 
2478
 
 
2479
  if (!cupsdLoadJob(job))
 
2480
    return;
 
2481
 
 
2482
 /*
 
2483
  * Don't do anything if the state is unchanged and we aren't purging the
 
2484
  * job...
 
2485
  */
 
2486
 
 
2487
  oldstate = job->state_value;
 
2488
  if (newstate == oldstate && action != CUPSD_JOB_PURGE)
 
2489
    return;
 
2490
 
 
2491
 /*
 
2492
  * Stop any processes that are working on the current job...
 
2493
  */
 
2494
 
 
2495
  if (oldstate == IPP_JOB_PROCESSING)
 
2496
    stop_job(job, action);
 
2497
 
 
2498
 /*
 
2499
  * Set the new job state...
 
2500
  */
 
2501
 
 
2502
  job->state_value = newstate;
 
2503
 
 
2504
  if (job->state)
 
2505
    job->state->values[0].integer = newstate;
 
2506
 
 
2507
  switch (newstate)
 
2508
  {
 
2509
    case IPP_JOB_PENDING :
 
2510
       /*
 
2511
        * Update job-hold-until as needed...
 
2512
        */
 
2513
 
 
2514
        if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
 
2515
                                     IPP_TAG_KEYWORD)) == NULL)
 
2516
          attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
 
2517
 
 
2518
        if (attr)
 
2519
        {
 
2520
          attr->value_tag = IPP_TAG_KEYWORD;
 
2521
          cupsdSetString(&(attr->values[0].string.text), "no-hold");
 
2522
        }
 
2523
 
 
2524
    default :
 
2525
        break;
 
2526
 
 
2527
    case IPP_JOB_ABORTED :
 
2528
    case IPP_JOB_CANCELED :
 
2529
    case IPP_JOB_COMPLETED :
 
2530
        set_time(job, "time-at-completed");
 
2531
        ippSetString(job->attrs, &job->reasons, 0, "processing-to-stop-point");
 
2532
        break;
 
2533
  }
 
2534
 
 
2535
 /*
 
2536
  * Log message as needed...
 
2537
  */
 
2538
 
 
2539
  if (message)
 
2540
  {
 
2541
    char        buffer[2048];           /* Message buffer */
 
2542
    va_list     ap;                     /* Pointer to additional arguments */
 
2543
 
 
2544
    va_start(ap, message);
 
2545
    vsnprintf(buffer, sizeof(buffer), message, ap);
 
2546
    va_end(ap);
 
2547
 
 
2548
    if (newstate > IPP_JOB_STOPPED)
 
2549
      cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, "%s", buffer);
 
2550
    else
 
2551
      cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "%s", buffer);
 
2552
 
 
2553
    if (newstate == IPP_JOB_STOPPED || newstate == IPP_JOB_ABORTED)
 
2554
      cupsdLogJob(job, CUPSD_LOG_ERROR, "%s", buffer);
 
2555
    else
 
2556
      cupsdLogJob(job, CUPSD_LOG_INFO, "%s", buffer);
 
2557
  }
 
2558
 
 
2559
 /*
 
2560
  * Handle post-state-change actions...
 
2561
  */
 
2562
 
 
2563
  switch (newstate)
 
2564
  {
 
2565
    case IPP_JOB_PROCESSING :
 
2566
       /*
 
2567
        * Add the job to the "printing" list...
 
2568
        */
 
2569
 
 
2570
        if (!cupsArrayFind(PrintingJobs, job))
 
2571
          cupsArrayAdd(PrintingJobs, job);
 
2572
 
 
2573
       /*
 
2574
        * Set the processing time...
 
2575
        */
 
2576
 
 
2577
        set_time(job, "time-at-processing");
 
2578
 
 
2579
    case IPP_JOB_PENDING :
 
2580
    case IPP_JOB_HELD :
 
2581
    case IPP_JOB_STOPPED :
 
2582
       /*
 
2583
        * Make sure the job is in the active list...
 
2584
        */
 
2585
 
 
2586
        if (!cupsArrayFind(ActiveJobs, job))
 
2587
          cupsArrayAdd(ActiveJobs, job);
 
2588
 
 
2589
       /*
 
2590
        * Save the job state to disk...
 
2591
        */
 
2592
 
 
2593
        job->dirty = 1;
 
2594
        cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
2595
        break;
 
2596
 
 
2597
    case IPP_JOB_ABORTED :
 
2598
    case IPP_JOB_CANCELED :
 
2599
    case IPP_JOB_COMPLETED :
 
2600
        if (newstate == IPP_JOB_CANCELED)
 
2601
        {
 
2602
         /*
 
2603
          * Remove the job from the active list if there are no processes still
 
2604
          * running for it...
 
2605
          */
 
2606
 
 
2607
          for (i = 0; job->filters[i] < 0; i++);
 
2608
 
 
2609
          if (!job->filters[i] && job->backend <= 0)
 
2610
            cupsArrayRemove(ActiveJobs, job);
 
2611
        }
 
2612
        else
 
2613
        {
 
2614
         /*
 
2615
          * Otherwise just remove the job from the active list immediately...
 
2616
          */
 
2617
 
 
2618
          cupsArrayRemove(ActiveJobs, job);
 
2619
        }
 
2620
 
 
2621
       /*
 
2622
        * Expire job subscriptions since the job is now "completed"...
 
2623
        */
 
2624
 
 
2625
        cupsdExpireSubscriptions(NULL, job);
 
2626
 
 
2627
#ifdef __APPLE__
 
2628
       /*
 
2629
        * If we are going to sleep and the PrintingJobs count is now 0, allow the
 
2630
        * sleep to happen immediately...
 
2631
        */
 
2632
 
 
2633
        if (Sleeping && cupsArrayCount(PrintingJobs) == 0)
 
2634
          cupsdAllowSleep();
 
2635
#endif /* __APPLE__ */
 
2636
 
 
2637
       /*
 
2638
        * Remove any authentication data...
 
2639
        */
 
2640
 
 
2641
        snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
 
2642
        if (cupsdRemoveFile(filename) && errno != ENOENT)
 
2643
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
2644
                          "Unable to remove authentication cache: %s",
 
2645
                          strerror(errno));
 
2646
 
 
2647
        for (i = 0;
 
2648
             i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
 
2649
             i ++)
 
2650
          cupsdClearString(job->auth_env + i);
 
2651
 
 
2652
        cupsdClearString(&job->auth_uid);
 
2653
 
 
2654
       /*
 
2655
        * Remove the print file for good if we aren't preserving jobs or
 
2656
        * files...
 
2657
        */
 
2658
 
 
2659
        if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE)
 
2660
          remove_job_files(job);
 
2661
 
 
2662
        if (JobHistory && action != CUPSD_JOB_PURGE)
 
2663
        {
 
2664
         /*
 
2665
          * Save job state info...
 
2666
          */
 
2667
 
 
2668
          job->dirty = 1;
 
2669
          cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
2670
        }
 
2671
        else if (!job->printer)
 
2672
        {
 
2673
         /*
 
2674
          * Delete the job immediately if not actively printing...
 
2675
          */
 
2676
 
 
2677
          cupsdDeleteJob(job, CUPSD_JOB_PURGE);
 
2678
          job = NULL;
 
2679
        }
 
2680
        break;
 
2681
  }
 
2682
 
 
2683
 /*
 
2684
  * Finalize the job immediately if we forced things...
 
2685
  */
 
2686
 
 
2687
  if (action >= CUPSD_JOB_FORCE && job && job->printer)
 
2688
    finalize_job(job, 0);
 
2689
 
 
2690
 /*
 
2691
  * Update the server "busy" state...
 
2692
  */
 
2693
 
 
2694
  cupsdSetBusyState();
 
2695
}
 
2696
 
 
2697
 
 
2698
/*
 
2699
 * 'cupsdStopAllJobs()' - Stop all print jobs.
 
2700
 */
 
2701
 
 
2702
void
 
2703
cupsdStopAllJobs(
 
2704
    cupsd_jobaction_t action,           /* I - Action */
 
2705
    int               kill_delay)       /* I - Number of seconds before we kill */
 
2706
{
 
2707
  cupsd_job_t   *job;                   /* Current job */
 
2708
 
 
2709
 
 
2710
  DEBUG_puts("cupsdStopAllJobs()");
 
2711
 
 
2712
  for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
 
2713
       job;
 
2714
       job = (cupsd_job_t *)cupsArrayNext(PrintingJobs))
 
2715
  {
 
2716
    if (kill_delay)
 
2717
      job->kill_time = time(NULL) + kill_delay;
 
2718
 
 
2719
    cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL);
 
2720
  }
 
2721
}
 
2722
 
 
2723
 
 
2724
/*
 
2725
 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
 
2726
 */
 
2727
 
 
2728
void
 
2729
cupsdUnloadCompletedJobs(void)
 
2730
{
 
2731
  cupsd_job_t   *job;                   /* Current job */
 
2732
  time_t        expire;                 /* Expiration time */
 
2733
 
 
2734
 
 
2735
  expire = time(NULL) - 60;
 
2736
 
 
2737
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
2738
       job;
 
2739
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
2740
    if (job->attrs && job->state_value >= IPP_JOB_STOPPED && !job->printer &&
 
2741
        job->access_time < expire)
 
2742
    {
 
2743
      if (job->dirty)
 
2744
        cupsdSaveJob(job);
 
2745
 
 
2746
      unload_job(job);
 
2747
    }
 
2748
}
 
2749
 
 
2750
 
 
2751
/*
 
2752
 * 'cupsdUpdateJobs()' - Update the history/file files for all jobs.
 
2753
 */
 
2754
 
 
2755
void
 
2756
cupsdUpdateJobs(void)
 
2757
{
 
2758
  cupsd_job_t           *job;           /* Current job */
 
2759
  time_t                curtime;        /* Current time */
 
2760
  ipp_attribute_t       *attr;          /* time-at-completed attribute */
 
2761
 
 
2762
 
 
2763
  curtime          = time(NULL);
 
2764
  JobHistoryUpdate = 0;
 
2765
 
 
2766
  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
 
2767
       job;
 
2768
       job = (cupsd_job_t *)cupsArrayNext(Jobs))
 
2769
  {
 
2770
    if (job->state_value >= IPP_JOB_CANCELED &&
 
2771
        (attr = ippFindAttribute(job->attrs, "time-at-completed",
 
2772
                                 IPP_TAG_INTEGER)) != NULL)
 
2773
    {
 
2774
     /*
 
2775
      * Update history/file expiration times...
 
2776
      */
 
2777
 
 
2778
      if (JobHistory < INT_MAX)
 
2779
        job->history_time = attr->values[0].integer + JobHistory;
 
2780
      else
 
2781
        job->history_time = INT_MAX;
 
2782
 
 
2783
      if (job->history_time < curtime)
 
2784
      {
 
2785
        cupsdDeleteJob(job, CUPSD_JOB_PURGE);
 
2786
        continue;
 
2787
      }
 
2788
 
 
2789
      if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
 
2790
        JobHistoryUpdate = job->history_time;
 
2791
 
 
2792
      if (JobFiles < INT_MAX)
 
2793
        job->file_time = attr->values[0].integer + JobFiles;
 
2794
      else
 
2795
        job->file_time = INT_MAX;
 
2796
 
 
2797
      if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
 
2798
        JobHistoryUpdate = job->file_time;
 
2799
    }
 
2800
  }
 
2801
 
 
2802
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdUpdateAllJobs: JobHistoryUpdate=%ld",
 
2803
                  (long)JobHistoryUpdate);
 
2804
}
 
2805
 
 
2806
 
 
2807
/*
 
2808
 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
 
2809
 */
 
2810
 
 
2811
static int                              /* O - Difference */
 
2812
compare_active_jobs(void *first,        /* I - First job */
 
2813
                    void *second,       /* I - Second job */
 
2814
                    void *data)         /* I - App data (not used) */
 
2815
{
 
2816
  int   diff;                           /* Difference */
 
2817
 
 
2818
 
 
2819
  (void)data;
 
2820
 
 
2821
  if ((diff = ((cupsd_job_t *)second)->priority -
 
2822
              ((cupsd_job_t *)first)->priority) != 0)
 
2823
    return (diff);
 
2824
  else
 
2825
    return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
 
2826
}
 
2827
 
 
2828
 
 
2829
/*
 
2830
 * 'compare_jobs()' - Compare the job IDs of two jobs.
 
2831
 */
 
2832
 
 
2833
static int                              /* O - Difference */
 
2834
compare_jobs(void *first,               /* I - First job */
 
2835
             void *second,              /* I - Second job */
 
2836
             void *data)                /* I - App data (not used) */
 
2837
{
 
2838
  (void)data;
 
2839
 
 
2840
  return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
 
2841
}
 
2842
 
 
2843
 
 
2844
/*
 
2845
 * 'dump_job_history()' - Dump any debug messages for a job.
 
2846
 */
 
2847
 
 
2848
static void
 
2849
dump_job_history(cupsd_job_t *job)      /* I - Job */
 
2850
{
 
2851
  int                   i,              /* Looping var */
 
2852
                        oldsize;        /* Current MaxLogSize */
 
2853
  struct tm             *date;          /* Date/time value */
 
2854
  cupsd_joblog_t        *message;       /* Current message */
 
2855
  char                  temp[2048],     /* Log message */
 
2856
                        *ptr,           /* Pointer into log message */
 
2857
                        start[256],     /* Start time */
 
2858
                        end[256];       /* End time */
 
2859
  cupsd_printer_t       *printer;       /* Printer for job */
 
2860
 
 
2861
 
 
2862
 /*
 
2863
  * See if we have anything to dump...
 
2864
  */
 
2865
 
 
2866
  if (!job->history)
 
2867
    return;
 
2868
 
 
2869
 /*
 
2870
  * Disable log rotation temporarily...
 
2871
  */
 
2872
 
 
2873
  oldsize    = MaxLogSize;
 
2874
  MaxLogSize = 0;
 
2875
 
 
2876
 /*
 
2877
  * Copy the debug messages to the log...
 
2878
  */
 
2879
 
 
2880
  message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
 
2881
  date = localtime(&(message->time));
 
2882
  strftime(start, sizeof(start), "%X", date);
 
2883
 
 
2884
  message = (cupsd_joblog_t *)cupsArrayLast(job->history);
 
2885
  date = localtime(&(message->time));
 
2886
  strftime(end, sizeof(end), "%X", date);
 
2887
 
 
2888
  snprintf(temp, sizeof(temp),
 
2889
           "[Job %d] The following messages were recorded from %s to %s",
 
2890
           job->id, start, end);
 
2891
  cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
 
2892
 
 
2893
  for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
 
2894
       message;
 
2895
       message = (cupsd_joblog_t *)cupsArrayNext(job->history))
 
2896
    cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message);
 
2897
 
 
2898
  snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id);
 
2899
  cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
 
2900
 
 
2901
 /*
 
2902
  * Log the printer state values...
 
2903
  */
 
2904
 
 
2905
  if ((printer = job->printer) == NULL)
 
2906
    printer = cupsdFindDest(job->dest);
 
2907
 
 
2908
  if (printer)
 
2909
  {
 
2910
    snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id,
 
2911
             printer->state,
 
2912
             printer->state == IPP_PRINTER_IDLE ? "idle" :
 
2913
                 printer->state == IPP_PRINTER_PROCESSING ? "processing" :
 
2914
                 "stopped");
 
2915
    cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
 
2916
 
 
2917
    snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"",
 
2918
             job->id, printer->state_message);
 
2919
    cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
 
2920
 
 
2921
    snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id);
 
2922
    ptr = temp + strlen(temp);
 
2923
    if (printer->num_reasons == 0)
 
2924
      strlcpy(ptr, "none", sizeof(temp) - (ptr - temp));
 
2925
    else
 
2926
    {
 
2927
      for (i = 0;
 
2928
           i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2);
 
2929
           i ++)
 
2930
      {
 
2931
        if (i)
 
2932
          *ptr++ = ',';
 
2933
 
 
2934
        strlcpy(ptr, printer->reasons[i], sizeof(temp) - (ptr - temp));
 
2935
        ptr += strlen(ptr);
 
2936
      }
 
2937
    }
 
2938
    cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
 
2939
  }
 
2940
 
 
2941
 /*
 
2942
  * Restore log file rotation...
 
2943
  */
 
2944
 
 
2945
  MaxLogSize = oldsize;
 
2946
 
 
2947
 /*
 
2948
  * Free all messages...
 
2949
  */
 
2950
 
 
2951
  free_job_history(job);
 
2952
}
 
2953
 
 
2954
 
 
2955
/*
 
2956
 * 'free_job_history()' - Free any log history.
 
2957
 */
 
2958
 
 
2959
static void
 
2960
free_job_history(cupsd_job_t *job)      /* I - Job */
 
2961
{
 
2962
  char  *message;                       /* Current message */
 
2963
 
 
2964
 
 
2965
  if (!job->history)
 
2966
    return;
 
2967
 
 
2968
  for (message = (char *)cupsArrayFirst(job->history);
 
2969
       message;
 
2970
       message = (char *)cupsArrayNext(job->history))
 
2971
    free(message);
 
2972
 
 
2973
  cupsArrayDelete(job->history);
 
2974
  job->history = NULL;
 
2975
}
 
2976
 
 
2977
 
 
2978
/*
 
2979
 * 'finalize_job()' - Cleanup after job filter processes and support data.
 
2980
 */
 
2981
 
 
2982
static void
 
2983
finalize_job(cupsd_job_t *job,          /* I - Job */
 
2984
             int         set_job_state) /* I - 1 = set the job state */
 
2985
{
 
2986
  ipp_pstate_t          printer_state;  /* New printer state value */
 
2987
  ipp_jstate_t          job_state;      /* New job state value */
 
2988
  const char            *message;       /* Message for job state */
 
2989
  char                  buffer[1024];   /* Buffer for formatted messages */
 
2990
 
 
2991
 
 
2992
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
 
2993
 
 
2994
 /*
 
2995
  * Clear the "connecting-to-device" reason, which is only valid when a printer
 
2996
  * is processing, along with any remote printing job state...
 
2997
  */
 
2998
 
 
2999
  cupsdSetPrinterReasons(job->printer, "-connecting-to-device,"
 
3000
                                       "cups-remote-pending,"
 
3001
                                       "cups-remote-pending-held,"
 
3002
                                       "cups-remote-processing,"
 
3003
                                       "cups-remote-stopped,"
 
3004
                                       "cups-remote-canceled,"
 
3005
                                       "cups-remote-aborted,"
 
3006
                                       "cups-remote-completed");
 
3007
 
 
3008
 /*
 
3009
  * Similarly, clear the "offline-report" reason for non-USB devices since we
 
3010
  * rarely have current information for network devices...
 
3011
  */
 
3012
 
 
3013
  if (strncmp(job->printer->device_uri, "usb:", 4) &&
 
3014
      strncmp(job->printer->device_uri, "ippusb:", 7))
 
3015
    cupsdSetPrinterReasons(job->printer, "-offline-report");
 
3016
 
 
3017
 /*
 
3018
  * Free the security profile...
 
3019
  */
 
3020
 
 
3021
  cupsdDestroyProfile(job->profile);
 
3022
  job->profile = NULL;
 
3023
 
 
3024
 /*
 
3025
  * Clear the unresponsive job watchdog timers...
 
3026
  */
 
3027
 
 
3028
  job->cancel_time = 0;
 
3029
  job->kill_time   = 0;
 
3030
 
 
3031
 /*
 
3032
  * Close pipes and status buffer...
 
3033
  */
 
3034
 
 
3035
  cupsdClosePipe(job->print_pipes);
 
3036
  cupsdClosePipe(job->back_pipes);
 
3037
  cupsdClosePipe(job->side_pipes);
 
3038
 
 
3039
  cupsdRemoveSelect(job->status_pipes[0]);
 
3040
  cupsdClosePipe(job->status_pipes);
 
3041
  cupsdStatBufDelete(job->status_buffer);
 
3042
  job->status_buffer = NULL;
 
3043
 
 
3044
 /*
 
3045
  * Process the exit status...
 
3046
  */
 
3047
 
 
3048
  if (job->printer->state == IPP_PRINTER_PROCESSING)
 
3049
    printer_state = IPP_PRINTER_IDLE;
 
3050
  else
 
3051
    printer_state = job->printer->state;
 
3052
 
 
3053
  switch (job_state = job->state_value)
 
3054
  {
 
3055
    case IPP_JOB_PENDING :
 
3056
        message = "Job paused.";
 
3057
        break;
 
3058
 
 
3059
    case IPP_JOB_HELD :
 
3060
        message = "Job held.";
 
3061
        break;
 
3062
 
 
3063
    default :
 
3064
    case IPP_JOB_PROCESSING :
 
3065
    case IPP_JOB_COMPLETED :
 
3066
        job_state = IPP_JOB_COMPLETED;
 
3067
        message   = "Job completed.";
 
3068
 
 
3069
        if (!job->status)
 
3070
          ippSetString(job->attrs, &job->reasons, 0,
 
3071
                       "job-completed-successfully");
 
3072
        break;
 
3073
 
 
3074
    case IPP_JOB_STOPPED :
 
3075
        message = "Job stopped.";
 
3076
 
 
3077
        ippSetString(job->attrs, &job->reasons, 0, "job-stopped");
 
3078
        break;
 
3079
 
 
3080
    case IPP_JOB_CANCELED :
 
3081
        message = "Job canceled.";
 
3082
 
 
3083
        ippSetString(job->attrs, &job->reasons, 0, "job-canceled-by-user");
 
3084
        break;
 
3085
 
 
3086
    case IPP_JOB_ABORTED :
 
3087
        message = "Job aborted.";
 
3088
        break;
 
3089
  }
 
3090
 
 
3091
  if (job->status < 0)
 
3092
  {
 
3093
   /*
 
3094
    * Backend had errors...
 
3095
    */
 
3096
 
 
3097
    int exit_code;                      /* Exit code from backend */
 
3098
 
 
3099
   /*
 
3100
    * Convert the status to an exit code.  Due to the way the W* macros are
 
3101
    * implemented on MacOS X (bug?), we have to store the exit status in a
 
3102
    * variable first and then convert...
 
3103
    */
 
3104
 
 
3105
    exit_code = -job->status;
 
3106
    if (WIFEXITED(exit_code))
 
3107
      exit_code = WEXITSTATUS(exit_code);
 
3108
    else
 
3109
    {
 
3110
      ippSetString(job->attrs, &job->reasons, 0, "cups-backend-crashed");
 
3111
      exit_code = job->status;
 
3112
    }
 
3113
 
 
3114
    cupsdLogJob(job, CUPSD_LOG_INFO, "Backend returned status %d (%s)",
 
3115
                exit_code,
 
3116
                exit_code == CUPS_BACKEND_FAILED ? "failed" :
 
3117
                    exit_code == CUPS_BACKEND_AUTH_REQUIRED ?
 
3118
                        "authentication required" :
 
3119
                    exit_code == CUPS_BACKEND_HOLD ? "hold job" :
 
3120
                    exit_code == CUPS_BACKEND_STOP ? "stop printer" :
 
3121
                    exit_code == CUPS_BACKEND_CANCEL ? "cancel job" :
 
3122
                    exit_code == CUPS_BACKEND_RETRY ? "retry job later" :
 
3123
                    exit_code == CUPS_BACKEND_RETRY_CURRENT ? "retry job immediately" :
 
3124
                    exit_code < 0 ? "crashed" : "unknown");
 
3125
 
 
3126
   /*
 
3127
    * Do what needs to be done...
 
3128
    */
 
3129
 
 
3130
    switch (exit_code)
 
3131
    {
 
3132
      default :
 
3133
      case CUPS_BACKEND_FAILED :
 
3134
         /*
 
3135
          * Backend failure, use the error-policy to determine how to
 
3136
          * act...
 
3137
          */
 
3138
 
 
3139
          if (job->dtype & CUPS_PRINTER_CLASS)
 
3140
          {
 
3141
           /*
 
3142
            * Queued on a class - mark the job as pending and we'll retry on
 
3143
            * another printer...
 
3144
            */
 
3145
 
 
3146
            if (job_state == IPP_JOB_COMPLETED)
 
3147
            {
 
3148
              job_state = IPP_JOB_PENDING;
 
3149
              message   = "Retrying job on another printer.";
 
3150
 
 
3151
              ippSetString(job->attrs, &job->reasons, 0,
 
3152
                           "resources-are-not-ready");
 
3153
            }
 
3154
          }
 
3155
          else if (!strcmp(job->printer->error_policy, "retry-current-job"))
 
3156
          {
 
3157
           /*
 
3158
            * The error policy is "retry-current-job" - mark the job as pending
 
3159
            * and we'll retry on the same printer...
 
3160
            */
 
3161
 
 
3162
            if (job_state == IPP_JOB_COMPLETED)
 
3163
            {
 
3164
              job_state = IPP_JOB_PENDING;
 
3165
              message   = "Retrying job on same printer.";
 
3166
 
 
3167
              ippSetString(job->attrs, &job->reasons, 0, "none");
 
3168
            }
 
3169
          }
 
3170
          else if ((job->printer->type & CUPS_PRINTER_FAX) ||
 
3171
                   !strcmp(job->printer->error_policy, "retry-job"))
 
3172
          {
 
3173
            if (job_state == IPP_JOB_COMPLETED)
 
3174
            {
 
3175
             /*
 
3176
              * The job was queued on a fax or the error policy is "retry-job" -
 
3177
              * hold the job if the number of retries is less than the
 
3178
              * JobRetryLimit, otherwise abort the job.
 
3179
              */
 
3180
 
 
3181
              job->tries ++;
 
3182
 
 
3183
              if (job->tries > JobRetryLimit && JobRetryLimit > 0)
 
3184
              {
 
3185
               /*
 
3186
                * Too many tries...
 
3187
                */
 
3188
 
 
3189
                snprintf(buffer, sizeof(buffer),
 
3190
                         "Job aborted after %d unsuccessful attempts.",
 
3191
                         JobRetryLimit);
 
3192
                job_state = IPP_JOB_ABORTED;
 
3193
                message   = buffer;
 
3194
 
 
3195
                ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
 
3196
              }
 
3197
              else
 
3198
              {
 
3199
               /*
 
3200
                * Try again in N seconds...
 
3201
                */
 
3202
 
 
3203
                snprintf(buffer, sizeof(buffer),
 
3204
                         "Job held for %d seconds since it could not be sent.",
 
3205
                         JobRetryInterval);
 
3206
 
 
3207
                job->hold_until = time(NULL) + JobRetryInterval;
 
3208
                job_state       = IPP_JOB_HELD;
 
3209
                message         = buffer;
 
3210
 
 
3211
                ippSetString(job->attrs, &job->reasons, 0,
 
3212
                             "resources-are-not-ready");
 
3213
              }
 
3214
            }
 
3215
          }
 
3216
          else if (!strcmp(job->printer->error_policy, "abort-job") &&
 
3217
                   job_state == IPP_JOB_COMPLETED)
 
3218
          {
 
3219
            job_state = IPP_JOB_ABORTED;
 
3220
            message   = "Job aborted due to backend errors; please consult "
 
3221
                        "the error_log file for details.";
 
3222
 
 
3223
            ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
 
3224
          }
 
3225
          else if (job->state_value == IPP_JOB_PROCESSING)
 
3226
          {
 
3227
            job_state     = IPP_JOB_PENDING;
 
3228
            printer_state = IPP_PRINTER_STOPPED;
 
3229
            message       = "Printer stopped due to backend errors; please "
 
3230
                            "consult the error_log file for details.";
 
3231
 
 
3232
            ippSetString(job->attrs, &job->reasons, 0, "none");
 
3233
          }
 
3234
          break;
 
3235
 
 
3236
      case CUPS_BACKEND_CANCEL :
 
3237
         /*
 
3238
          * Cancel the job...
 
3239
          */
 
3240
 
 
3241
          if (job_state == IPP_JOB_COMPLETED)
 
3242
          {
 
3243
            job_state = IPP_JOB_CANCELED;
 
3244
            message   = "Job canceled at printer.";
 
3245
 
 
3246
            ippSetString(job->attrs, &job->reasons, 0, "canceled-at-device");
 
3247
          }
 
3248
          break;
 
3249
 
 
3250
      case CUPS_BACKEND_HOLD :
 
3251
          if (job_state == IPP_JOB_COMPLETED)
 
3252
          {
 
3253
           /*
 
3254
            * Hold the job...
 
3255
            */
 
3256
 
 
3257
            const char *reason = ippGetString(job->reasons, 0, NULL);
 
3258
 
 
3259
            cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-state-reasons=\"%s\"",
 
3260
                        reason);
 
3261
 
 
3262
            if (!reason || strncmp(reason, "account-", 8))
 
3263
            {
 
3264
              cupsdSetJobHoldUntil(job, "indefinite", 1);
 
3265
 
 
3266
              ippSetString(job->attrs, &job->reasons, 0,
 
3267
                           "job-hold-until-specified");
 
3268
              message = "Job held indefinitely due to backend errors; please "
 
3269
                        "consult the error_log file for details.";
 
3270
            }
 
3271
            else if (!strcmp(reason, "account-info-needed"))
 
3272
            {
 
3273
              cupsdSetJobHoldUntil(job, "indefinite", 0);
 
3274
 
 
3275
              message = "Job held indefinitely - account information is "
 
3276
                        "required.";
 
3277
            }
 
3278
            else if (!strcmp(reason, "account-closed"))
 
3279
            {
 
3280
              cupsdSetJobHoldUntil(job, "indefinite", 0);
 
3281
 
 
3282
              message = "Job held indefinitely - account has been closed.";
 
3283
            }
 
3284
            else if (!strcmp(reason, "account-limit-reached"))
 
3285
            {
 
3286
              cupsdSetJobHoldUntil(job, "indefinite", 0);
 
3287
 
 
3288
              message = "Job held indefinitely - account limit has been "
 
3289
                        "reached.";
 
3290
            }
 
3291
            else
 
3292
            {
 
3293
              cupsdSetJobHoldUntil(job, "indefinite", 0);
 
3294
 
 
3295
              message = "Job held indefinitely - account authorization failed.";
 
3296
            }
 
3297
 
 
3298
            job_state = IPP_JOB_HELD;
 
3299
          }
 
3300
          break;
 
3301
 
 
3302
      case CUPS_BACKEND_STOP :
 
3303
         /*
 
3304
          * Stop the printer...
 
3305
          */
 
3306
 
 
3307
          printer_state = IPP_PRINTER_STOPPED;
 
3308
          message       = "Printer stopped due to backend errors; please "
 
3309
                          "consult the error_log file for details.";
 
3310
 
 
3311
          if (job_state == IPP_JOB_COMPLETED)
 
3312
          {
 
3313
            job_state = IPP_JOB_PENDING;
 
3314
 
 
3315
            ippSetString(job->attrs, &job->reasons, 0,
 
3316
                         "resources-are-not-ready");
 
3317
          }
 
3318
          break;
 
3319
 
 
3320
      case CUPS_BACKEND_AUTH_REQUIRED :
 
3321
         /*
 
3322
          * Hold the job for authentication...
 
3323
          */
 
3324
 
 
3325
          if (job_state == IPP_JOB_COMPLETED)
 
3326
          {
 
3327
            cupsdSetJobHoldUntil(job, "auth-info-required", 1);
 
3328
 
 
3329
            job_state = IPP_JOB_HELD;
 
3330
            message   = "Job held for authentication.";
 
3331
 
 
3332
            if (strncmp(job->reasons->values[0].string.text, "account-", 8))
 
3333
              ippSetString(job->attrs, &job->reasons, 0,
 
3334
                           "cups-held-for-authentication");
 
3335
          }
 
3336
          break;
 
3337
 
 
3338
      case CUPS_BACKEND_RETRY :
 
3339
          if (job_state == IPP_JOB_COMPLETED)
 
3340
          {
 
3341
           /*
 
3342
            * Hold the job if the number of retries is less than the
 
3343
            * JobRetryLimit, otherwise abort the job.
 
3344
            */
 
3345
 
 
3346
            job->tries ++;
 
3347
 
 
3348
            if (job->tries > JobRetryLimit && JobRetryLimit > 0)
 
3349
            {
 
3350
             /*
 
3351
              * Too many tries...
 
3352
              */
 
3353
 
 
3354
              snprintf(buffer, sizeof(buffer),
 
3355
                       "Job aborted after %d unsuccessful attempts.",
 
3356
                       JobRetryLimit);
 
3357
              job_state = IPP_JOB_ABORTED;
 
3358
              message   = buffer;
 
3359
 
 
3360
              ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
 
3361
            }
 
3362
            else
 
3363
            {
 
3364
             /*
 
3365
              * Try again in N seconds...
 
3366
              */
 
3367
 
 
3368
              snprintf(buffer, sizeof(buffer),
 
3369
                       "Job held for %d seconds since it could not be sent.",
 
3370
                       JobRetryInterval);
 
3371
 
 
3372
              job->hold_until = time(NULL) + JobRetryInterval;
 
3373
              job_state       = IPP_JOB_HELD;
 
3374
              message         = buffer;
 
3375
 
 
3376
              ippSetString(job->attrs, &job->reasons, 0,
 
3377
                           "resources-are-not-ready");
 
3378
            }
 
3379
          }
 
3380
          break;
 
3381
 
 
3382
      case CUPS_BACKEND_RETRY_CURRENT :
 
3383
         /*
 
3384
          * Mark the job as pending and retry on the same printer...
 
3385
          */
 
3386
 
 
3387
          if (job_state == IPP_JOB_COMPLETED)
 
3388
          {
 
3389
            job_state = IPP_JOB_PENDING;
 
3390
            message   = "Retrying job on same printer.";
 
3391
 
 
3392
            ippSetString(job->attrs, &job->reasons, 0, "none");
 
3393
          }
 
3394
          break;
 
3395
    }
 
3396
  }
 
3397
  else if (job->status > 0)
 
3398
  {
 
3399
   /*
 
3400
    * Filter had errors; stop job...
 
3401
    */
 
3402
 
 
3403
    if (job_state == IPP_JOB_COMPLETED)
 
3404
    {
 
3405
      job_state = IPP_JOB_STOPPED;
 
3406
      message   = "Job stopped due to filter errors; please consult the "
 
3407
                  "error_log file for details.";
 
3408
 
 
3409
      if (WIFSIGNALED(job->status))
 
3410
        ippSetString(job->attrs, &job->reasons, 0, "cups-filter-crashed");
 
3411
      else
 
3412
        ippSetString(job->attrs, &job->reasons, 0, "job-completed-with-errors");
 
3413
    }
 
3414
  }
 
3415
 
 
3416
 /*
 
3417
  * Update the printer and job state.
 
3418
  */
 
3419
 
 
3420
  if (set_job_state && job_state != job->state_value)
 
3421
    cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
 
3422
 
 
3423
  cupsdSetPrinterState(job->printer, printer_state,
 
3424
                       printer_state == IPP_PRINTER_STOPPED);
 
3425
  update_job_attrs(job, 0);
 
3426
 
 
3427
  if (job->history)
 
3428
  {
 
3429
    if (job->status &&
 
3430
        (job->state_value == IPP_JOB_ABORTED ||
 
3431
         job->state_value == IPP_JOB_STOPPED))
 
3432
      dump_job_history(job);
 
3433
    else
 
3434
      free_job_history(job);
 
3435
  }
 
3436
 
 
3437
  cupsArrayRemove(PrintingJobs, job);
 
3438
 
 
3439
 /*
 
3440
  * Apply any PPD updates...
 
3441
  */
 
3442
 
 
3443
  if (job->num_keywords)
 
3444
  {
 
3445
    if (cupsdUpdatePrinterPPD(job->printer, job->num_keywords, job->keywords))
 
3446
      cupsdSetPrinterAttrs(job->printer);
 
3447
 
 
3448
    cupsFreeOptions(job->num_keywords, job->keywords);
 
3449
 
 
3450
    job->num_keywords = 0;
 
3451
    job->keywords     = NULL;
 
3452
  }
 
3453
 
 
3454
 /*
 
3455
  * Clear the printer <-> job association...
 
3456
  */
 
3457
 
 
3458
  job->printer->job = NULL;
 
3459
  job->printer      = NULL;
 
3460
 
 
3461
 /*
 
3462
  * Try printing another job...
 
3463
  */
 
3464
 
 
3465
  if (printer_state != IPP_PRINTER_STOPPED)
 
3466
    cupsdCheckJobs();
 
3467
}
 
3468
 
 
3469
 
 
3470
/*
 
3471
 * 'get_options()' - Get a string containing the job options.
 
3472
 */
 
3473
 
 
3474
static char *                           /* O - Options string */
 
3475
get_options(cupsd_job_t *job,           /* I - Job */
 
3476
            int         banner_page,    /* I - Printing a banner page? */
 
3477
            char        *copies,        /* I - Copies buffer */
 
3478
            size_t      copies_size,    /* I - Size of copies buffer */
 
3479
            char        *title,         /* I - Title buffer */
 
3480
            size_t      title_size)     /* I - Size of title buffer */
 
3481
{
 
3482
  int                   i;              /* Looping var */
 
3483
  size_t                newlength;      /* New option buffer length */
 
3484
  char                  *optptr,        /* Pointer to options */
 
3485
                        *valptr;        /* Pointer in value string */
 
3486
  ipp_attribute_t       *attr;          /* Current attribute */
 
3487
  _ppd_cache_t          *pc;            /* PPD cache and mapping data */
 
3488
  int                   num_pwgppds;    /* Number of PWG->PPD options */
 
3489
  cups_option_t         *pwgppds,       /* PWG->PPD options */
 
3490
                        *pwgppd,        /* Current PWG->PPD option */
 
3491
                        *preset;        /* Current preset option */
 
3492
  int                   print_color_mode,
 
3493
                                        /* Output mode (if any) */
 
3494
                        print_quality;  /* Print quality (if any) */
 
3495
  const char            *ppd;           /* PPD option choice */
 
3496
  int                   exact;          /* Did we get an exact match? */
 
3497
  static char           *options = NULL;/* Full list of options */
 
3498
  static size_t         optlength = 0;  /* Length of option buffer */
 
3499
 
 
3500
 
 
3501
 /*
 
3502
  * Building the options string is harder than it needs to be, but for the
 
3503
  * moment we need to pass strings for command-line args and not IPP attribute
 
3504
  * pointers... :)
 
3505
  *
 
3506
  * First build an options array for any PWG->PPD mapped option/choice pairs.
 
3507
  */
 
3508
 
 
3509
  pc          = job->printer->pc;
 
3510
  num_pwgppds = 0;
 
3511
  pwgppds     = NULL;
 
3512
 
 
3513
  if (pc &&
 
3514
      !ippFindAttribute(job->attrs,
 
3515
                        "com.apple.print.DocumentTicket.PMSpoolFormat",
 
3516
                        IPP_TAG_ZERO) &&
 
3517
      !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
 
3518
      (ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) ||
 
3519
       ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO)))
 
3520
  {
 
3521
   /*
 
3522
    * Map print-color-mode and print-quality to a preset...
 
3523
    */
 
3524
 
 
3525
    if ((attr = ippFindAttribute(job->attrs, "print-color-mode",
 
3526
                                 IPP_TAG_KEYWORD)) != NULL &&
 
3527
        !strcmp(attr->values[0].string.text, "monochrome"))
 
3528
      print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
 
3529
    else
 
3530
      print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
 
3531
 
 
3532
    if ((attr = ippFindAttribute(job->attrs, "print-quality",
 
3533
                                 IPP_TAG_ENUM)) != NULL &&
 
3534
        attr->values[0].integer >= IPP_QUALITY_DRAFT &&
 
3535
        attr->values[0].integer <= IPP_QUALITY_HIGH)
 
3536
      print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT;
 
3537
    else
 
3538
      print_quality = _PWG_PRINT_QUALITY_NORMAL;
 
3539
 
 
3540
    if (pc->num_presets[print_color_mode][print_quality] == 0)
 
3541
    {
 
3542
     /*
 
3543
      * Try to find a preset that works so that we maximize the chances of us
 
3544
      * getting a good print using IPP attributes.
 
3545
      */
 
3546
 
 
3547
      if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0)
 
3548
        print_quality = _PWG_PRINT_QUALITY_NORMAL;
 
3549
      else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0)
 
3550
        print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
 
3551
      else
 
3552
      {
 
3553
        print_quality    = _PWG_PRINT_QUALITY_NORMAL;
 
3554
        print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
 
3555
      }
 
3556
    }
 
3557
 
 
3558
    if (pc->num_presets[print_color_mode][print_quality] > 0)
 
3559
    {
 
3560
     /*
 
3561
      * Copy the preset options as long as the corresponding names are not
 
3562
      * already defined in the IPP request...
 
3563
      */
 
3564
 
 
3565
      for (i = pc->num_presets[print_color_mode][print_quality],
 
3566
               preset = pc->presets[print_color_mode][print_quality];
 
3567
           i > 0;
 
3568
           i --, preset ++)
 
3569
      {
 
3570
        if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
 
3571
          num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds,
 
3572
                                      &pwgppds);
 
3573
      }
 
3574
    }
 
3575
  }
 
3576
 
 
3577
  if (pc)
 
3578
  {
 
3579
    if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) &&
 
3580
        !ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO))
 
3581
    {
 
3582
      if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL)
 
3583
        num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds,
 
3584
                                    &pwgppds);
 
3585
    }
 
3586
    if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) &&
 
3587
        (ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL)
 
3588
      num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds);
 
3589
 
 
3590
    if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) &&
 
3591
        !ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) &&
 
3592
        (ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL)
 
3593
    {
 
3594
      num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds);
 
3595
 
 
3596
      if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO))
 
3597
        num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds);
 
3598
    }
 
3599
 
 
3600
    if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) &&
 
3601
        (attr = ippFindAttribute(job->attrs, "output-bin",
 
3602
                                 IPP_TAG_ZERO)) != NULL &&
 
3603
        (attr->value_tag == IPP_TAG_KEYWORD ||
 
3604
         attr->value_tag == IPP_TAG_NAME) &&
 
3605
        (ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL)
 
3606
    {
 
3607
     /*
 
3608
      * Map output-bin to OutputBin option...
 
3609
      */
 
3610
 
 
3611
      num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds);
 
3612
    }
 
3613
 
 
3614
    if (pc->sides_option &&
 
3615
        !ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) &&
 
3616
        (attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL)
 
3617
    {
 
3618
     /*
 
3619
      * Map sides to duplex option...
 
3620
      */
 
3621
 
 
3622
      if (!strcmp(attr->values[0].string.text, "one-sided"))
 
3623
        num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided,
 
3624
                                    num_pwgppds, &pwgppds);
 
3625
      else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge"))
 
3626
        num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long,
 
3627
                                    num_pwgppds, &pwgppds);
 
3628
      else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge"))
 
3629
        num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short,
 
3630
                                    num_pwgppds, &pwgppds);
 
3631
    }
 
3632
 
 
3633
   /*
 
3634
    * Map finishings values...
 
3635
    */
 
3636
 
 
3637
    num_pwgppds = _ppdCacheGetFinishingOptions(pc, job->attrs,
 
3638
                                               IPP_FINISHINGS_NONE, num_pwgppds,
 
3639
                                               &pwgppds);
 
3640
  }
 
3641
 
 
3642
 /*
 
3643
  * Figure out how much room we need...
 
3644
  */
 
3645
 
 
3646
  newlength = ipp_length(job->attrs);
 
3647
 
 
3648
  for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
 
3649
    newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value);
 
3650
 
 
3651
 /*
 
3652
  * Then allocate/reallocate the option buffer as needed...
 
3653
  */
 
3654
 
 
3655
  if (newlength == 0)                   /* This can never happen, but Clang */
 
3656
    newlength = 1;                      /* thinks it can... */
 
3657
 
 
3658
  if (newlength > optlength || !options)
 
3659
  {
 
3660
    if (!options)
 
3661
      optptr = malloc(newlength);
 
3662
    else
 
3663
      optptr = realloc(options, newlength);
 
3664
 
 
3665
    if (!optptr)
 
3666
    {
 
3667
      cupsdLogJob(job, CUPSD_LOG_CRIT,
 
3668
                  "Unable to allocate " CUPS_LLFMT " bytes for option buffer.",
 
3669
                  CUPS_LLCAST newlength);
 
3670
      return (NULL);
 
3671
    }
 
3672
 
 
3673
    options   = optptr;
 
3674
    optlength = newlength;
 
3675
  }
 
3676
 
 
3677
 /*
 
3678
  * Now loop through the attributes and convert them to the textual
 
3679
  * representation used by the filters...
 
3680
  */
 
3681
 
 
3682
  optptr  = options;
 
3683
  *optptr = '\0';
 
3684
 
 
3685
  snprintf(title, title_size, "%s-%d", job->printer->name, job->id);
 
3686
  strlcpy(copies, "1", copies_size);
 
3687
 
 
3688
  for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
 
3689
  {
 
3690
    if (!strcmp(attr->name, "copies") &&
 
3691
        attr->value_tag == IPP_TAG_INTEGER)
 
3692
    {
 
3693
     /*
 
3694
      * Don't use the # copies attribute if we are printing the job sheets...
 
3695
      */
 
3696
 
 
3697
      if (!banner_page)
 
3698
        snprintf(copies, copies_size, "%d", attr->values[0].integer);
 
3699
    }
 
3700
    else if (!strcmp(attr->name, "job-name") &&
 
3701
             (attr->value_tag == IPP_TAG_NAME ||
 
3702
              attr->value_tag == IPP_TAG_NAMELANG))
 
3703
      strlcpy(title, attr->values[0].string.text, title_size);
 
3704
    else if (attr->group_tag == IPP_TAG_JOB)
 
3705
    {
 
3706
     /*
 
3707
      * Filter out other unwanted attributes...
 
3708
      */
 
3709
 
 
3710
      if (attr->value_tag == IPP_TAG_NOVALUE ||
 
3711
          attr->value_tag == IPP_TAG_MIMETYPE ||
 
3712
          attr->value_tag == IPP_TAG_NAMELANG ||
 
3713
          attr->value_tag == IPP_TAG_TEXTLANG ||
 
3714
          (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid") &&
 
3715
           strcmp(attr->name, "job-authorization-uri")) ||
 
3716
          attr->value_tag == IPP_TAG_URISCHEME ||
 
3717
          attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
 
3718
        continue;
 
3719
 
 
3720
      if (!strcmp(attr->name, "job-hold-until") ||
 
3721
          !strcmp(attr->name, "job-id") ||
 
3722
          !strcmp(attr->name, "job-k-octets") ||
 
3723
          !strcmp(attr->name, "job-media-sheets") ||
 
3724
          !strcmp(attr->name, "job-media-sheets-completed") ||
 
3725
          !strcmp(attr->name, "job-state") ||
 
3726
          !strcmp(attr->name, "job-state-reasons"))
 
3727
        continue;
 
3728
 
 
3729
      if (!strncmp(attr->name, "job-", 4) &&
 
3730
          strcmp(attr->name, "job-account-id") &&
 
3731
          strcmp(attr->name, "job-accounting-user-id") &&
 
3732
          strcmp(attr->name, "job-authorization-uri") &&
 
3733
          strcmp(attr->name, "job-billing") &&
 
3734
          strcmp(attr->name, "job-impressions") &&
 
3735
          strcmp(attr->name, "job-originating-host-name") &&
 
3736
          strcmp(attr->name, "job-password") &&
 
3737
          strcmp(attr->name, "job-password-encryption") &&
 
3738
          strcmp(attr->name, "job-uuid") &&
 
3739
          !(job->printer->type & CUPS_PRINTER_REMOTE))
 
3740
        continue;
 
3741
 
 
3742
      if ((!strcmp(attr->name, "job-impressions") ||
 
3743
           !strcmp(attr->name, "page-label") ||
 
3744
           !strcmp(attr->name, "page-border") ||
 
3745
           !strncmp(attr->name, "number-up", 9) ||
 
3746
           !strcmp(attr->name, "page-ranges") ||
 
3747
           !strcmp(attr->name, "page-set") ||
 
3748
           !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
 
3749
           !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
 
3750
           !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
 
3751
                                   "PMTotalSidesImaged..n.") ||
 
3752
           !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
 
3753
                                   "PMTotalBeginPages..n.")) &&
 
3754
          banner_page)
 
3755
        continue;
 
3756
 
 
3757
     /*
 
3758
      * Otherwise add them to the list...
 
3759
      */
 
3760
 
 
3761
      if (optptr > options)
 
3762
        strlcat(optptr, " ", optlength - (optptr - options));
 
3763
 
 
3764
      if (attr->value_tag != IPP_TAG_BOOLEAN)
 
3765
      {
 
3766
        strlcat(optptr, attr->name, optlength - (optptr - options));
 
3767
        strlcat(optptr, "=", optlength - (optptr - options));
 
3768
      }
 
3769
 
 
3770
      for (i = 0; i < attr->num_values; i ++)
 
3771
      {
 
3772
        if (i)
 
3773
          strlcat(optptr, ",", optlength - (optptr - options));
 
3774
 
 
3775
        optptr += strlen(optptr);
 
3776
 
 
3777
        switch (attr->value_tag)
 
3778
        {
 
3779
          case IPP_TAG_INTEGER :
 
3780
          case IPP_TAG_ENUM :
 
3781
              snprintf(optptr, optlength - (optptr - options),
 
3782
                       "%d", attr->values[i].integer);
 
3783
              break;
 
3784
 
 
3785
          case IPP_TAG_BOOLEAN :
 
3786
              if (!attr->values[i].boolean)
 
3787
                strlcat(optptr, "no", optlength - (optptr - options));
 
3788
 
 
3789
              strlcat(optptr, attr->name,
 
3790
                      optlength - (optptr - options));
 
3791
              break;
 
3792
 
 
3793
          case IPP_TAG_RANGE :
 
3794
              if (attr->values[i].range.lower == attr->values[i].range.upper)
 
3795
                snprintf(optptr, optlength - (optptr - options) - 1,
 
3796
                         "%d", attr->values[i].range.lower);
 
3797
              else
 
3798
                snprintf(optptr, optlength - (optptr - options) - 1,
 
3799
                         "%d-%d", attr->values[i].range.lower,
 
3800
                         attr->values[i].range.upper);
 
3801
              break;
 
3802
 
 
3803
          case IPP_TAG_RESOLUTION :
 
3804
              snprintf(optptr, optlength - (optptr - options) - 1,
 
3805
                       "%dx%d%s", attr->values[i].resolution.xres,
 
3806
                       attr->values[i].resolution.yres,
 
3807
                       attr->values[i].resolution.units == IPP_RES_PER_INCH ?
 
3808
                           "dpi" : "dpcm");
 
3809
              break;
 
3810
 
 
3811
          case IPP_TAG_STRING :
 
3812
          case IPP_TAG_TEXT :
 
3813
          case IPP_TAG_NAME :
 
3814
          case IPP_TAG_KEYWORD :
 
3815
          case IPP_TAG_CHARSET :
 
3816
          case IPP_TAG_LANGUAGE :
 
3817
          case IPP_TAG_URI :
 
3818
              for (valptr = attr->values[i].string.text; *valptr;)
 
3819
              {
 
3820
                if (strchr(" \t\n\\\'\"", *valptr))
 
3821
                  *optptr++ = '\\';
 
3822
                *optptr++ = *valptr++;
 
3823
              }
 
3824
 
 
3825
              *optptr = '\0';
 
3826
              break;
 
3827
 
 
3828
          default :
 
3829
              break; /* anti-compiler-warning-code */
 
3830
        }
 
3831
      }
 
3832
 
 
3833
      optptr += strlen(optptr);
 
3834
    }
 
3835
  }
 
3836
 
 
3837
 /*
 
3838
  * Finally loop through the PWG->PPD mapped options and add them...
 
3839
  */
 
3840
 
 
3841
  for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
 
3842
  {
 
3843
    *optptr++ = ' ';
 
3844
    strlcpy(optptr, pwgppd->name, optlength - (optptr - options));
 
3845
    optptr += strlen(optptr);
 
3846
    *optptr++ = '=';
 
3847
    strlcpy(optptr, pwgppd->value, optlength - (optptr - options));
 
3848
    optptr += strlen(optptr);
 
3849
  }
 
3850
 
 
3851
  cupsFreeOptions(num_pwgppds, pwgppds);
 
3852
 
 
3853
 /*
 
3854
  * Return the options string...
 
3855
  */
 
3856
 
 
3857
  return (options);
 
3858
}
 
3859
 
 
3860
 
 
3861
/*
 
3862
 * 'ipp_length()' - Compute the size of the buffer needed to hold
 
3863
 *                  the textual IPP attributes.
 
3864
 */
 
3865
 
 
3866
static size_t                           /* O - Size of attribute buffer */
 
3867
ipp_length(ipp_t *ipp)                  /* I - IPP request */
 
3868
{
 
3869
  size_t                bytes;          /* Number of bytes */
 
3870
  int                   i;              /* Looping var */
 
3871
  ipp_attribute_t       *attr;          /* Current attribute */
 
3872
 
 
3873
 
 
3874
 /*
 
3875
  * Loop through all attributes...
 
3876
  */
 
3877
 
 
3878
  bytes = 0;
 
3879
 
 
3880
  for (attr = ipp->attrs; attr != NULL; attr = attr->next)
 
3881
  {
 
3882
   /*
 
3883
    * Skip attributes that won't be sent to filters...
 
3884
    */
 
3885
 
 
3886
    if (attr->value_tag == IPP_TAG_NOVALUE ||
 
3887
        attr->value_tag == IPP_TAG_MIMETYPE ||
 
3888
        attr->value_tag == IPP_TAG_NAMELANG ||
 
3889
        attr->value_tag == IPP_TAG_TEXTLANG ||
 
3890
        attr->value_tag == IPP_TAG_URI ||
 
3891
        attr->value_tag == IPP_TAG_URISCHEME)
 
3892
      continue;
 
3893
 
 
3894
   /*
 
3895
    * Add space for a leading space and commas between each value.
 
3896
    * For the first attribute, the leading space isn't used, so the
 
3897
    * extra byte can be used as the nul terminator...
 
3898
    */
 
3899
 
 
3900
    bytes ++;                           /* " " separator */
 
3901
    bytes += attr->num_values;          /* "," separators */
 
3902
 
 
3903
   /*
 
3904
    * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
 
3905
    * other attributes appear as "foo=value1,value2,...,valueN".
 
3906
    */
 
3907
 
 
3908
    if (attr->value_tag != IPP_TAG_BOOLEAN)
 
3909
      bytes += strlen(attr->name);
 
3910
    else
 
3911
      bytes += attr->num_values * strlen(attr->name);
 
3912
 
 
3913
   /*
 
3914
    * Now add the size required for each value in the attribute...
 
3915
    */
 
3916
 
 
3917
    switch (attr->value_tag)
 
3918
    {
 
3919
      case IPP_TAG_INTEGER :
 
3920
      case IPP_TAG_ENUM :
 
3921
         /*
 
3922
          * Minimum value of a signed integer is -2147483647, or 11 digits.
 
3923
          */
 
3924
 
 
3925
          bytes += attr->num_values * 11;
 
3926
          break;
 
3927
 
 
3928
      case IPP_TAG_BOOLEAN :
 
3929
         /*
 
3930
          * Add two bytes for each false ("no") value...
 
3931
          */
 
3932
 
 
3933
          for (i = 0; i < attr->num_values; i ++)
 
3934
            if (!attr->values[i].boolean)
 
3935
              bytes += 2;
 
3936
          break;
 
3937
 
 
3938
      case IPP_TAG_RANGE :
 
3939
         /*
 
3940
          * A range is two signed integers separated by a hyphen, or
 
3941
          * 23 characters max.
 
3942
          */
 
3943
 
 
3944
          bytes += attr->num_values * 23;
 
3945
          break;
 
3946
 
 
3947
      case IPP_TAG_RESOLUTION :
 
3948
         /*
 
3949
          * A resolution is two signed integers separated by an "x" and
 
3950
          * suffixed by the units, or 26 characters max.
 
3951
          */
 
3952
 
 
3953
          bytes += attr->num_values * 26;
 
3954
          break;
 
3955
 
 
3956
      case IPP_TAG_STRING :
 
3957
      case IPP_TAG_TEXT :
 
3958
      case IPP_TAG_NAME :
 
3959
      case IPP_TAG_KEYWORD :
 
3960
      case IPP_TAG_CHARSET :
 
3961
      case IPP_TAG_LANGUAGE :
 
3962
      case IPP_TAG_URI :
 
3963
         /*
 
3964
          * Strings can contain characters that need quoting.  We need
 
3965
          * at least 2 * len + 2 characters to cover the quotes and
 
3966
          * any backslashes in the string.
 
3967
          */
 
3968
 
 
3969
          for (i = 0; i < attr->num_values; i ++)
 
3970
            bytes += 2 * strlen(attr->values[i].string.text) + 2;
 
3971
          break;
 
3972
 
 
3973
       default :
 
3974
          break; /* anti-compiler-warning-code */
 
3975
    }
 
3976
  }
 
3977
 
 
3978
  return (bytes);
 
3979
}
 
3980
 
 
3981
 
 
3982
/*
 
3983
 * 'load_job_cache()' - Load jobs from the job.cache file.
 
3984
 */
 
3985
 
 
3986
static void
 
3987
load_job_cache(const char *filename)    /* I - job.cache filename */
 
3988
{
 
3989
  cups_file_t   *fp;                    /* job.cache file */
 
3990
  char          line[1024],             /* Line buffer */
 
3991
                *value;                 /* Value on line */
 
3992
  int           linenum;                /* Line number in file */
 
3993
  cupsd_job_t   *job;                   /* Current job */
 
3994
  int           jobid;                  /* Job ID */
 
3995
  char          jobfile[1024];          /* Job filename */
 
3996
 
 
3997
 
 
3998
 /*
 
3999
  * Open the job.cache file...
 
4000
  */
 
4001
 
 
4002
  if ((fp = cupsdOpenConfFile(filename)) == NULL)
 
4003
  {
 
4004
    load_request_root();
 
4005
    return;
 
4006
  }
 
4007
 
 
4008
 /*
 
4009
  * Read entries from the job cache file and create jobs as needed.
 
4010
  */
 
4011
 
 
4012
  cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...",
 
4013
                  filename);
 
4014
 
 
4015
  linenum = 0;
 
4016
  job     = NULL;
 
4017
 
 
4018
  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
 
4019
  {
 
4020
    if (!_cups_strcasecmp(line, "NextJobId"))
 
4021
    {
 
4022
      if (value)
 
4023
        NextJobId = atoi(value);
 
4024
    }
 
4025
    else if (!_cups_strcasecmp(line, "<Job"))
 
4026
    {
 
4027
      if (job)
 
4028
      {
 
4029
        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d.",
 
4030
                        linenum);
 
4031
        continue;
 
4032
      }
 
4033
 
 
4034
      if (!value)
 
4035
      {
 
4036
        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d.", linenum);
 
4037
        continue;
 
4038
      }
 
4039
 
 
4040
      jobid = atoi(value);
 
4041
 
 
4042
      if (jobid < 1)
 
4043
      {
 
4044
        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d.", jobid,
 
4045
                        linenum);
 
4046
        continue;
 
4047
      }
 
4048
 
 
4049
      snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
 
4050
      if (access(jobfile, 0))
 
4051
      {
 
4052
        snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", RequestRoot, jobid);
 
4053
        if (access(jobfile, 0))
 
4054
        {
 
4055
          cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away.",
 
4056
                          jobid);
 
4057
          continue;
 
4058
        }
 
4059
      }
 
4060
 
 
4061
      job = calloc(1, sizeof(cupsd_job_t));
 
4062
      if (!job)
 
4063
      {
 
4064
        cupsdLogMessage(CUPSD_LOG_EMERG,
 
4065
                        "[Job %d] Unable to allocate memory for job.", jobid);
 
4066
        break;
 
4067
      }
 
4068
 
 
4069
      job->id              = jobid;
 
4070
      job->back_pipes[0]   = -1;
 
4071
      job->back_pipes[1]   = -1;
 
4072
      job->print_pipes[0]  = -1;
 
4073
      job->print_pipes[1]  = -1;
 
4074
      job->side_pipes[0]   = -1;
 
4075
      job->side_pipes[1]   = -1;
 
4076
      job->status_pipes[0] = -1;
 
4077
      job->status_pipes[1] = -1;
 
4078
 
 
4079
      cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading from cache...",
 
4080
                      job->id);
 
4081
    }
 
4082
    else if (!job)
 
4083
    {
 
4084
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
4085
                      "Missing <Job #> directive on line %d.", linenum);
 
4086
      continue;
 
4087
    }
 
4088
    else if (!_cups_strcasecmp(line, "</Job>"))
 
4089
    {
 
4090
      cupsArrayAdd(Jobs, job);
 
4091
 
 
4092
      if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
 
4093
        cupsArrayAdd(ActiveJobs, job);
 
4094
 
 
4095
      job = NULL;
 
4096
    }
 
4097
    else if (!value)
 
4098
    {
 
4099
      cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d.", linenum);
 
4100
      continue;
 
4101
    }
 
4102
    else if (!_cups_strcasecmp(line, "State"))
 
4103
    {
 
4104
      job->state_value = (ipp_jstate_t)atoi(value);
 
4105
 
 
4106
      if (job->state_value < IPP_JOB_PENDING)
 
4107
        job->state_value = IPP_JOB_PENDING;
 
4108
      else if (job->state_value > IPP_JOB_COMPLETED)
 
4109
        job->state_value = IPP_JOB_COMPLETED;
 
4110
    }
 
4111
    else if (!_cups_strcasecmp(line, "HoldUntil"))
 
4112
    {
 
4113
      job->hold_until = atoi(value);
 
4114
    }
 
4115
    else if (!_cups_strcasecmp(line, "Priority"))
 
4116
    {
 
4117
      job->priority = atoi(value);
 
4118
    }
 
4119
    else if (!_cups_strcasecmp(line, "Username"))
 
4120
    {
 
4121
      cupsdSetString(&job->username, value);
 
4122
    }
 
4123
    else if (!_cups_strcasecmp(line, "Destination"))
 
4124
    {
 
4125
      cupsdSetString(&job->dest, value);
 
4126
    }
 
4127
    else if (!_cups_strcasecmp(line, "DestType"))
 
4128
    {
 
4129
      job->dtype = (cups_ptype_t)atoi(value);
 
4130
    }
 
4131
    else if (!_cups_strcasecmp(line, "NumFiles"))
 
4132
    {
 
4133
      job->num_files = atoi(value);
 
4134
 
 
4135
      if (job->num_files < 0)
 
4136
      {
 
4137
        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d.",
 
4138
                        job->num_files, linenum);
 
4139
        job->num_files = 0;
 
4140
        continue;
 
4141
      }
 
4142
 
 
4143
      if (job->num_files > 0)
 
4144
      {
 
4145
        snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot,
 
4146
                 job->id);
 
4147
        if (access(jobfile, 0))
 
4148
        {
 
4149
          cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Data files have gone away.",
 
4150
                          job->id);
 
4151
          job->num_files = 0;
 
4152
          continue;
 
4153
        }
 
4154
 
 
4155
        job->filetypes    = calloc(job->num_files, sizeof(mime_type_t *));
 
4156
        job->compressions = calloc(job->num_files, sizeof(int));
 
4157
 
 
4158
        if (!job->filetypes || !job->compressions)
 
4159
        {
 
4160
          cupsdLogMessage(CUPSD_LOG_EMERG,
 
4161
                          "[Job %d] Unable to allocate memory for %d files.",
 
4162
                          job->id, job->num_files);
 
4163
          break;
 
4164
        }
 
4165
      }
 
4166
    }
 
4167
    else if (!_cups_strcasecmp(line, "File"))
 
4168
    {
 
4169
      int       number,                 /* File number */
 
4170
                compression;            /* Compression value */
 
4171
      char      super[MIME_MAX_SUPER],  /* MIME super type */
 
4172
                type[MIME_MAX_TYPE];    /* MIME type */
 
4173
 
 
4174
 
 
4175
      if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
 
4176
                 &compression) != 4)
 
4177
      {
 
4178
        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d.", linenum);
 
4179
        continue;
 
4180
      }
 
4181
 
 
4182
      if (number < 1 || number > job->num_files)
 
4183
      {
 
4184
        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d.",
 
4185
                        number, linenum);
 
4186
        continue;
 
4187
      }
 
4188
 
 
4189
      number --;
 
4190
 
 
4191
      job->compressions[number] = compression;
 
4192
      job->filetypes[number]    = mimeType(MimeDatabase, super, type);
 
4193
 
 
4194
      if (!job->filetypes[number])
 
4195
      {
 
4196
       /*
 
4197
        * If the original MIME type is unknown, auto-type it!
 
4198
        */
 
4199
 
 
4200
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
4201
                        "[Job %d] Unknown MIME type %s/%s for file %d.",
 
4202
                        job->id, super, type, number + 1);
 
4203
 
 
4204
        snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
 
4205
                 job->id, number + 1);
 
4206
        job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL,
 
4207
                                              job->compressions + number);
 
4208
 
 
4209
       /*
 
4210
        * If that didn't work, assume it is raw...
 
4211
        */
 
4212
 
 
4213
        if (!job->filetypes[number])
 
4214
          job->filetypes[number] = mimeType(MimeDatabase, "application",
 
4215
                                            "vnd.cups-raw");
 
4216
      }
 
4217
    }
 
4218
    else
 
4219
      cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d.",
 
4220
                      line, linenum);
 
4221
  }
 
4222
 
 
4223
  if (job)
 
4224
  {
 
4225
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
4226
                    "Missing </Job> directive on line %d.", linenum);
 
4227
    cupsdDeleteJob(job, CUPSD_JOB_PURGE);
 
4228
  }
 
4229
 
 
4230
  cupsFileClose(fp);
 
4231
}
 
4232
 
 
4233
 
 
4234
/*
 
4235
 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
 
4236
 */
 
4237
 
 
4238
static void
 
4239
load_next_job_id(const char *filename)  /* I - job.cache filename */
 
4240
{
 
4241
  cups_file_t   *fp;                    /* job.cache file */
 
4242
  char          line[1024],             /* Line buffer */
 
4243
                *value;                 /* Value on line */
 
4244
  int           linenum;                /* Line number in file */
 
4245
  int           next_job_id;            /* NextJobId value from line */
 
4246
 
 
4247
 
 
4248
 /*
 
4249
  * Read the NextJobId directive from the job.cache file and use
 
4250
  * the value (if any).
 
4251
  */
 
4252
 
 
4253
  if ((fp = cupsFileOpen(filename, "r")) == NULL)
 
4254
  {
 
4255
    if (errno != ENOENT)
 
4256
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
4257
                      "Unable to open job cache file \"%s\": %s",
 
4258
                      filename, strerror(errno));
 
4259
 
 
4260
    return;
 
4261
  }
 
4262
 
 
4263
  cupsdLogMessage(CUPSD_LOG_INFO,
 
4264
                  "Loading NextJobId from job cache file \"%s\"...", filename);
 
4265
 
 
4266
  linenum = 0;
 
4267
 
 
4268
  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
 
4269
  {
 
4270
    if (!_cups_strcasecmp(line, "NextJobId"))
 
4271
    {
 
4272
      if (value)
 
4273
      {
 
4274
        next_job_id = atoi(value);
 
4275
 
 
4276
        if (next_job_id > NextJobId)
 
4277
          NextJobId = next_job_id;
 
4278
      }
 
4279
      break;
 
4280
    }
 
4281
  }
 
4282
 
 
4283
  cupsFileClose(fp);
 
4284
}
 
4285
 
 
4286
 
 
4287
/*
 
4288
 * 'load_request_root()' - Load jobs from the RequestRoot directory.
 
4289
 */
 
4290
 
 
4291
static void
 
4292
load_request_root(void)
 
4293
{
 
4294
  cups_dir_t            *dir;           /* Directory */
 
4295
  cups_dentry_t         *dent;          /* Directory entry */
 
4296
  cupsd_job_t           *job;           /* New job */
 
4297
 
 
4298
 
 
4299
 /*
 
4300
  * Open the requests directory...
 
4301
  */
 
4302
 
 
4303
  cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot);
 
4304
 
 
4305
  if ((dir = cupsDirOpen(RequestRoot)) == NULL)
 
4306
  {
 
4307
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
4308
                    "Unable to open spool directory \"%s\": %s",
 
4309
                    RequestRoot, strerror(errno));
 
4310
    return;
 
4311
  }
 
4312
 
 
4313
 /*
 
4314
  * Read all the c##### files...
 
4315
  */
 
4316
 
 
4317
  while ((dent = cupsDirRead(dir)) != NULL)
 
4318
    if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
 
4319
    {
 
4320
     /*
 
4321
      * Allocate memory for the job...
 
4322
      */
 
4323
 
 
4324
      if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
 
4325
      {
 
4326
        cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs.");
 
4327
        cupsDirClose(dir);
 
4328
        return;
 
4329
      }
 
4330
 
 
4331
     /*
 
4332
      * Assign the job ID...
 
4333
      */
 
4334
 
 
4335
      job->id              = atoi(dent->filename + 1);
 
4336
      job->back_pipes[0]   = -1;
 
4337
      job->back_pipes[1]   = -1;
 
4338
      job->print_pipes[0]  = -1;
 
4339
      job->print_pipes[1]  = -1;
 
4340
      job->side_pipes[0]   = -1;
 
4341
      job->side_pipes[1]   = -1;
 
4342
      job->status_pipes[0] = -1;
 
4343
      job->status_pipes[1] = -1;
 
4344
 
 
4345
      if (job->id >= NextJobId)
 
4346
        NextJobId = job->id + 1;
 
4347
 
 
4348
     /*
 
4349
      * Load the job...
 
4350
      */
 
4351
 
 
4352
      if (cupsdLoadJob(job))
 
4353
      {
 
4354
       /*
 
4355
        * Insert the job into the array, sorting by job priority and ID...
 
4356
        */
 
4357
 
 
4358
        cupsArrayAdd(Jobs, job);
 
4359
 
 
4360
        if (job->state_value <= IPP_JOB_STOPPED)
 
4361
          cupsArrayAdd(ActiveJobs, job);
 
4362
        else
 
4363
          unload_job(job);
 
4364
      }
 
4365
      else
 
4366
        free(job);
 
4367
    }
 
4368
 
 
4369
  cupsDirClose(dir);
 
4370
}
 
4371
 
 
4372
 
 
4373
/*
 
4374
 * 'remove_job_files()' - Remove the document files for a job.
 
4375
 */
 
4376
 
 
4377
static void
 
4378
remove_job_files(cupsd_job_t *job)      /* I - Job */
 
4379
{
 
4380
  int   i;                              /* Looping var */
 
4381
  char  filename[1024];                 /* Document filename */
 
4382
 
 
4383
 
 
4384
  if (job->num_files <= 0)
 
4385
    return;
 
4386
 
 
4387
  for (i = 1; i <= job->num_files; i ++)
 
4388
  {
 
4389
    snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
 
4390
             job->id, i);
 
4391
    cupsdUnlinkOrRemoveFile(filename);
 
4392
  }
 
4393
 
 
4394
  free(job->filetypes);
 
4395
  free(job->compressions);
 
4396
 
 
4397
  job->file_time    = 0;
 
4398
  job->num_files    = 0;
 
4399
  job->filetypes    = NULL;
 
4400
  job->compressions = NULL;
 
4401
 
 
4402
  LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
 
4403
}
 
4404
 
 
4405
 
 
4406
/*
 
4407
 * 'remove_job_history()' - Remove the control file for a job.
 
4408
 */
 
4409
 
 
4410
static void
 
4411
remove_job_history(cupsd_job_t *job)    /* I - Job */
 
4412
{
 
4413
  char  filename[1024];                 /* Control filename */
 
4414
 
 
4415
 
 
4416
 /*
 
4417
  * Remove the job info file...
 
4418
  */
 
4419
 
 
4420
  snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
 
4421
           job->id);
 
4422
  cupsdUnlinkOrRemoveFile(filename);
 
4423
 
 
4424
  LastEvent |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
 
4425
}
 
4426
 
 
4427
 
 
4428
/*
 
4429
 * 'set_time()' - Set one of the "time-at-xyz" attributes.
 
4430
 */
 
4431
 
 
4432
static void
 
4433
set_time(cupsd_job_t *job,              /* I - Job to update */
 
4434
         const char  *name)             /* I - Name of attribute */
 
4435
{
 
4436
  ipp_attribute_t       *attr;          /* Time attribute */
 
4437
  time_t                curtime;        /* Current time */
 
4438
 
 
4439
 
 
4440
  curtime = time(NULL);
 
4441
 
 
4442
  cupsdLogJob(job, CUPSD_LOG_DEBUG, "%s=%ld", name, (long)curtime);
 
4443
 
 
4444
  if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
 
4445
  {
 
4446
    attr->value_tag         = IPP_TAG_INTEGER;
 
4447
    attr->values[0].integer = curtime;
 
4448
  }
 
4449
 
 
4450
  if (!strcmp(name, "time-at-completed"))
 
4451
  {
 
4452
    if (JobHistory < INT_MAX && attr)
 
4453
      job->history_time = attr->values[0].integer + JobHistory;
 
4454
    else
 
4455
      job->history_time = INT_MAX;
 
4456
 
 
4457
    if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
 
4458
      JobHistoryUpdate = job->history_time;
 
4459
 
 
4460
    if (JobFiles < INT_MAX && attr)
 
4461
      job->file_time = attr->values[0].integer + JobFiles;
 
4462
    else
 
4463
      job->file_time = INT_MAX;
 
4464
 
 
4465
    if (job->file_time < JobHistoryUpdate || !JobHistoryUpdate)
 
4466
      JobHistoryUpdate = job->file_time;
 
4467
 
 
4468
    cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_time: JobHistoryUpdate=%ld",
 
4469
                    (long)JobHistoryUpdate);
 
4470
  }
 
4471
}
 
4472
 
 
4473
 
 
4474
/*
 
4475
 * 'start_job()' - Start a print job.
 
4476
 */
 
4477
 
 
4478
static void
 
4479
start_job(cupsd_job_t     *job,         /* I - Job ID */
 
4480
          cupsd_printer_t *printer)     /* I - Printer to print job */
 
4481
{
 
4482
  const char    *filename;              /* Support filename */
 
4483
 
 
4484
 
 
4485
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job(job=%p(%d), printer=%p(%s))",
 
4486
                  job, job->id, printer, printer->name);
 
4487
 
 
4488
 /*
 
4489
  * Make sure we have some files around before we try to print...
 
4490
  */
 
4491
 
 
4492
  if (job->num_files == 0)
 
4493
  {
 
4494
    ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
 
4495
    cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
 
4496
                     "Aborting job because it has no files.");
 
4497
    return;
 
4498
  }
 
4499
 
 
4500
 /*
 
4501
  * Update the printer and job state to "processing"...
 
4502
  */
 
4503
 
 
4504
  if (!cupsdLoadJob(job))
 
4505
    return;
 
4506
 
 
4507
  if (!job->printer_message)
 
4508
    job->printer_message = ippFindAttribute(job->attrs,
 
4509
                                            "job-printer-state-message",
 
4510
                                            IPP_TAG_TEXT);
 
4511
  if (job->printer_message)
 
4512
    cupsdSetString(&(job->printer_message->values[0].string.text), "");
 
4513
 
 
4514
  ippSetString(job->attrs, &job->reasons, 0, "job-printing");
 
4515
  cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
 
4516
  cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
 
4517
  cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
 
4518
                                  "cups-remote-pending-held,"
 
4519
                                  "cups-remote-processing,"
 
4520
                                  "cups-remote-stopped,"
 
4521
                                  "cups-remote-canceled,"
 
4522
                                  "cups-remote-aborted,"
 
4523
                                  "cups-remote-completed");
 
4524
 
 
4525
  job->cost         = 0;
 
4526
  job->current_file = 0;
 
4527
  job->file_time    = 0;
 
4528
  job->history_time = 0;
 
4529
  job->progress     = 0;
 
4530
  job->printer      = printer;
 
4531
  printer->job      = job;
 
4532
 
 
4533
  if (MaxJobTime > 0)
 
4534
    job->cancel_time = time(NULL) + MaxJobTime;
 
4535
  else
 
4536
    job->cancel_time = 0;
 
4537
 
 
4538
 /*
 
4539
  * Check for support files...
 
4540
  */
 
4541
 
 
4542
  cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
 
4543
                                       "cups-insecure-filter-warning");
 
4544
 
 
4545
  if (printer->pc)
 
4546
  {
 
4547
    for (filename = (const char *)cupsArrayFirst(printer->pc->support_files);
 
4548
         filename;
 
4549
         filename = (const char *)cupsArrayNext(printer->pc->support_files))
 
4550
    {
 
4551
      if (_cupsFileCheck(filename, _CUPS_FILE_CHECK_FILE, !RunUser,
 
4552
                         cupsdLogFCMessage, printer))
 
4553
        break;
 
4554
    }
 
4555
  }
 
4556
 
 
4557
 /*
 
4558
  * Setup the last exit status and security profiles...
 
4559
  */
 
4560
 
 
4561
  job->status  = 0;
 
4562
  job->profile = cupsdCreateProfile(job->id);
 
4563
 
 
4564
 /*
 
4565
  * Create the status pipes and buffer...
 
4566
  */
 
4567
 
 
4568
  if (cupsdOpenPipe(job->status_pipes))
 
4569
  {
 
4570
    cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
4571
                "Unable to create job status pipes - %s.", strerror(errno));
 
4572
 
 
4573
    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
 
4574
                     "Job stopped because the scheduler could not create the "
 
4575
                     "job status pipes.");
 
4576
 
 
4577
    cupsdDestroyProfile(job->profile);
 
4578
    job->profile = NULL;
 
4579
    return;
 
4580
  }
 
4581
 
 
4582
  job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
 
4583
  job->status_level  = CUPSD_LOG_INFO;
 
4584
 
 
4585
 /*
 
4586
  * Create the backchannel pipes and make them non-blocking...
 
4587
  */
 
4588
 
 
4589
  if (cupsdOpenPipe(job->back_pipes))
 
4590
  {
 
4591
    cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
4592
                "Unable to create back-channel pipes - %s.", strerror(errno));
 
4593
 
 
4594
    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
 
4595
                     "Job stopped because the scheduler could not create the "
 
4596
                     "back-channel pipes.");
 
4597
 
 
4598
    cupsdClosePipe(job->status_pipes);
 
4599
    cupsdStatBufDelete(job->status_buffer);
 
4600
    job->status_buffer = NULL;
 
4601
 
 
4602
    cupsdDestroyProfile(job->profile);
 
4603
    job->profile = NULL;
 
4604
    return;
 
4605
  }
 
4606
 
 
4607
  fcntl(job->back_pipes[0], F_SETFL,
 
4608
        fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
 
4609
  fcntl(job->back_pipes[1], F_SETFL,
 
4610
        fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
 
4611
 
 
4612
 /*
 
4613
  * Create the side-channel pipes and make them non-blocking...
 
4614
  */
 
4615
 
 
4616
  if (socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes))
 
4617
  {
 
4618
    cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
4619
                "Unable to create side-channel pipes - %s.", strerror(errno));
 
4620
 
 
4621
    cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
 
4622
                     "Job stopped because the scheduler could not create the "
 
4623
                     "side-channel pipes.");
 
4624
 
 
4625
    cupsdClosePipe(job->back_pipes);
 
4626
 
 
4627
    cupsdClosePipe(job->status_pipes);
 
4628
    cupsdStatBufDelete(job->status_buffer);
 
4629
    job->status_buffer = NULL;
 
4630
 
 
4631
    cupsdDestroyProfile(job->profile);
 
4632
    job->profile = NULL;
 
4633
    return;
 
4634
  }
 
4635
 
 
4636
  fcntl(job->side_pipes[0], F_SETFL,
 
4637
        fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK);
 
4638
  fcntl(job->side_pipes[1], F_SETFL,
 
4639
        fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
 
4640
 
 
4641
  fcntl(job->side_pipes[0], F_SETFD,
 
4642
        fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC);
 
4643
  fcntl(job->side_pipes[1], F_SETFD,
 
4644
        fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC);
 
4645
 
 
4646
 /*
 
4647
  * Now start the first file in the job...
 
4648
  */
 
4649
 
 
4650
  cupsdContinueJob(job);
 
4651
}
 
4652
 
 
4653
 
 
4654
/*
 
4655
 * 'stop_job()' - Stop a print job.
 
4656
 */
 
4657
 
 
4658
static void
 
4659
stop_job(cupsd_job_t       *job,        /* I - Job */
 
4660
         cupsd_jobaction_t action)      /* I - Action */
 
4661
{
 
4662
  int   i;                              /* Looping var */
 
4663
 
 
4664
 
 
4665
  cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_job(job=%p(%d), action=%d)", job,
 
4666
                  job->id, action);
 
4667
 
 
4668
  FilterLevel -= job->cost;
 
4669
  job->cost   = 0;
 
4670
 
 
4671
  if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
 
4672
    job->kill_time = time(NULL) + JobKillDelay;
 
4673
  else if (action >= CUPSD_JOB_FORCE)
 
4674
    job->kill_time = 0;
 
4675
 
 
4676
  for (i = 0; job->filters[i]; i ++)
 
4677
    if (job->filters[i] > 0)
 
4678
    {
 
4679
      cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE);
 
4680
 
 
4681
      if (action >= CUPSD_JOB_FORCE)
 
4682
        job->filters[i] = -job->filters[i];
 
4683
    }
 
4684
 
 
4685
  if (job->backend > 0)
 
4686
  {
 
4687
    cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE);
 
4688
 
 
4689
    if (action >= CUPSD_JOB_FORCE)
 
4690
      job->backend = -job->backend;
 
4691
  }
 
4692
 
 
4693
  if (action >= CUPSD_JOB_FORCE)
 
4694
  {
 
4695
   /*
 
4696
    * Clear job status...
 
4697
    */
 
4698
 
 
4699
    job->status = 0;
 
4700
  }
 
4701
}
 
4702
 
 
4703
 
 
4704
/*
 
4705
 * 'unload_job()' - Unload a job from memory.
 
4706
 */
 
4707
 
 
4708
static void
 
4709
unload_job(cupsd_job_t *job)            /* I - Job */
 
4710
{
 
4711
  if (!job->attrs)
 
4712
    return;
 
4713
 
 
4714
  cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id);
 
4715
 
 
4716
  ippDelete(job->attrs);
 
4717
 
 
4718
  job->attrs           = NULL;
 
4719
  job->state           = NULL;
 
4720
  job->reasons         = NULL;
 
4721
  job->sheets          = NULL;
 
4722
  job->job_sheets      = NULL;
 
4723
  job->printer_message = NULL;
 
4724
  job->printer_reasons = NULL;
 
4725
}
 
4726
 
 
4727
 
 
4728
/*
 
4729
 * 'update_job()' - Read a status update from a job's filters.
 
4730
 */
 
4731
 
 
4732
void
 
4733
update_job(cupsd_job_t *job)            /* I - Job to check */
 
4734
{
 
4735
  int           i;                      /* Looping var */
 
4736
  int           copies;                 /* Number of copies printed */
 
4737
  char          message[CUPSD_SB_BUFFER_SIZE],
 
4738
                                        /* Message text */
 
4739
                *ptr;                   /* Pointer update... */
 
4740
  int           loglevel,               /* Log level for message */
 
4741
                event = 0;              /* Events? */
 
4742
  static const char * const levels[] =  /* Log levels */
 
4743
                {
 
4744
                  "NONE",
 
4745
                  "EMERG",
 
4746
                  "ALERT",
 
4747
                  "CRIT",
 
4748
                  "ERROR",
 
4749
                  "WARN",
 
4750
                  "NOTICE",
 
4751
                  "INFO",
 
4752
                  "DEBUG",
 
4753
                  "DEBUG2"
 
4754
                };
 
4755
 
 
4756
 
 
4757
 /*
 
4758
  * Get the printer associated with this job; if the printer is stopped for
 
4759
  * any reason then job->printer will be reset to NULL, so make sure we have
 
4760
  * a valid pointer...
 
4761
  */
 
4762
 
 
4763
  while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
 
4764
                                   message, sizeof(message))) != NULL)
 
4765
  {
 
4766
   /*
 
4767
    * Process page and printer state messages as needed...
 
4768
    */
 
4769
 
 
4770
    if (loglevel == CUPSD_LOG_PAGE)
 
4771
    {
 
4772
     /*
 
4773
      * Page message; send the message to the page_log file and update the
 
4774
      * job sheet count...
 
4775
      */
 
4776
 
 
4777
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message);
 
4778
 
 
4779
      if (job->sheets)
 
4780
      {
 
4781
        if (!_cups_strncasecmp(message, "total ", 6))
 
4782
        {
 
4783
         /*
 
4784
          * Got a total count of pages from a backend or filter...
 
4785
          */
 
4786
 
 
4787
          copies = atoi(message + 6);
 
4788
          copies -= job->sheets->values[0].integer; /* Just track the delta */
 
4789
        }
 
4790
        else if (!sscanf(message, "%*d%d", &copies))
 
4791
          copies = 1;
 
4792
 
 
4793
        job->sheets->values[0].integer += copies;
 
4794
 
 
4795
        if (job->printer->page_limit)
 
4796
          cupsdUpdateQuota(job->printer, job->username, copies, 0);
 
4797
      }
 
4798
 
 
4799
      cupsdLogPage(job, message);
 
4800
 
 
4801
      if (job->sheets)
 
4802
        cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
 
4803
                      "Printed %d page(s).", job->sheets->values[0].integer);
 
4804
    }
 
4805
    else if (loglevel == CUPSD_LOG_JOBSTATE)
 
4806
    {
 
4807
     /*
 
4808
      * Support "keyword" to set job-state-reasons to the specified keyword.
 
4809
      * This is sufficient for the current paid printing stuff.
 
4810
      */
 
4811
 
 
4812
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "JOBSTATE: %s", message);
 
4813
 
 
4814
      ippSetString(job->attrs, &job->reasons, 0, message);
 
4815
    }
 
4816
    else if (loglevel == CUPSD_LOG_STATE)
 
4817
    {
 
4818
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "STATE: %s", message);
 
4819
 
 
4820
      if (!strcmp(message, "paused"))
 
4821
      {
 
4822
        cupsdStopPrinter(job->printer, 1);
 
4823
        return;
 
4824
      }
 
4825
      else if (message[0] && cupsdSetPrinterReasons(job->printer, message))
 
4826
      {
 
4827
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4828
 
 
4829
        if (MaxJobTime > 0 && strstr(message, "connecting-to-device") != NULL)
 
4830
        {
 
4831
         /*
 
4832
          * Reset cancel time after connecting to the device...
 
4833
          */
 
4834
 
 
4835
          for (i = 0; i < job->printer->num_reasons; i ++)
 
4836
            if (!strcmp(job->printer->reasons[i], "connecting-to-device"))
 
4837
              break;
 
4838
 
 
4839
          if (i >= job->printer->num_reasons)
 
4840
            job->cancel_time = time(NULL) + MaxJobTime;
 
4841
        }
 
4842
      }
 
4843
 
 
4844
      update_job_attrs(job, 0);
 
4845
    }
 
4846
    else if (loglevel == CUPSD_LOG_ATTR)
 
4847
    {
 
4848
     /*
 
4849
      * Set attribute(s)...
 
4850
      */
 
4851
 
 
4852
      int               num_attrs;      /* Number of attributes */
 
4853
      cups_option_t     *attrs;         /* Attributes */
 
4854
      const char        *attr;          /* Attribute */
 
4855
 
 
4856
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "ATTR: %s", message);
 
4857
 
 
4858
      num_attrs = cupsParseOptions(message, 0, &attrs);
 
4859
 
 
4860
      if ((attr = cupsGetOption("auth-info-default", num_attrs,
 
4861
                                attrs)) != NULL)
 
4862
      {
 
4863
        job->printer->num_options = cupsAddOption("auth-info", attr,
 
4864
                                                  job->printer->num_options,
 
4865
                                                  &(job->printer->options));
 
4866
        cupsdSetPrinterAttrs(job->printer);
 
4867
 
 
4868
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4869
      }
 
4870
 
 
4871
      if ((attr = cupsGetOption("auth-info-required", num_attrs,
 
4872
                                attrs)) != NULL)
 
4873
      {
 
4874
        cupsdSetAuthInfoRequired(job->printer, attr, NULL);
 
4875
        cupsdSetPrinterAttrs(job->printer);
 
4876
 
 
4877
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4878
      }
 
4879
 
 
4880
      if ((attr = cupsGetOption("job-media-progress", num_attrs,
 
4881
                                attrs)) != NULL)
 
4882
      {
 
4883
        int progress = atoi(attr);
 
4884
 
 
4885
 
 
4886
        if (progress >= 0 && progress <= 100)
 
4887
        {
 
4888
          job->progress = progress;
 
4889
 
 
4890
          if (job->sheets)
 
4891
            cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
 
4892
                          "Printing page %d, %d%%",
 
4893
                          job->sheets->values[0].integer, job->progress);
 
4894
        }
 
4895
      }
 
4896
 
 
4897
      if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
 
4898
      {
 
4899
        cupsdSetString(&job->printer->alert, attr);
 
4900
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4901
      }
 
4902
 
 
4903
      if ((attr = cupsGetOption("printer-alert-description", num_attrs,
 
4904
                                attrs)) != NULL)
 
4905
      {
 
4906
        cupsdSetString(&job->printer->alert_description, attr);
 
4907
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4908
      }
 
4909
 
 
4910
      if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL)
 
4911
      {
 
4912
        cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr);
 
4913
        job->printer->marker_time = time(NULL);
 
4914
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4915
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4916
      }
 
4917
 
 
4918
      if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL)
 
4919
      {
 
4920
        cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr);
 
4921
        job->printer->marker_time = time(NULL);
 
4922
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4923
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4924
      }
 
4925
 
 
4926
      if ((attr = cupsGetOption("marker-low-levels", num_attrs, attrs)) != NULL)
 
4927
      {
 
4928
        cupsdSetPrinterAttr(job->printer, "marker-low-levels", (char *)attr);
 
4929
        job->printer->marker_time = time(NULL);
 
4930
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4931
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4932
      }
 
4933
 
 
4934
      if ((attr = cupsGetOption("marker-high-levels", num_attrs, attrs)) != NULL)
 
4935
      {
 
4936
        cupsdSetPrinterAttr(job->printer, "marker-high-levels", (char *)attr);
 
4937
        job->printer->marker_time = time(NULL);
 
4938
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4939
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4940
      }
 
4941
 
 
4942
      if ((attr = cupsGetOption("marker-message", num_attrs, attrs)) != NULL)
 
4943
      {
 
4944
        cupsdSetPrinterAttr(job->printer, "marker-message", (char *)attr);
 
4945
        job->printer->marker_time = time(NULL);
 
4946
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4947
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4948
      }
 
4949
 
 
4950
      if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL)
 
4951
      {
 
4952
        cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr);
 
4953
        job->printer->marker_time = time(NULL);
 
4954
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4955
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4956
      }
 
4957
 
 
4958
      if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL)
 
4959
      {
 
4960
        cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr);
 
4961
        job->printer->marker_time = time(NULL);
 
4962
        event |= CUPSD_EVENT_PRINTER_STATE;
 
4963
        cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
4964
      }
 
4965
 
 
4966
      cupsFreeOptions(num_attrs, attrs);
 
4967
    }
 
4968
    else if (loglevel == CUPSD_LOG_PPD)
 
4969
    {
 
4970
     /*
 
4971
      * Set attribute(s)...
 
4972
      */
 
4973
 
 
4974
      cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message);
 
4975
 
 
4976
      job->num_keywords = cupsParseOptions(message, job->num_keywords,
 
4977
                                           &job->keywords);
 
4978
    }
 
4979
    else
 
4980
    {
 
4981
     /*
 
4982
      * Strip legacy message prefix...
 
4983
      */
 
4984
 
 
4985
      if (!strncmp(message, "recoverable:", 12))
 
4986
      {
 
4987
        ptr = message + 12;
 
4988
        while (isspace(*ptr & 255))
 
4989
          ptr ++;
 
4990
      }
 
4991
      else if (!strncmp(message, "recovered:", 10))
 
4992
      {
 
4993
        ptr = message + 10;
 
4994
        while (isspace(*ptr & 255))
 
4995
          ptr ++;
 
4996
      }
 
4997
      else
 
4998
        ptr = message;
 
4999
 
 
5000
      if (*ptr)
 
5001
        cupsdLogJob(job, loglevel, "%s", ptr);
 
5002
 
 
5003
      if (loglevel < CUPSD_LOG_DEBUG &&
 
5004
          strcmp(job->printer->state_message, ptr))
 
5005
      {
 
5006
        strlcpy(job->printer->state_message, ptr,
 
5007
                sizeof(job->printer->state_message));
 
5008
 
 
5009
        event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
 
5010
 
 
5011
        if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR)
 
5012
        {
 
5013
         /*
 
5014
          * Some messages show in the job-printer-state-message attribute...
 
5015
          */
 
5016
 
 
5017
          if (loglevel != CUPSD_LOG_NOTICE)
 
5018
            job->status_level = loglevel;
 
5019
 
 
5020
          update_job_attrs(job, 1);
 
5021
 
 
5022
          cupsdLogJob(job, CUPSD_LOG_DEBUG,
 
5023
                      "Set job-printer-state-message to \"%s\", "
 
5024
                      "current level=%s",
 
5025
                      job->printer_message->values[0].string.text,
 
5026
                      levels[job->status_level]);
 
5027
        }
 
5028
      }
 
5029
    }
 
5030
 
 
5031
    if (!strchr(job->status_buffer->buffer, '\n'))
 
5032
      break;
 
5033
  }
 
5034
 
 
5035
  if (event & CUPSD_EVENT_JOB_PROGRESS)
 
5036
    cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
 
5037
                  "%s", job->printer->state_message);
 
5038
  if (event & CUPSD_EVENT_PRINTER_STATE)
 
5039
    cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
 
5040
                  (job->printer->type & CUPS_PRINTER_CLASS) ?
 
5041
                      "Class \"%s\" state changed." :
 
5042
                      "Printer \"%s\" state changed.",
 
5043
                  job->printer->name);
 
5044
 
 
5045
 
 
5046
  if (ptr == NULL && !job->status_buffer->bufused)
 
5047
  {
 
5048
   /*
 
5049
    * See if all of the filters and the backend have returned their
 
5050
    * exit statuses.
 
5051
    */
 
5052
 
 
5053
    for (i = 0; job->filters[i] < 0; i ++);
 
5054
 
 
5055
    if (job->filters[i])
 
5056
    {
 
5057
     /*
 
5058
      * EOF but we haven't collected the exit status of all filters...
 
5059
      */
 
5060
 
 
5061
      cupsdCheckProcess();
 
5062
      return;
 
5063
    }
 
5064
 
 
5065
    if (job->current_file >= job->num_files && job->backend > 0)
 
5066
    {
 
5067
     /*
 
5068
      * EOF but we haven't collected the exit status of the backend...
 
5069
      */
 
5070
 
 
5071
      cupsdCheckProcess();
 
5072
      return;
 
5073
    }
 
5074
 
 
5075
   /*
 
5076
    * Handle the end of job stuff...
 
5077
    */
 
5078
 
 
5079
    finalize_job(job, 1);
 
5080
 
 
5081
   /*
 
5082
    * Check for new jobs...
 
5083
    */
 
5084
 
 
5085
    cupsdCheckJobs();
 
5086
  }
 
5087
}
 
5088
 
 
5089
 
 
5090
/*
 
5091
 * 'update_job_attrs()' - Update the job-printer-* attributes.
 
5092
 */
 
5093
 
 
5094
void
 
5095
update_job_attrs(cupsd_job_t *job,      /* I - Job to update */
 
5096
                 int         do_message)/* I - 1 = copy job-printer-state message */
 
5097
{
 
5098
  int                   i;              /* Looping var */
 
5099
  int                   num_reasons;    /* Actual number of reasons */
 
5100
  const char * const    *reasons;       /* Reasons */
 
5101
  static const char     *none = "none"; /* "none" reason */
 
5102
 
 
5103
 
 
5104
 /*
 
5105
  * Get/create the job-printer-state-* attributes...
 
5106
  */
 
5107
 
 
5108
  if (!job->printer_message)
 
5109
  {
 
5110
    if ((job->printer_message = ippFindAttribute(job->attrs,
 
5111
                                                 "job-printer-state-message",
 
5112
                                                 IPP_TAG_TEXT)) == NULL)
 
5113
      job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
 
5114
                                          "job-printer-state-message",
 
5115
                                          NULL, "");
 
5116
  }
 
5117
 
 
5118
  if (!job->printer_reasons)
 
5119
    job->printer_reasons = ippFindAttribute(job->attrs,
 
5120
                                            "job-printer-state-reasons",
 
5121
                                            IPP_TAG_KEYWORD);
 
5122
 
 
5123
 /*
 
5124
  * Copy or clear the printer-state-message value as needed...
 
5125
  */
 
5126
 
 
5127
  if (job->state_value != IPP_JOB_PROCESSING &&
 
5128
      job->status_level == CUPSD_LOG_INFO)
 
5129
  {
 
5130
    cupsdSetString(&(job->printer_message->values[0].string.text), "");
 
5131
 
 
5132
    job->dirty = 1;
 
5133
    cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
5134
  }
 
5135
  else if (job->printer->state_message[0] && do_message)
 
5136
  {
 
5137
    cupsdSetString(&(job->printer_message->values[0].string.text),
 
5138
                   job->printer->state_message);
 
5139
 
 
5140
    job->dirty = 1;
 
5141
    cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
5142
  }
 
5143
 
 
5144
 /*
 
5145
  * ... and the printer-state-reasons value...
 
5146
  */
 
5147
 
 
5148
  if (job->printer->num_reasons == 0)
 
5149
  {
 
5150
    num_reasons = 1;
 
5151
    reasons     = &none;
 
5152
  }
 
5153
  else
 
5154
  {
 
5155
    num_reasons = job->printer->num_reasons;
 
5156
    reasons     = (const char * const *)job->printer->reasons;
 
5157
  }
 
5158
 
 
5159
  if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
 
5160
  {
 
5161
   /*
 
5162
    * Replace/create a job-printer-state-reasons attribute...
 
5163
    */
 
5164
 
 
5165
    ippDeleteAttribute(job->attrs, job->printer_reasons);
 
5166
 
 
5167
    job->printer_reasons = ippAddStrings(job->attrs,
 
5168
                                         IPP_TAG_JOB, IPP_TAG_KEYWORD,
 
5169
                                         "job-printer-state-reasons",
 
5170
                                         num_reasons, NULL, NULL);
 
5171
  }
 
5172
  else
 
5173
  {
 
5174
   /*
 
5175
    * Don't bother clearing the reason strings if they are the same...
 
5176
    */
 
5177
 
 
5178
    for (i = 0; i < num_reasons; i ++)
 
5179
      if (strcmp(job->printer_reasons->values[i].string.text, reasons[i]))
 
5180
        break;
 
5181
 
 
5182
    if (i >= num_reasons)
 
5183
      return;
 
5184
 
 
5185
   /*
 
5186
    * Not the same, so free the current strings...
 
5187
    */
 
5188
 
 
5189
    for (i = 0; i < num_reasons; i ++)
 
5190
      _cupsStrFree(job->printer_reasons->values[i].string.text);
 
5191
  }
 
5192
 
 
5193
 /*
 
5194
  * Copy the reasons...
 
5195
  */
 
5196
 
 
5197
  for (i = 0; i < num_reasons; i ++)
 
5198
    job->printer_reasons->values[i].string.text = _cupsStrAlloc(reasons[i]);
 
5199
 
 
5200
  job->dirty = 1;
 
5201
  cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
5202
}
 
5203
 
 
5204
 
 
5205
/*
 
5206
 * End of "$Id: job.c 11147 2013-07-17 02:54:31Z msweet $".
 
5207
 */