~ubuntu-branches/ubuntu/trusty/cups/trusty

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