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

« back to all changes in this revision

Viewing changes to .pc/no-conffile-timestamp.patch/scheduler/subscriptions.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt, Till Kamppeter, Martin Pitt
  • Date: 2011-07-14 15:02:36 UTC
  • mfrom: (1.2.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110714150236-skkf0p5m6ccu5usu
Tags: 1.4.7-1
* New upstream version.

[ Till Kamppeter ]
* debian/patches/ubuntu-upstart.dpatch: Updated the patch to add support
  to the new device enumeration functionality of udev-configure-printer.
  This way we do not need to retrigger the printers. Retriggering is only
  needed if udev rules change. A fallback to the old bahavior is provided
  so that this CUPS package continues to work with older versions of
  udev-configure-printer.

[ Martin Pitt ]
* Update patches for new upstream release.
* Drop fix-broken-ipv6-uris.patch, applied upstream.
* debian/local/apparmor-profile: /var/run → /run transition. (LP: #810270)
* Drop debian/patches/ubuntu-upstart.dpatch and move the upstart script to
  debian/local/cups.upstart. In debian/rules, copy it to debian/, and remove
  that again during clean. This is a slightly easier workaround for a
  nonexisting "dh_installinit --sysvinit-only" option than the previous
  creation of the upstart file with an ubuntu specific dpatch.
* debian/patches/, debian/rules, debian/control, debian/source/format: Move
  to source format "3.0 (quilt)" and convert our dpatches to quilt patches.
  Drop dpatch build dependency.
* Move Ubuntu specific patches to debian/patches/ubuntu. In debian/rules,
  apply them when building on Ubuntu. Add "patch" build dependency.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * "$Id: subscriptions.c 9445 2011-01-08 00:03:51Z mike $"
 
3
 *
 
4
 *   Subscription routines for the CUPS scheduler.
 
5
 *
 
6
 *   Copyright 2007-2011 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
 *   cupsdAddEvent()               - Add an event to the global event cache.
 
18
 *   cupsdAddSubscription()        - Add a new subscription object.
 
19
 *   cupsdDeleteAllSubscriptions() - Delete all subscriptions.
 
20
 *   cupsdDeleteSubscription()     - Delete a subscription object.
 
21
 *   cupsdEventName()              - Return a single event name.
 
22
 *   cupsdEventValue()             - Return the event mask value for a name.
 
23
 *   cupsdExpireSubscriptions()    - Expire old subscription objects.
 
24
 *   cupsdFindSubscription()       - Find a subscription by ID.
 
25
 *   cupsdLoadAllSubscriptions()   - Load all subscriptions from the .conf file.
 
26
 *   cupsdSaveAllSubscriptions()   - Save all subscriptions to the .conf file.
 
27
 *   cupsdStopAllNotifiers()       - Stop all notifier processes.
 
28
 *   cupsd_compare_subscriptions() - Compare two subscriptions.
 
29
 *   cupsd_delete_event()          - Delete a single event...
 
30
 *   cupsd_send_dbus()             - Send a DBUS notification...
 
31
 *   cupsd_send_notification()     - Send a notification for the specified
 
32
 *                                   event.
 
33
 *   cupsd_start_notifier()        - Start a notifier subprocess...
 
34
 *   cupsd_update_notifier()       - Read messages from notifiers.
 
35
 */
 
36
 
 
37
/*
 
38
 * Include necessary headers...
 
39
 */
 
40
 
 
41
#include "cupsd.h"
 
42
#ifdef HAVE_DBUS
 
43
#  include <dbus/dbus.h>
 
44
#  ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
 
45
#    define dbus_message_append_iter_init dbus_message_iter_init_append
 
46
#    define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
 
47
#    define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
 
48
#  endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
 
49
#endif /* HAVE_DBUS */
 
50
 
 
51
 
 
52
/*
 
53
 * Local functions...
 
54
 */
 
55
 
 
56
static int      cupsd_compare_subscriptions(cupsd_subscription_t *first,
 
57
                                            cupsd_subscription_t *second,
 
58
                                            void *unused);
 
59
static void     cupsd_delete_event(cupsd_event_t *event);
 
60
#ifdef HAVE_DBUS
 
61
static void     cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
 
62
                                cupsd_job_t *job);
 
63
#endif /* HAVE_DBUS */
 
64
static void     cupsd_send_notification(cupsd_subscription_t *sub,
 
65
                                        cupsd_event_t *event);
 
66
static void     cupsd_start_notifier(cupsd_subscription_t *sub);
 
67
static void     cupsd_update_notifier(void);
 
68
 
 
69
 
 
70
/*
 
71
 * 'cupsdAddEvent()' - Add an event to the global event cache.
 
72
 */
 
73
 
 
74
void
 
75
cupsdAddEvent(
 
76
    cupsd_eventmask_t event,            /* I - Event */
 
77
    cupsd_printer_t   *dest,            /* I - Printer associated with event */
 
78
    cupsd_job_t       *job,             /* I - Job associated with event */
 
79
    const char        *text,            /* I - Notification text */
 
80
    ...)                                /* I - Additional arguments as needed */
 
81
{
 
82
  va_list               ap;             /* Pointer to additional arguments */
 
83
  char                  ftext[1024];    /* Formatted text buffer */
 
84
  ipp_attribute_t       *attr;          /* Printer/job attribute */
 
85
  cupsd_event_t         *temp;          /* New event pointer */
 
86
  cupsd_subscription_t  *sub;           /* Current subscription */
 
87
 
 
88
 
 
89
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
90
                  "cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
 
91
                  cupsdEventName(event), dest, dest ? dest->name : "",
 
92
                  job, job ? job->id : 0, text);
 
93
 
 
94
 /*
 
95
  * Keep track of events with any OS-supplied notification mechanisms...
 
96
  */
 
97
 
 
98
  LastEvent |= event;
 
99
 
 
100
#ifdef HAVE_DBUS
 
101
  cupsd_send_dbus(event, dest, job);
 
102
#endif /* HAVE_DBUS */
 
103
 
 
104
 /*
 
105
  * Return if we aren't keeping events...
 
106
  */
 
107
 
 
108
  if (MaxEvents <= 0)
 
109
  {
 
110
    cupsdLogMessage(CUPSD_LOG_WARN,
 
111
                    "cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
 
112
                    cupsdEventName(event), MaxEvents);
 
113
    return;
 
114
  }
 
115
 
 
116
 /*
 
117
  * Then loop through the subscriptions and add the event to the corresponding
 
118
  * caches...
 
119
  */
 
120
 
 
121
  for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
 
122
       sub;
 
123
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
124
  {
 
125
   /*
 
126
    * Check if this subscription requires this event...
 
127
    */
 
128
 
 
129
    if ((sub->mask & event) != 0 &&
 
130
        (sub->dest == dest || !sub->dest) &&
 
131
        (sub->job == job || !sub->job))
 
132
    {
 
133
     /*
 
134
      * Need this event, so create a new event record...
 
135
      */
 
136
 
 
137
      if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
 
138
      {
 
139
        cupsdLogMessage(CUPSD_LOG_CRIT,
 
140
                        "Unable to allocate memory for event - %s",
 
141
                        strerror(errno));
 
142
        return;
 
143
      }
 
144
 
 
145
      temp->event = event;
 
146
      temp->time  = time(NULL);
 
147
      temp->attrs = ippNew();
 
148
      temp->job   = job;
 
149
      temp->dest  = dest;
 
150
 
 
151
     /*
 
152
      * Add common event notification attributes...
 
153
      */
 
154
 
 
155
      ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
 
156
                   "notify-charset", NULL, "utf-8");
 
157
 
 
158
      ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
 
159
                   "notify-natural-language", NULL, "en-US");
 
160
 
 
161
      ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
 
162
                    "notify-subscription-id", sub->id);
 
