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