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