163
 
 
164
      ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
 
165
                    "notify-sequence-number", sub->next_event_id);
 
166
 
 
167
      ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
 
168
                   "notify-subscribed-event", NULL, cupsdEventName(event));
 
169
 
 
170
      if (sub->user_data_len > 0)
 
171
        ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
172
                          "notify-user-data", sub->user_data,
 
173
                          sub->user_data_len);
 
174
 
 
175
      ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
 
176
                    "printer-up-time", time(NULL));
 
177
 
 
178
      va_start(ap, text);
 
179
      vsnprintf(ftext, sizeof(ftext), text, ap);
 
180
      va_end(ap);
 
181
 
 
182
      ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
 
183
                   "notify-text", NULL, ftext);
 
184
 
 
185
      if (dest)
 
186
      {
 
187
       /*
 
188
        * Add printer attributes...
 
189
        */
 
190
 
 
191
        ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI,
 
192
                     "notify-printer-uri", NULL, dest->uri);
 
193
 
 
194
        ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
 
195
                     "printer-name", NULL, dest->name);
 
196
 
 
197
        ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
 
198
                      "printer-state", dest->state);
 
199
 
 
200
        if (dest->num_reasons == 0)
 
201
          ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
202
                       IPP_TAG_KEYWORD, "printer-state-reasons", NULL,
 
203
                       dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
 
204
        else
 
205
          ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
206
                        IPP_TAG_KEYWORD, "printer-state-reasons",
 
207
                        dest->num_reasons, NULL,
 
208
                        (const char * const *)dest->reasons);
 
209
 
 
210
        ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
211
                      "printer-is-accepting-jobs", dest->accepting);
 
212
      }
 
213
 
 
214
      if (job)
 
215
      {
 
216
       /*
 
217
        * Add job attributes...
 
218
        */
 
219
 
 
220
        ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
 
221
                      "notify-job-id", job->id);
 
222
        ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM,
 
223
                      "job-state", job->state_value);
 
224
 
 
225
        if ((attr = ippFindAttribute(job->attrs, "job-name",
 
226
                                     IPP_TAG_NAME)) != NULL)
 
227
          ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME,
 
228
                       "job-name", NULL, attr->values[0].string.text);
 
229
 
 
230
        switch (job->state_value)
 
231
        {
 
232
          case IPP_JOB_PENDING :
 
233
              if (dest && dest->state == IPP_PRINTER_STOPPED)
 
234
                ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
235
                             IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
236
                             "printer-stopped");
 
237
              else
 
238
                ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
239
                             IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
240
                             "none");
 
241
              break;
 
242
 
 
243
          case IPP_JOB_HELD :
 
244
              if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
 
245
                  ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
 
246
                ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
247
                             IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
248
                             "job-hold-until-specified");
 
249
              else
 
250
                ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
251
                             IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
252
                             "job-incoming");
 
253
              break;
 
254
 
 
255
          case IPP_JOB_PROCESSING :
 
256
              ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
257
                           IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
258
                           "job-printing");
 
259
              break;
 
260
 
 
261
          case IPP_JOB_STOPPED :
 
262
              ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
263
                           IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
264
                           "job-stopped");
 
265
              break;
 
266
 
 
267
          case IPP_JOB_CANCELED :
 
268
              ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
269
                           IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
270
                           "job-canceled-by-user");
 
271
              break;
 
272
 
 
273
          case IPP_JOB_ABORTED :
 
274
              ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
275
                           IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
276
                           "aborted-by-system");
 
277
              break;
 
278
 
 
279
          case IPP_JOB_COMPLETED :
 
280
              ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
 
281
                           IPP_TAG_KEYWORD, "job-state-reasons", NULL,
 
282
                           "job-completed-successfully");
 
283
              break;
 
284
        }
 
285
 
 
286
        ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
 
287
                      "job-impressions-completed",
 
288
                      job->sheets ? job->sheets->values[0].integer : 0);
 
289
      }
 
290
 
 
291
     /*
 
292
      * Send the notification for this subscription...
 
293
      */
 
294
 
 
295
      cupsd_send_notification(sub, temp);
 
296
    }
 
297
  }
 
298
 
 
299
  if (temp)
 
300
    cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
 
301
  else
 
302
    cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...",
 
303
                    cupsdEventName(event));
 
304
}
 
305
 
 
306
 
 
307
/*
 
308
 * 'cupsdAddSubscription()' - Add a new subscription object.
 
309
 */
 
310
 
 
311
cupsd_subscription_t *                  /* O - New subscription object */
 
312
cupsdAddSubscription(
 
313
    unsigned        mask,               /* I - Event mask */
 
314
    cupsd_printer_t *dest,              /* I - Printer, if any */
 
315
    cupsd_job_t     *job,               /* I - Job, if any */
 
316
    const char      *uri,               /* I - notify-recipient-uri, if any */
 
317
    int             sub_id)             /* I - notify-subscription-id or 0 */
 
318
{
 
319
  cupsd_subscription_t  *temp;          /* New subscription object */
 
320
 
 
321
 
 
322
  cupsdLogMessage(CUPSD_LOG_DEBUG,
 
323
                  "cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
 
324
                  "uri=\"%s\")",
 
325
                  mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
 
326
                  uri ? uri : "(null)");
 
327
 
 
328
  if (!Subscriptions)
 
329
    Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
 
330
                                 NULL);
 
331
 
 
332
  if (!Subscriptions)
 
333
  {
 
334
    cupsdLogMessage(CUPSD_LOG_CRIT,
 
335
                    "Unable to allocate memory for subscriptions - %s",
 
336
                    strerror(errno));
 
337
    return (NULL);
 
338
  }
 
339
 
 
340
 /*
 
341
  * Limit the number of subscriptions...
 
342
  */
 
343
 
 
344
  if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
 
345
  {
 
346
    cupsdLogMessage(CUPSD_LOG_DEBUG,
 
347
                    "cupsdAddSubscription: Reached MaxSubscriptions %d "
 
348
                    "(count=%d)", MaxSubscriptions,
 
349
                    cupsArrayCount(Subscriptions));
 
350
    return (NULL);
 
351
  }
 
352
 
 
353
  if (MaxSubscriptionsPerJob > 0 && job)
 
354
  {
 
355
    int count;                          /* Number of job subscriptions */
 
356
 
 
357
    for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
 
358
             count = 0;
 
359
         temp;
 
360
         temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
361
      if (temp->job == job)
 
362
        count ++;
 
363
 
 
364
    if (count >= MaxSubscriptionsPerJob)
 
365
    {
 
366
      cupsdLogMessage(CUPSD_LOG_DEBUG,
 
367
                      "cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
 
368
                      "for job #%d (count=%d)", MaxSubscriptionsPerJob,
 
369
                      job->id, count);
 
370
      return (NULL);
 
371
    }
 
372
  }
 
373
 
 
374
  if (MaxSubscriptionsPerPrinter > 0 && dest)
 
