1/*
2 * "$Id: subscriptions.c 11093 2013-07-03 20:48:42Z msweet $"
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
56static int	cupsd_compare_subscriptions(cupsd_subscription_t *first,
57		                            cupsd_subscription_t *second,
58		                            void *unused);
59static void	cupsd_delete_event(cupsd_event_t *event);
60#ifdef HAVE_DBUS
61static void	cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
62		                cupsd_job_t *job);
63#endif /* HAVE_DBUS */
64static void	cupsd_send_notification(cupsd_subscription_t *sub,
65		                        cupsd_event_t *event);
66static void	cupsd_start_notifier(cupsd_subscription_t *sub);
67static void	cupsd_update_notifier(void);
68
69
70/*
71 * 'cupsdAddEvent()' - Add an event to the global event cache.
72 */
73
74void
75cupsdAddEvent(
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
150      if (dest)
151        temp->dest = dest;
152      else if (job)
153        temp->dest = dest = cupsdFindPrinter(job->dest);
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
315cupsd_subscription_t *			/* O - New subscription object */
316cupsdAddSubscription(
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
459void
460cupsdDeleteAllSubscriptions(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
482void
483cupsdDeleteSubscription(
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
507  cupsArrayDelete(sub->events);
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
524const char *				/* O - Event name */
525cupsdEventName(
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
612cupsd_eventmask_t			/* O - Event mask value */
613cupsdEventValue(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
670void
671cupsdExpireSubscriptions(
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
707cupsd_subscription_t *			/* O - Subscription object */
708cupsdFindSubscription(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
723void
724cupsdLoadAllSubscriptions(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);
742  if ((fp = cupsdOpenConfFile(line)) == NULL)
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  {
755    if (!_cups_strcasecmp(line, "NextSubscriptionId") && value)
756    {
757     /*
758      * NextSubscriptionId NNN
759      */
760
761      i = atoi(value);
762      if (i >= NextSubscriptionId && i > 0)
763        NextSubscriptionId = i;
764    }
765    else if (!_cups_strcasecmp(line, "<Subscription"))
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    }
784    else if (!_cups_strcasecmp(line, "</Subscription>"))
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    }
806    else if (!_cups_strcasecmp(line, "Events"))
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    }
847    else if (!_cups_strcasecmp(line, "Owner"))
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    }
863    else if (!_cups_strcasecmp(line, "Recipient"))
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    }
879    else if (!_cups_strcasecmp(line, "JobId"))
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    }
903    else if (!_cups_strcasecmp(line, "PrinterName"))
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    }
927    else if (!_cups_strcasecmp(line, "UserData"))
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    }
989    else if (!_cups_strcasecmp(line, "LeaseDuration"))
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    }
1008    else if (!_cups_strcasecmp(line, "Interval"))
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    }
1024    else if (!_cups_strcasecmp(line, "ExpirationTime"))
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    }
1040    else if (!_cups_strcasecmp(line, "NextEventId"))
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
1076void
1077cupsdSaveAllSubscriptions(void)
1078{
1079  int			i;		/* Looping var */
1080  cups_file_t		*fp;		/* subscriptions.conf file */
1081  char			filename[1024],	/* subscriptions.conf filename */
1082			temp[1024];	/* Temporary string */
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
1095  snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot);
1096
1097  if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
1098    return;
1099
1100  cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
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
1201  cupsdCloseCreatedConfFile(fp, filename);
1202}
1203
1204
1205/*
1206 * 'cupsdStopAllNotifiers()' - Stop all notifier processes.
1207 */
1208
1209void
1210cupsdStopAllNotifiers(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
1261static int				/* O - Result of comparison */
1262cupsd_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
1280static void
1281cupsd_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
1297static void
1298cupsd_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
1376static void
1377cupsd_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  {
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);
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
1412  if (cupsArrayCount(sub->events) >= MaxEvents)
1413  {
1414   /*
1415    * Purge the oldest event in the cache...
1416    */
1417
1418    cupsArrayRemove(sub->events, cupsArrayFirst(sub->events));
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
1430  cupsArrayAdd(sub->events, event);
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
1498static void
1499cupsd_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
1621void
1622cupsd_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/*
1641 * End of "$Id: subscriptions.c 11093 2013-07-03 20:48:42Z msweet $".
1642 */
1643