375
  {
 
376
    int count;                          /* Number of printer subscriptions */
 
377
 
 
378
    for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
 
379
             count = 0;
 
380
         temp;
 
381
         temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
382
      if (temp->dest == dest)
 
383
        count ++;
 
384
 
 
385
    if (count >= MaxSubscriptionsPerPrinter)
 
386
    {
 
387
      cupsdLogMessage(CUPSD_LOG_DEBUG,
 
388
                      "cupsdAddSubscription: Reached "
 
389
                      "MaxSubscriptionsPerPrinter %d for %s (count=%d)",
 
390
                      MaxSubscriptionsPerPrinter, dest->name, count);
 
391
      return (NULL);
 
392
    }
 
393
  }
 
394
 
 
395
 /*
 
396
  * Allocate memory for this subscription...
 
397
  */
 
398
 
 
399
  if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
 
400
  {
 
401
    cupsdLogMessage(CUPSD_LOG_CRIT,
 
402
                    "Unable to allocate memory for subscription object - %s",
 
403
                    strerror(errno));
 
404
    return (NULL);
 
405
  }
 
406
 
 
407
 /*
 
408
  * Fill in common data...
 
409
  */
 
410
 
 
411
  if (sub_id)
 
412
  {
 
413
    temp->id = sub_id;
 
414
 
 
415
    if (sub_id >= NextSubscriptionId)
 
416
      NextSubscriptionId = sub_id + 1;
 
417
  }
 
418
  else
 
419
  {
 
420
    temp->id = NextSubscriptionId;
 
421
 
 
422
    NextSubscriptionId ++;
 
423
  }
 
424
 
 
425
  temp->mask           = mask;
 
426
  temp->dest           = dest;
 
427
  temp->job            = job;
 
428
  temp->pipe           = -1;
 
429
  temp->first_event_id = 1;
 
430
  temp->next_event_id  = 1;
 
431
 
 
432
  cupsdSetString(&(temp->recipient), uri);
 
433
 
 
434
 /*
 
435
  * Add the subscription to the array...
 
436
  */
 
437
 
 
438
  cupsArrayAdd(Subscriptions, temp);
 
439
 
 
440
 /*
 
441
  * For RSS subscriptions, run the notifier immediately...
 
442
  */
 
443
 
 
444
  if (uri && !strncmp(uri, "rss:", 4))
 
445
    cupsd_start_notifier(temp);
 
446
 
 
447
  return (temp);
 
448
}
 
449
 
 
450
 
 
451
/*
 
452
 * 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
 
453
 */
 
454
 
 
455
void
 
456
cupsdDeleteAllSubscriptions(void)
 
457
{
 
458
  cupsd_subscription_t  *sub;           /* Subscription */
 
459
 
 
460
 
 
461
  if (!Subscriptions)
 
462
    return;
 
463
 
 
464
  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
 
465
       sub;
 
466
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
467
    cupsdDeleteSubscription(sub, 0);
 
468
 
 
469
  cupsArrayDelete(Subscriptions);
 
470
  Subscriptions = NULL;
 
471
}
 
472
 
 
473
 
 
474
/*
 
475
 * 'cupsdDeleteSubscription()' - Delete a subscription object.
 
476
 */
 
477
 
 
478
void
 
479
cupsdDeleteSubscription(
 
480
    cupsd_subscription_t *sub,          /* I - Subscription object */
 
481
    int                  update)        /* I - 1 = update subscriptions.conf */
 
482
{
 
483
  int   i;                              /* Looping var */
 
484
 
 
485
 
 
486
 /*
 
487
  * Close the pipe to the notifier as needed...
 
488
  */
 
489
 
 
490
  if (sub->pipe >= 0)
 
491
    close(sub->pipe);
 
492
 
 
493
 /*
 
494
  * Remove subscription from array...
 
495
  */
 
496
 
 
497
  cupsArrayRemove(Subscriptions, sub);
 
498
 
 
499
 /*
 
500
  * Free memory...
 
501
  */
 
502
 
 
503
  cupsdClearString(&(sub->owner));
 
504
  cupsdClearString(&(sub->recipient));
 
505
 
 
506
  if (sub->events)
 
507
  {
 
508
    for (i = 0; i < sub->num_events; i ++)
 
509
      cupsd_delete_event(sub->events[i]);
 
510
 
 
511
    free(sub->events);
 
512
  }
 
513
 
 
514
  free(sub);
 
515
 
 
516
 /*
 
517
  * Update the subscriptions as needed...
 
518
  */
 
519
 
 
520
  if (update)
 
521
    cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
 
522
}
 
523
 
 
524
 
 
525
/*
 
526
 * 'cupsdEventName()' - Return a single event name.
 
527
 */
 
528
 
 
529
const char *                            /* O - Event name */
 
530
cupsdEventName(
 
531
    cupsd_eventmask_t event)            /* I - Event value */
 
532
{
 
533
  switch (event)
 
534
  {
 
535
    default :
 
536
        return (NULL);
 
537
 
 
538
    case CUPSD_EVENT_PRINTER_RESTARTED :
 
539
        return ("printer-restarted");
 
540
 
 
541
    case CUPSD_EVENT_PRINTER_SHUTDOWN :
 
542
        return ("printer-shutdown");
 
543
 
 
544
    case CUPSD_EVENT_PRINTER_STOPPED :
 
545
        return ("printer-stopped");
 
546
 
 
547
    case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
 
548
        return ("printer-finishings-changed");
 
549
 
 
550
    case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
 
551
        return ("printer-media-changed");
 
552
 
 
553
    case CUPSD_EVENT_PRINTER_ADDED :
 
554
        return ("printer-added");
 
555
 
 
556
    case CUPSD_EVENT_PRINTER_DELETED :
 
557
        return ("printer-deleted");
 
558
 
 
559
    case CUPSD_EVENT_PRINTER_MODIFIED :
 
560
        return ("printer-modified");
 
561
 
 
562
    case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
 
563
        return ("printer-queue-order-changed");
 
564
 
 
565
    case CUPSD_EVENT_PRINTER_STATE :
 
566
    case CUPSD_EVENT_PRINTER_STATE_CHANGED :
 
567
        return ("printer-state-changed");
 
568
 
 
569
    case CUPSD_EVENT_PRINTER_CONFIG :
 
570
    case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
 
571
        return ("printer-config-changed");
 
572
 
 
573
    case CUPSD_EVENT_PRINTER_CHANGED :
 
574
        return ("printer-changed");
 
575
 
 
576
    case CUPSD_EVENT_JOB_CREATED :
 
577
        return ("job-created");
 
578
 
 
579
    case CUPSD_EVENT_JOB_COMPLETED :
 
580
        return ("job-completed");
 
581
 
 
582
    case CUPSD_EVENT_JOB_STOPPED :
 
583
        return ("job-stopped");
 
584
 
 
585
    case CUPSD_EVENT_JOB_CONFIG_CHANGED :
 
586
        return ("job-config-changed");
 
587
 
 
588
    case CUPSD_EVENT_JOB_PROGRESS :
 
589
        return ("job-progress");
 
590
 
 
591
    case CUPSD_EVENT_JOB_STATE :
 
592
    case CUPSD_EVENT_JOB_STATE_CHANGED :
 
593
        return ("job-state-changed");
 
594
 
 
595
    case CUPSD_EVENT_SERVER_RESTARTED :
 
596
        return ("server-restarted");
 
597
 
 
598
    case CUPSD_EVENT_SERVER_STARTED :
 
599
        return ("server-started");
 
600
 
 
601
    case CUPSD_EVENT_SERVER_STOPPED :
 
602
        return ("server-stopped");
 
603
 
 
604
    case CUPSD_EVENT_SERVER_AUDIT :
 
605
        return ("server-audit");
 
606
 
 
607
    case CUPSD_EVENT_ALL :
 
608
        return ("all");
 
609
  }
 
610
}
 
611
 
 
612
 
 
613
/*
 
614
 * 'cupsdEventValue()' - Return the event mask value for a name.
 
615
 */
 
616
 
 
617
cupsd_eventmask_t                       /* O - Event mask value */
 
618
cupsdEventValue(const char *name)       /* I - Name of event */
 
619
{
 
620
  if (!strcmp(name, "all"))
 
621
    return (CUPSD_EVENT_ALL);
 
622
  else if (!strcmp(name, "printer-restarted"))
 
623
    return (CUPSD_EVENT_PRINTER_RESTARTED);
 
624
  else if (!strcmp(name, "printer-shutdown"))
 
625
    return (CUPSD_EVENT_PRINTER_SHUTDOWN);
 
626
  else if (!strcmp(name, "printer-stopped"))
 
627
    return (CUPSD_EVENT_PRINTER_STOPPED);
 
628
  else if (!strcmp(name, "printer-finishings-changed"))
 
629
    return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
 
630
  else if (!strcmp(name, "printer-media-changed"))
 
631
    return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
 
632
  else if (!strcmp(name, "printer-added"))
 
633
    return (CUPSD_EVENT_PRINTER_ADDED);
 
634
  else if (!strcmp(name, "printer-deleted"))
 
635
    return (CUPSD_EVENT_PRINTER_DELETED);
 
636
  else if (!strcmp(name, "printer-modified"))
 
637
    return (CUPSD_EVENT_PRINTER_MODIFIED);
 
638
  else if (!strcmp(name, "printer-queue-order-changed"))
 
639
    return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
 
640
  else if (!strcmp(name, "printer-state-changed"))
 
641
    return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
 
642
  else if (!strcmp(name, "printer-config-changed"))
 
643
    return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
 
644
  else if (!strcmp(name, "printer-changed"))
 
645
    return (CUPSD_EVENT_PRINTER_CHANGED);
 
646
  else if (!strcmp(name, "job-created"))
 
647
    return (CUPSD_EVENT_JOB_CREATED);
 
648
  else if (!strcmp(name, "job-completed"))
 
649
    return (CUPSD_EVENT_JOB_COMPLETED);
 
650
  else if (!strcmp(name, "job-stopped"))
 
651
    return (CUPSD_EVENT_JOB_STOPPED);
 
652
  else if (!strcmp(name, "job-config-changed"))
 
653
    return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
 
654
  else if (!strcmp(name, "job-progress"))
 
655
    return (CUPSD_EVENT_JOB_PROGRESS);
 
656
  else if (!strcmp(name, "job-state-changed"))
 
657
    return (CUPSD_EVENT_JOB_STATE_CHANGED);
 
658
  else if (!strcmp(name, "server-restarted"))
 
659
    return (CUPSD_EVENT_SERVER_RESTARTED);
 
660
  else if (!strcmp(name, "server-started"))
 
661
    return (CUPSD_EVENT_SERVER_STARTED);
 
662
  else if (!strcmp(name, "server-stopped"))
 
663
    return (CUPSD_EVENT_SERVER_STOPPED);
 
664
  else if (!strcmp(name, "server-audit"))
 
665
    return (CUPSD_EVENT_SERVER_AUDIT);
 
666
  else
 
667
    return (CUPSD_EVENT_NONE);
 
668
}
 
669
 
 
670
 
 
671
/*
 
672
 * 'cupsdExpireSubscriptions()' - Expire old subscription objects.
 
673
 */
 
674
 
 
675
void
 
676
cupsdExpireSubscriptions(
 
677
    cupsd_printer_t *dest,              /* I - Printer, if any */
 
678
    cupsd_job_t     *job)               /* I - Job, if any */
 
679
{
 
680
  cupsd_subscription_t  *sub;           /* Current subscription */
 
681
  int                   update;         /* Update subscriptions.conf? */
 
682
  time_t                curtime;        /* Current time */
 
683
 
 
684
 
 
685
  curtime = time(NULL);
 
686
  update  = 0;
 
687
 
 
688
  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
 
689
       sub;
 
690
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
691
    if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
 
692
        (dest && sub->dest == dest) ||
 
693
        (job && sub->job == job))
 
694
    {
 
695
      cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
 
696
                      sub->id);
 
697
 
 
698
      cupsdDeleteSubscription(sub, 0);
 
699
 
 
700
      update = 1;
 
701
    }
 
702
 
 
703
  if (update)
 
704
    cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
 
705
}
 
706
 
 
707
 
 
708
/*
 
709
 * 'cupsdFindSubscription()' - Find a subscription by ID.
 
710
 */
 
711
 
 
712
cupsd_subscription_t *                  /* O - Subscription object */
 
713
cupsdFindSubscription(int id)           /* I - Subscription ID */
 
714
{
 
715
  cupsd_subscription_t  sub;            /* Subscription template */
 
716
 
 
717
 
 
718
  sub.id = id;
 
719
 
 
720
  return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
 
721
}
 
722
 
 
723
 
 
724
/*
 
725
 * 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
 
726
 */
 
727
 
 
728
void
 
729
cupsdLoadAllSubscriptions(void)
 
730
{
 
731
  int                   i;              /* Looping var */
 
732
  cups_file_t           *fp;            /* subscriptions.conf file */
 
733
  int                   linenum;        /* Current line number */
 
734
  char                  line[1024],     /* Line from file */
 
735
                        *value,         /* Pointer to value */
 
736
                        *valueptr;      /* Pointer into value */
 
737
  cupsd_subscription_t  *sub;           /* Current subscription */
 
738
  int                   hex;            /* Non-zero if reading hex data */
 
739
  int                   delete_sub;     /* Delete subscription? */
 
740
 
 
741
 
 
742
 /*
 
743
  * Open the subscriptions.conf file...
 
744
  */
 
745
 
 
746
  snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
 
747
  if ((fp = cupsFileOpen(line, "r")) == NULL)
 
748
  {
 
749
    if (errno != ENOENT)
 
750
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
751
                      "LoadAllSubscriptions: Unable to open %s - %s", line,
 
752
                      strerror(errno));
 
753
    return;
 
754
  }
 
755
 
 
756
 /*
 
757
  * Read all of the lines from the file...
 
758
  */
 
759
 
 
760
  linenum    = 0;
 
761
  sub        = NULL;
 
762
  delete_sub = 0;
 
763
 
 
764
  while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
 
765
  {
 
766
    if (!strcasecmp(line, "NextSubscriptionId") && value)
 
767
    {
 
768
     /*
 
769
      * NextSubscriptionId NNN
 
770
      */
 
771
 
 
772
      i = atoi(value);
 
773
      if (i >= NextSubscriptionId && i > 0)
 
774
        NextSubscriptionId = i;
 
775
    }
 
776
    else if (!strcasecmp(line, "<Subscription"))
 
777
    {
 
778
     /*
 
779
      * <Subscription #>
 
780
      */
 
781
 
 
782
      if (!sub && value && isdigit(value[0] & 255))
 
783
      {
 
784
        sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
 
785
                                   atoi(value));
 
786
      }
 
787
      else
 
788
      {
 
789
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
790
                        "Syntax error on line %d of subscriptions.conf.",
 
791
                        linenum);
 
792
        break;
 
793
      }
 
794
    }
 
795
    else if (!strcasecmp(line, "</Subscription>"))
 
796
    {
 
797
      if (!sub)
 
798
      {
 
799
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
800
                        "Syntax error on line %d of subscriptions.conf.",
 
801
                        linenum);
 
802
        break;
 
803
      }
 
804
 
 
805
      if (delete_sub)
 
806
        cupsdDeleteSubscription(sub, 0);
 
807
 
 
808
      sub        = NULL;
 
809
      delete_sub = 0;
 
810
    }
 
811
    else if (!sub)
 
812
    {
 
813
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
814
                      "Syntax error on line %d of subscriptions.conf.",
 
815
                      linenum);
 
816
    }
 
817
    else if (!strcasecmp(line, "Events"))
 
818
    {
 
819
     /*
 
820
      * Events name
 
821
      * Events name name name ...
 
822
      */
 
823
 
 
824
      if (!value)
 
825
      {
 
826
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
827
                        "Syntax error on line %d of subscriptions.conf.",
 
828
                        linenum);
 
829
        break;
 
830
      }
 
831
 
 
832
      while (*value)
 
833
      {
 
834
       /*
 
835
        * Separate event names...
 
836
        */
 
837
 
 
838
        for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
 
839
 
 
840
        while (isspace(*valueptr & 255))
 
841
          *valueptr++ = '\0';
 
842
 
 
843
       /*
 
844
        * See if the name exists...
 
845
        */
 
846
 
 
847
        if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
 
848
        {
 
849
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
850
                          "Unknown event name \'%s\' on line %d of subscriptions.conf.",
 
851
                          value, linenum);
 
852
          break;
 
853
        }
 
854
 
 
855
        value = valueptr;
 
856
      }
 
857
    }
 
858
    else if (!strcasecmp(line, "Owner"))
 
859
    {
 
860
     /*
 
861
      * Owner
 
862
      */
 
863
 
 
864
      if (value)
 
865
        cupsdSetString(&sub->owner, value);
 
866
      else
 
867
      {
 
868
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
869
                        "Syntax error on line %d of subscriptions.conf.",
 
870
                        linenum);
 
871
        break;
 
872
      }
 
873
    }
 
874
    else if (!strcasecmp(line, "Recipient"))
 
875
    {
 
876
     /*
 
877
      * Recipient uri
 
878
      */
 
879
 
 
880
      if (value)
 
881
        cupsdSetString(&sub->recipient, value);
 
882
      else
 
883
      {
 
884
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
885
                        "Syntax error on line %d of subscriptions.conf.",
 
886
                        linenum);
 
887
        break;
 
888
      }
 
889
    }
 
890
    else if (!strcasecmp(line, "JobId"))
 
891
    {
 
892
     /*
 
893
      * JobId #
 
894
      */
 
895
 
 
896
      if (value && isdigit(*value & 255))
 
897
      {
 
898
        if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
 
899
        {
 
900
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
901
                          "Job %s not found on line %d of subscriptions.conf.",
 
902
                          value, linenum);
 
903
          delete_sub = 1;
 
904
        }
 
905
      }
 
906
      else
 
907
      {
 
908
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
909
                        "Syntax error on line %d of subscriptions.conf.",
 
910
                        linenum);
 
911
        break;
 
912
      }
 
913
    }
 
914
    else if (!strcasecmp(line, "PrinterName"))
 
915
    {
 
916
     /*
 
917
      * PrinterName name
 
918
      */
 
919
 
 
920
      if (value)
 
921
      {
 
922
        if ((sub->dest = cupsdFindDest(value)) == NULL)
 
923
        {
 
924
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
925
                          "Printer \'%s\' not found on line %d of subscriptions.conf.",
 
926
                          value, linenum);
 
927
          delete_sub = 1;
 
928
        }
 
929
      }
 
930
      else
 
931
      {
 
932
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
933
                        "Syntax error on line %d of subscriptions.conf.",
 
934
                        linenum);
 
935
        break;
 
936
      }
 
937
    }
 
938
    else if (!strcasecmp(line, "UserData"))
 
939
    {
 
940
     /*
 
941
      * UserData encoded-string
 
942
      */
 
943
 
 
944
      if (value)
 
945
      {
 
946
        for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
 
947
        {
 
948
          if (*valueptr == '<' && !hex)
 
949
          {
 
950
            hex = 1;
 
951
            valueptr ++;
 
952
          }
 
953
 
 
954
          if (hex)
 
955
          {
 
956
            if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
 
957
            {
 
958
              if (isdigit(valueptr[0]))
 
959
                sub->user_data[i] = (valueptr[0] - '0') << 4;
 
960
              else
 
961
                sub->user_data[i] = (tolower(valueptr[0]) - 'a' + 10) << 4;
 
962
 
 
963
              if (isdigit(valueptr[1]))
 
964
                sub->user_data[i] |= valueptr[1] - '0';
 
965
              else
 
966
                sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
 
967
 
 
968
              valueptr += 2;
 
969
 
 
970
              if (*valueptr == '>')
 
971
              {
 
972
                hex = 0;
 
973
                valueptr ++;
 
974
              }
 
975
            }
 
976
            else
 
977
              break;
 
978
          }
 
979
          else
 
980
            sub->user_data[i] = *valueptr++;
 
981
        }
 
982
 
 
983
        if (*valueptr)
 
984
        {
 
985
          cupsdLogMessage(CUPSD_LOG_ERROR,
 
986
                          "Bad UserData \'%s\' on line %d of subscriptions.conf.",
 
987
                          value, linenum);
 
988
        }
 
989
        else
 
990
          sub->user_data_len = i;
 
991
      }
 
992
      else
 
993
      {
 
994
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
995
                        "Syntax error on line %d of subscriptions.conf.",
 
996
                        linenum);
 
997
        break;
 
998
      }
 
999
    }
 
1000
    else if (!strcasecmp(line, "LeaseDuration"))
 
1001
    {
 
1002
     /*
 
1003
      * LeaseDuration #
 
1004
      */
 
1005
 
 
1006
      if (value && isdigit(*value & 255))
 
1007
      {
 
1008
        sub->lease  = atoi(value);
 
1009
        sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
 
1010
      }
 
1011
      else
 
1012
      {
 
1013
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
1014
                        "Syntax error on line %d of subscriptions.conf.",
 
1015
                        linenum);
 
1016
        break;
 
1017
      }
 
1018
    }
 
1019
    else if (!strcasecmp(line, "Interval"))
 
1020
    {
 
1021
     /*
 
1022
      * Interval #
 
1023
      */
 
1024
 
 
1025
      if (value && isdigit(*value & 255))
 
1026
        sub->interval = atoi(value);
 
1027
      else
 
1028
      {
 
1029
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
1030
                        "Syntax error on line %d of subscriptions.conf.",
 
1031
                        linenum);
 
1032
        break;
 
1033
      }
 
1034
    }
 
1035
    else if (!strcasecmp(line, "ExpirationTime"))
 
1036
    {
 
1037
     /*
 
1038
      * ExpirationTime #
 
1039
      */
 
1040
 
 
1041
      if (value && isdigit(*value & 255))
 
1042
        sub->expire = atoi(value);
 
1043
      else
 
1044
      {
 
1045
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
1046
                        "Syntax error on line %d of subscriptions.conf.",
 
1047
                        linenum);
 
1048
        break;
 
1049
      }
 
1050
    }
 
1051
    else if (!strcasecmp(line, "NextEventId"))
 
1052
    {
 
1053
     /*
 
1054
      * NextEventId #
 
1055
      */
 
1056
 
 
1057
      if (value && isdigit(*value & 255))
 
1058
        sub->next_event_id = sub->first_event_id = atoi(value);
 
1059
      else
 
1060
      {
 
1061
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
1062
                        "Syntax error on line %d of subscriptions.conf.",
 
1063
                        linenum);
 
1064
        break;
 
1065
      }
 
1066
    }
 
1067
    else
 
1068
    {
 
1069
     /*
 
1070
      * Something else we don't understand...
 
1071
      */
 
1072
 
 
1073
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1074
                      "Unknown configuration directive %s on line %d of subscriptions.conf.",
 
1075
                      line, linenum);
 
1076
    }
 
1077
  }
 
1078
 
 
1079
  cupsFileClose(fp);
 
1080
}
 
1081
 
 
1082
 
 
1083
/*
 
1084
 * 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
 
1085
 */
 
1086
 
 
1087
void
 
1088
cupsdSaveAllSubscriptions(void)
 
1089
{
 
1090
  int                   i;              /* Looping var */
 
1091
  cups_file_t           *fp;            /* subscriptions.conf file */
 
1092
  char                  temp[1024];     /* Temporary string */
 
1093
  char                  backup[1024];   /* subscriptions.conf.O file */
 
1094
  cupsd_subscription_t  *sub;           /* Current subscription */
 
1095
  time_t                curtime;        /* Current time */
 
1096
  struct tm             *curdate;       /* Current date */
 
1097
  unsigned              mask;           /* Current event mask */
 
1098
  const char            *name;          /* Current event name */
 
1099
  int                   hex;            /* Non-zero if we are writing hex data */
 
1100
 
 
1101
 
 
1102
 /*
 
1103
  * Create the subscriptions.conf file...
 
1104
  */
 
1105
 
 
1106
  snprintf(temp, sizeof(temp), "%s/subscriptions.conf", ServerRoot);
 
1107
  snprintf(backup, sizeof(backup), "%s/subscriptions.conf.O", ServerRoot);
 
1108
 
 
1109
  if (rename(temp, backup))
 
1110
  {
 
1111
    if (errno != ENOENT)
 
1112
      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to backup subscriptions.conf - %s",
 
1113
                      strerror(errno));
 
1114
  }
 
1115
 
 
1116
  if ((fp = cupsFileOpen(temp, "w")) == NULL)
 
1117
  {
 
1118
    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to save subscriptions.conf - %s",
 
1119
                    strerror(errno));
 
1120
 
 
1121
    if (rename(backup, temp))
 
1122
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1123
                      "Unable to restore subscriptions.conf - %s",
 
1124
                      strerror(errno));
 
1125
    return;
 
1126
  }
 
1127
  else
 
1128
    cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
 
1129
 
 
1130
 /*
 
1131
  * Restrict access to the file...
 
1132
  */
 
1133
 
 
1134
  fchown(cupsFileNumber(fp), getuid(), Group);
 
1135
  fchmod(cupsFileNumber(fp), ConfigFilePerm);
 
1136
 
 
1137
 /*
 
1138
  * Write a small header to the file...
 
1139
  */
 
1140
 
 
1141
  curtime = time(NULL);
 
1142
  curdate = localtime(&curtime);
 
1143
  strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
 
1144
 
 
1145
  cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
 
1146
  cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
 
1147
 
 
1148
  cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
 
1149
 
 
1150
 /*
 
1151
  * Write every subscription known to the system...
 
1152
  */
 
1153
 
 
1154
  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
 
1155
       sub;
 
1156
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
1157
  {
 
1158
    cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
 
1159
 
 
1160
    if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
 
1161
    {
 
1162
     /*
 
1163
      * Simple event list...
 
1164
      */
 
1165
 
 
1166
      cupsFilePrintf(fp, "Events %s\n", name);
 
1167
    }
 
1168
    else
 
1169
    {
 
1170
     /*
 
1171
      * Complex event list...
 
1172
      */
 
1173
 
 
1174
      cupsFilePuts(fp, "Events");
 
1175
 
 
1176
      for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
 
1177
        if (sub->mask & mask)
 
1178
          cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
 
1179
 
 
1180
      cupsFilePuts(fp, "\n");
 
1181
    }
 
1182
 
 
1183
    if (sub->owner)
 
1184
      cupsFilePrintf(fp, "Owner %s\n", sub->owner);
 
1185
    if (sub->recipient)
 
1186
      cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
 
1187
    if (sub->job)
 
1188
      cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
 
1189
    if (sub->dest)
 
1190
      cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
 
1191
 
 
1192
    if (sub->user_data_len > 0)
 
1193
    {
 
1194
      cupsFilePuts(fp, "UserData ");
 
1195
 
 
1196
      for (i = 0, hex = 0; i < sub->user_data_len; i ++)
 
1197
      {
 
1198
        if (sub->user_data[i] < ' ' ||
 
1199
            sub->user_data[i] > 0x7f ||
 
1200
            sub->user_data[i] == '<')
 
1201
        {
 
1202
          if (!hex)
 
1203
          {
 
1204
            cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
 
1205
            hex = 1;
 
1206
          }
 
1207
          else
 
1208
            cupsFilePrintf(fp, "%02X", sub->user_data[i]);
 
1209
        }
 
1210
        else
 
1211
        {
 
1212
          if (hex)
 
1213
          {
 
1214
            cupsFilePrintf(fp, ">%c", sub->user_data[i]);
 
1215
            hex = 0;
 
1216
          }
 
1217
          else
 
1218
            cupsFilePutChar(fp, sub->user_data[i]);
 
1219
        }
 
1220
      }
 
1221
 
 
1222
      if (hex)
 
1223
        cupsFilePuts(fp, ">\n");
 
1224
      else
 
1225
        cupsFilePutChar(fp, '\n');
 
1226
    }
 
1227
 
 
1228
    cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
 
1229
    cupsFilePrintf(fp, "Interval %d\n", sub->interval);
 
1230
    cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
 
1231
    cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
 
1232
 
 
1233
    cupsFilePuts(fp, "</Subscription>\n");
 
1234
  }
 
1235
 
 
1236
  cupsFileClose(fp);
 
1237
}
 
1238
 
 
1239
 
 
1240
/*
 
1241
 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
 
1242
 */
 
1243
 
 
1244
void
 
1245
cupsdStopAllNotifiers(void)
 
1246
{
 
1247
  cupsd_subscription_t  *sub;           /* Current subscription */
 
1248
 
 
1249
 
 
1250
 /*
 
1251
  * See if we have started any notifiers...
 
1252
  */
 
1253
 
 
1254
  if (!NotifierStatusBuffer)
 
1255
    return;
 
1256
 
 
1257
 /*
 
1258
  * Yes, kill any processes that are left...
 
1259
  */
 
1260
 
 
1261
  for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
 
1262
       sub;
 
1263
       sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
 
1264
    if (sub->pid)
 
1265
    {
 
1266
      cupsdEndProcess(sub->pid, 0);
 
1267
 
 
1268
      close(sub->pipe);
 
1269
      sub->pipe = -1;
 
1270
    }
 
1271
 
 
1272
 /*
 
1273
  * Close the status pipes...
 
1274
  */
 
1275
 
 
1276
  if (NotifierPipes[0] >= 0)
 
1277
  {
 
1278
    cupsdRemoveSelect(NotifierPipes[0]);
 
1279
 
 
1280
    cupsdStatBufDelete(NotifierStatusBuffer);
 
1281
 
 
1282
    close(NotifierPipes[0]);
 
1283
    close(NotifierPipes[1]);
 
1284
 
 
1285
    NotifierPipes[0] = -1;
 
1286
    NotifierPipes[1] = -1;
 
1287
    NotifierStatusBuffer = NULL;
 
1288
  }
 
1289
}
 
1290
 
 
1291
 
 
1292
/*
 
1293
 * 'cupsd_compare_subscriptions()' - Compare two subscriptions.
 
1294
 */
 
1295
 
 
1296
static int                              /* O - Result of comparison */
 
1297
cupsd_compare_subscriptions(
 
1298
    cupsd_subscription_t *first,        /* I - First subscription object */
 
1299
    cupsd_subscription_t *second,       /* I - Second subscription object */
 
1300
    void                 *unused)       /* I - Unused user data pointer */
 
1301
{
 
1302
  (void)unused;
 
1303
 
 
1304
  return (first->id - second->id);
 
1305
}
 
1306
 
 
1307
 
 
1308
/*
 
1309
 * 'cupsd_delete_event()' - Delete a single event...
 
1310
 *
 
1311
 * Oldest events must be deleted first, otherwise the subscription cache
 
1312
 * flushing code will not work properly.
 
1313
 */
 
1314
 
 
1315
static void
 
1316
cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
 
1317
{
 
1318
 /*
 
1319
  * Free memory...
 
1320
  */
 
1321
 
 
1322
  ippDelete(event->attrs);
 
1323
  free(event);
 
1324
}
 
1325
 
 
1326
 
 
1327
#ifdef HAVE_DBUS
 
1328
/*
 
1329
 * 'cupsd_send_dbus()' - Send a DBUS notification...
 
1330
 */
 
1331
 
 
1332
static void
 
1333
cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
 
1334
                cupsd_printer_t   *dest,/* I - Destination, if any */
 
1335
                cupsd_job_t       *job) /* I - Job, if any */
 
1336
{
 
1337
  DBusError             error;          /* Error, if any */
 
1338
  DBusMessage           *message;       /* Message to send */
 
1339
  DBusMessageIter       iter;           /* Iterator for message data */
 
1340
  const char            *what;          /* What to send */
 
1341
  static DBusConnection *con = NULL;    /* Connection to DBUS server */
 
1342
 
 
1343
 
 
1344
 /*
 
1345
  * Figure out what to send, if anything...
 
1346
  */
 
1347
 
 
1348
  if (event & CUPSD_EVENT_PRINTER_ADDED)
 
1349
    what = "PrinterAdded";
 
1350
  else if (event & CUPSD_EVENT_PRINTER_DELETED)
 
1351
    what = "PrinterRemoved";
 
1352
  else if (event & CUPSD_EVENT_PRINTER_CHANGED)
 
1353
    what = "QueueChanged";
 
1354
  else if (event & CUPSD_EVENT_JOB_CREATED)
 
1355
    what = "JobQueuedLocal";
 
1356
  else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
 
1357
           job->state_value == IPP_JOB_PROCESSING)
 
1358
    what = "JobStartedLocal";
 
1359
  else
 
1360
    return;
 
1361
 
 
1362
 /*
 
1363
  * Verify connection to DBUS server...
 
1364
  */
 
1365
 
 
1366
  if (con && !dbus_connection_get_is_connected(con))
 
1367
  {
 
1368
    dbus_connection_unref(con);
 
1369
    con = NULL;
 
1370
  }
 
1371
 
 
1372
  if (!con)
 
1373
  {
 
1374
    dbus_error_init(&error);
 
1375
 
 
1376
    con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
 
1377
    if (!con)
 
1378
    {
 
1379
      dbus_error_free(&error);
 
1380
      return;
 
1381
    }
 
1382
  }
 
1383
 
 
1384
 /*
 
1385
  * Create and send the new message...
 
1386
  */
 
1387
 
 
1388
  message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
 
1389
                                    "com.redhat.PrinterSpooler", what);
 
1390
 
 
1391
  dbus_message_append_iter_init(message, &iter);
 
1392
  if (dest)
 
1393
    dbus_message_iter_append_string(&iter, dest->name);
 
1394
  if (job)
 
1395
  {
 
1396
    dbus_message_iter_append_uint32(&iter, job->id);
 
1397
    dbus_message_iter_append_string(&iter, job->username);
 
1398
  }
 
1399
 
 
1400
  dbus_connection_send(con, message, NULL);
 
1401
  dbus_connection_flush(con);
 
1402
  dbus_message_unref(message);
 
1403
}
 
1404
#endif /* HAVE_DBUS */
 
1405
 
 
1406
 
 
1407
/*
 
1408
 * 'cupsd_send_notification()' - Send a notification for the specified event.
 
1409
 */
 
1410
 
 
1411
static void
 
1412
cupsd_send_notification(
 
1413
    cupsd_subscription_t *sub,          /* I - Subscription object */
 
1414
    cupsd_event_t        *event)        /* I - Event to send */
 
1415
{
 
1416
  ipp_state_t   state;                  /* IPP event state */
 
1417
 
 
1418
 
 
1419
  cupsdLogMessage(CUPSD_LOG_DEBUG2,
 
1420
                  "cupsd_send_notification(sub=%p(%d), event=%p(%s))",
 
1421
                  sub, sub->id, event, cupsdEventName(event->event));
 
1422
 
 
1423
 /*
 
1424
  * Allocate the events array as needed...
 
1425
  */
 
1426
 
 
1427
  if (!sub->events)
 
1428
  {
 
1429
    sub->events = calloc(MaxEvents, sizeof(cupsd_event_t *));
 
1430
 
 
1431
    if (!sub->events)
 
1432
    {
 
1433
      cupsdLogMessage(CUPSD_LOG_CRIT,
 
1434
                      "Unable to allocate memory for subscription #%d!",
 
1435
                      sub->id);
 
1436
      return;
 
1437
    }
 
1438
  }
 
1439
 
 
1440
 /*
 
1441
  * Purge an old event as needed...
 
1442
  */
 
1443
 
 
1444
  if (sub->num_events >= MaxEvents)
 
1445
  {
 
1446
   /*
 
1447
    * Purge the oldest event in the cache...
 
1448
    */
 
1449
 
 
1450
    cupsd_delete_event(sub->events[0]);
 
1451
 
 
1452
    sub->num_events --;
 
1453
    sub->first_event_id ++;
 
1454
 
 
1455
    memmove(sub->events, sub->events + 1,
 
1456
            sub->num_events * sizeof(cupsd_event_t *));
 
1457
  }
 
1458
 
 
1459
 /*
 
1460
  * Add the event to the subscription.  Since the events array is
 
1461
  * always MaxEvents in length, and since we will have already
 
1462
  * removed an event from the subscription cache if we hit the
 
1463
  * event cache limit, we don't need to check for overflow here...
 
1464
  */
 
1465
 
 
1466
  sub->events[sub->num_events] = event;
 
1467
  sub->num_events ++;
 
1468
 
 
1469
 /*
 
1470
  * Deliver the event...
 
1471
  */
 
1472
 
 
1473
  if (sub->recipient)
 
1474
  {
 
1475
    for (;;)
 
1476
    {
 
1477
      if (sub->pipe < 0)
 
1478
        cupsd_start_notifier(sub);
 
1479
 
 
1480
      cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
 
1481
 
 
1482
      if (sub->pipe < 0)
 
1483
        break;
 
1484
 
 
1485
      event->attrs->state = IPP_IDLE;
 
1486
 
 
1487
      while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
 
1488
        if (state == IPP_ERROR)
 
1489
          break;
 
1490
 
 
1491
      if (state == IPP_ERROR)
 
1492
      {
 
1493
        if (errno == EPIPE)
 
1494
        {
 
1495
         /*
 
1496
          * Notifier died, try restarting it...
 
1497
          */
 
1498
 
 
1499
          cupsdLogMessage(CUPSD_LOG_WARN,
 
1500
                          "Notifier for subscription %d (%s) went away, "
 
1501
                          "retrying!",
 
1502
                          sub->id, sub->recipient);
 
1503
          cupsdEndProcess(sub->pid, 0);
 
1504
 
 
1505
          close(sub->pipe);
 
1506
          sub->pipe = -1;
 
1507
          continue;
 
1508
        }
 
1509
 
 
1510
        cupsdLogMessage(CUPSD_LOG_ERROR,
 
1511
                        "Unable to send event for subscription %d (%s)!",
 
1512
                        sub->id, sub->recipient);
 
1513
      }
 
1514
 
 
1515
     /*
 
1516
      * If we get this far, break out of the loop...
 
1517
      */
 
1518
 
 
1519
      break;
 
1520
    }
 
1521
  }
 
1522
 
 
1523
 /*
 
1524
  * Bump the event sequence number...
 
1525
  */
 
1526
 
 
1527
  sub->next_event_id ++;
 
1528
}
 
1529
 
 
1530
 
 
1531
/*
 
1532
 * 'cupsd_start_notifier()' - Start a notifier subprocess...
 
1533
 */
 
1534
 
 
1535
static void
 
1536
cupsd_start_notifier(
 
1537
    cupsd_subscription_t *sub)          /* I - Subscription object */
 
1538
{
 
1539
  int   pid;                            /* Notifier process ID */
 
1540
  int   fds[2];                         /* Pipe file descriptors */
 
1541
  char  *argv[4],                       /* Command-line arguments */
 
1542
        *envp[MAX_ENV],                 /* Environment variables */
 
1543
        user_data[128],                 /* Base-64 encoded user data */
 
1544
        scheme[256],                    /* notify-recipient-uri scheme */
 
1545
        *ptr,                           /* Pointer into scheme */
 
1546
        command[1024];                  /* Notifier command */
 
1547
 
 
1548
 
 
1549
 /*
 
1550
  * Extract the scheme name from the recipient URI and point to the
 
1551
  * notifier program...
 
1552
  */
 
1553
 
 
1554
  strlcpy(scheme, sub->recipient, sizeof(scheme));
 
1555
  if ((ptr = strchr(scheme, ':')) != NULL)
 
1556
    *ptr = '\0';
 
1557
 
 
1558
  snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
 
1559
 
 
1560
 /*
 
1561
  * Base-64 encode the user data...
 
1562
  */
 
1563
 
 
1564
  httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
 
1565
                 sub->user_data_len);
 
1566
 
 
1567
 /*
 
1568
  * Setup the argument array...
 
1569
  */
 
1570
 
 
1571
  argv[0] = command;
 
1572
  argv[1] = sub->recipient;
 
1573
  argv[2] = user_data;
 
1574
  argv[3] = NULL;
 
1575
 
 
1576
 /*
 
1577
  * Setup the environment...
 
1578
  */
 
1579
 
 
1580
  cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
 
1581
 
 
1582
 /*
 
1583
  * Create pipes as needed...
 
1584
  */
 
1585
 
 
1586
  if (!NotifierStatusBuffer)
 
1587
  {
 
1588
   /*
 
1589
    * Create the status pipe...
 
1590
    */
 
1591
 
 
1592
    if (cupsdOpenPipe(NotifierPipes))
 
1593
    {
 
1594
      cupsdLogMessage(CUPSD_LOG_ERROR,
 
1595
                      "Unable to create pipes for notifier status - %s",
 
1596
                      strerror(errno));
 
1597
      return;
 
1598
    }
 
1599
 
 
1600
    NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
 
1601
 
 
1602
    cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
 
1603
                   NULL, NULL);
 
1604
  }
 
1605
 
 
1606
  if (cupsdOpenPipe(fds))
 
1607
  {
 
1608
    cupsdLogMessage(CUPSD_LOG_ERROR,
 
1609
                    "Unable to create pipes for notifier %s - %s",
 
1610
                    scheme, strerror(errno));
 
1611
    return;
 
1612
  }
 
1613
 
 
1614
 /*
 
1615
  * Make sure the delivery pipe is non-blocking...
 
1616
  */
 
1617
 
 
1618
  fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
 
1619
 
 
1620
 /*
 
1621
  * Create the notifier process...
 
1622
  */
 
1623
 
 
1624
  if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
 
1625
                        -1, -1, 0, DefaultProfile, NULL, &pid) < 0)
 
1626
  {
 
1627
   /*
 
1628
    * Error - can't fork!
 
1629
    */
 
1630
 
 
1631
    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
 
1632
                    scheme, strerror(errno));
 
1633
 
 
1634
    cupsdClosePipe(fds);
 
1635
  }
 
1636
  else
 
1637
  {
 
1638
   /*
 
1639
    * Fork successful - return the PID...
 
1640
    */
 
1641
 
 
1642
    cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
 
1643
                    scheme, pid);
 
1644
 
 
1645
    sub->pid    = pid;
 
1646
    sub->pipe   = fds[1];
 
1647
    sub->status = 0;
 
1648
 
 
1649
    close(fds[0]);
 
1650
  }
 
1651
}
 
1652
 
 
1653
 
 
1654
/*
 
1655
 * 'cupsd_update_notifier()' - Read messages from notifiers.
 
1656
 */
 
1657
 
 
1658
void
 
1659
cupsd_update_notifier(void)
 
1660
{
 
1661
  char          message[1024];          /* Pointer to message text */
 
1662
  int           loglevel;               /* Log level for message */
 
1663
 
 
1664
 
 
1665
  while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
 
1666
                            message, sizeof(message)))
 
1667
  {
 
1668
    if (loglevel == CUPSD_LOG_INFO)
 
1669
      cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
 
1670
 
 
1671
    if (!strchr(NotifierStatusBuffer->buffer, '\n'))
 
1672
      break;
 
1673
  }
 
1674
}
 
1675
 
 
1676
 
 
1677
/*
 
1678
 * End of "$Id: subscriptions.c 9445 2011-01-08 00:03:51Z mike $".
 
1679
 */