|
|
/*
|
|
|
* Subscription routines for the CUPS scheduler.
|
|
|
*
|
|
|
* Copyright © 2007-2019 by Apple Inc.
|
|
|
* Copyright © 1997-2007 by Easy Software Products, all rights reserved.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
* information.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers...
|
|
|
*/
|
|
|
|
|
|
#include "cupsd.h"
|
|
|
#ifdef HAVE_DBUS
|
|
|
# include <dbus/dbus.h>
|
|
|
# ifdef HAVE_DBUS_MESSAGE_ITER_INIT_APPEND
|
|
|
# define dbus_message_append_iter_init dbus_message_iter_init_append
|
|
|
# define dbus_message_iter_append_string(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &(v))
|
|
|
# define dbus_message_iter_append_uint32(i,v) dbus_message_iter_append_basic(i, DBUS_TYPE_UINT32, &(v))
|
|
|
# endif /* HAVE_DBUS_MESSAGE_ITER_INIT_APPEND */
|
|
|
#endif /* HAVE_DBUS */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Local functions...
|
|
|
*/
|
|
|
|
|
|
static int cupsd_compare_subscriptions(cupsd_subscription_t *first,
|
|
|
cupsd_subscription_t *second,
|
|
|
void *unused);
|
|
|
static void cupsd_delete_event(cupsd_event_t *event);
|
|
|
#ifdef HAVE_DBUS
|
|
|
static void cupsd_send_dbus(cupsd_eventmask_t event, cupsd_printer_t *dest,
|
|
|
cupsd_job_t *job);
|
|
|
#endif /* HAVE_DBUS */
|
|
|
static void cupsd_send_notification(cupsd_subscription_t *sub,
|
|
|
cupsd_event_t *event);
|
|
|
static void cupsd_start_notifier(cupsd_subscription_t *sub);
|
|
|
static void cupsd_update_notifier(void);
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdAddEvent()' - Add an event to the global event cache.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdAddEvent(
|
|
|
cupsd_eventmask_t event, /* I - Event */
|
|
|
cupsd_printer_t *dest, /* I - Printer associated with event */
|
|
|
cupsd_job_t *job, /* I - Job associated with event */
|
|
|
const char *text, /* I - Notification text */
|
|
|
...) /* I - Additional arguments as needed */
|
|
|
{
|
|
|
va_list ap; /* Pointer to additional arguments */
|
|
|
char ftext[1024]; /* Formatted text buffer */
|
|
|
ipp_attribute_t *attr; /* Printer/job attribute */
|
|
|
cupsd_event_t *temp; /* New event pointer */
|
|
|
cupsd_subscription_t *sub; /* Current subscription */
|
|
|
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG2,
|
|
|
"cupsdAddEvent(event=%s, dest=%p(%s), job=%p(%d), text=\"%s\", ...)",
|
|
|
cupsdEventName(event), dest, dest ? dest->name : "",
|
|
|
job, job ? job->id : 0, text);
|
|
|
|
|
|
/*
|
|
|
* Keep track of events with any OS-supplied notification mechanisms...
|
|
|
*/
|
|
|
|
|
|
LastEvent |= event;
|
|
|
|
|
|
#ifdef HAVE_DBUS
|
|
|
cupsd_send_dbus(event, dest, job);
|
|
|
#endif /* HAVE_DBUS */
|
|
|
|
|
|
/*
|
|
|
* Return if we aren't keeping events...
|
|
|
*/
|
|
|
|
|
|
if (MaxEvents <= 0)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_WARN,
|
|
|
"cupsdAddEvent: Discarding %s event since MaxEvents is %d!",
|
|
|
cupsdEventName(event), MaxEvents);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Then loop through the subscriptions and add the event to the corresponding
|
|
|
* caches...
|
|
|
*/
|
|
|
|
|
|
for (temp = NULL, sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
|
|
|
sub;
|
|
|
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
{
|
|
|
/*
|
|
|
* Check if this subscription requires this event...
|
|
|
*/
|
|
|
|
|
|
if ((sub->mask & event) != 0 && (sub->dest == dest || !sub->dest || sub->job == job))
|
|
|
{
|
|
|
/*
|
|
|
* Need this event, so create a new event record...
|
|
|
*/
|
|
|
|
|
|
if ((temp = (cupsd_event_t *)calloc(1, sizeof(cupsd_event_t))) == NULL)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_CRIT,
|
|
|
"Unable to allocate memory for event - %s",
|
|
|
strerror(errno));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
temp->event = event;
|
|
|
temp->time = time(NULL);
|
|
|
temp->attrs = ippNew();
|
|
|
temp->job = job;
|
|
|
|
|
|
if (dest)
|
|
|
temp->dest = dest;
|
|
|
else if (job)
|
|
|
temp->dest = dest = cupsdFindPrinter(job->dest);
|
|
|
|
|
|
/*
|
|
|
* Add common event notification attributes...
|
|
|
*/
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_CHARSET,
|
|
|
"notify-charset", NULL, "utf-8");
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_LANGUAGE,
|
|
|
"notify-natural-language", NULL, "en-US");
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
|
|
|
"notify-subscription-id", sub->id);
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
|
|
|
"notify-sequence-number", sub->next_event_id);
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD,
|
|
|
"notify-subscribed-event", NULL, cupsdEventName(event));
|
|
|
|
|
|
if (sub->user_data_len > 0)
|
|
|
ippAddOctetString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION,
|
|
|
"notify-user-data", sub->user_data,
|
|
|
sub->user_data_len);
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER,
|
|
|
"printer-up-time", time(NULL));
|
|
|
|
|
|
va_start(ap, text);
|
|
|
vsnprintf(ftext, sizeof(ftext), text, ap);
|
|
|
va_end(ap);
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT,
|
|
|
"notify-text", NULL, ftext);
|
|
|
|
|
|
if (dest)
|
|
|
{
|
|
|
/*
|
|
|
* Add printer attributes...
|
|
|
*/
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-printer-uri", NULL, dest->uri);
|
|
|
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "printer-name", NULL, dest->name);
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "printer-state", (int)dest->state);
|
|
|
|
|
|
if (dest->num_reasons == 0)
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "printer-state-reasons", NULL, dest->state == IPP_PRINTER_STOPPED ? "paused" : "none");
|
|
|
else
|
|
|
ippAddStrings(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "printer-state-reasons", dest->num_reasons, NULL, (const char * const *)dest->reasons);
|
|
|
|
|
|
ippAddBoolean(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, "printer-is-accepting-jobs", (char)dest->accepting);
|
|
|
}
|
|
|
|
|
|
if (job)
|
|
|
{
|
|
|
/*
|
|
|
* Add job attributes...
|
|
|
*/
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-job-id", job->id);
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_ENUM, "job-state", (int)job->state_value);
|
|
|
|
|
|
if ((attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-name", NULL, attr->values[0].string.text);
|
|
|
|
|
|
switch (job->state_value)
|
|
|
{
|
|
|
case IPP_JOB_PENDING :
|
|
|
if (dest && dest->state == IPP_PRINTER_STOPPED)
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "printer-stopped");
|
|
|
else
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "none");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_HELD :
|
|
|
if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
|
|
|
ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-hold-until-specified");
|
|
|
else
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-incoming");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_PROCESSING :
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-printing");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_STOPPED :
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-stopped");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_CANCELED :
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-canceled-by-user");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_ABORTED :
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "aborted-by-system");
|
|
|
break;
|
|
|
|
|
|
case IPP_JOB_COMPLETED :
|
|
|
ippAddString(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_KEYWORD, "job-state-reasons", NULL, "job-completed-successfully");
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
ippAddInteger(temp->attrs, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "job-impressions-completed", job->sheets ? job->sheets->values[0].integer : 0);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Send the notification for this subscription...
|
|
|
*/
|
|
|
|
|
|
cupsd_send_notification(sub, temp);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (temp)
|
|
|
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
|
|
|
else
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG, "Discarding unused %s event...", cupsdEventName(event));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdAddSubscription()' - Add a new subscription object.
|
|
|
*/
|
|
|
|
|
|
cupsd_subscription_t * /* O - New subscription object */
|
|
|
cupsdAddSubscription(
|
|
|
unsigned mask, /* I - Event mask */
|
|
|
cupsd_printer_t *dest, /* I - Printer, if any */
|
|
|
cupsd_job_t *job, /* I - Job, if any */
|
|
|
const char *uri, /* I - notify-recipient-uri, if any */
|
|
|
int sub_id) /* I - notify-subscription-id or 0 */
|
|
|
{
|
|
|
cupsd_subscription_t *temp; /* New subscription object */
|
|
|
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
|
|
"cupsdAddSubscription(mask=%x, dest=%p(%s), job=%p(%d), "
|
|
|
"uri=\"%s\")",
|
|
|
mask, dest, dest ? dest->name : "", job, job ? job->id : 0,
|
|
|
uri ? uri : "(null)");
|
|
|
|
|
|
if (!Subscriptions)
|
|
|
Subscriptions = cupsArrayNew((cups_array_func_t)cupsd_compare_subscriptions,
|
|
|
NULL);
|
|
|
|
|
|
if (!Subscriptions)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_CRIT,
|
|
|
"Unable to allocate memory for subscriptions - %s",
|
|
|
strerror(errno));
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Limit the number of subscriptions...
|
|
|
*/
|
|
|
|
|
|
if (MaxSubscriptions > 0 && cupsArrayCount(Subscriptions) >= MaxSubscriptions)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
|
|
"cupsdAddSubscription: Reached MaxSubscriptions %d "
|
|
|
"(count=%d)", MaxSubscriptions,
|
|
|
cupsArrayCount(Subscriptions));
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
if (MaxSubscriptionsPerJob > 0 && job)
|
|
|
{
|
|
|
int count; /* Number of job subscriptions */
|
|
|
|
|
|
for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
|
|
|
count = 0;
|
|
|
temp;
|
|
|
temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
if (temp->job == job)
|
|
|
count ++;
|
|
|
|
|
|
if (count >= MaxSubscriptionsPerJob)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
|
|
"cupsdAddSubscription: Reached MaxSubscriptionsPerJob %d "
|
|
|
"for job #%d (count=%d)", MaxSubscriptionsPerJob,
|
|
|
job->id, count);
|
|
|
return (NULL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (MaxSubscriptionsPerPrinter > 0 && dest)
|
|
|
{
|
|
|
int count; /* Number of printer subscriptions */
|
|
|
|
|
|
for (temp = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions),
|
|
|
count = 0;
|
|
|
temp;
|
|
|
temp = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
if (temp->dest == dest)
|
|
|
count ++;
|
|
|
|
|
|
if (count >= MaxSubscriptionsPerPrinter)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG,
|
|
|
"cupsdAddSubscription: Reached "
|
|
|
"MaxSubscriptionsPerPrinter %d for %s (count=%d)",
|
|
|
MaxSubscriptionsPerPrinter, dest->name, count);
|
|
|
return (NULL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Allocate memory for this subscription...
|
|
|
*/
|
|
|
|
|
|
if ((temp = calloc(1, sizeof(cupsd_subscription_t))) == NULL)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_CRIT,
|
|
|
"Unable to allocate memory for subscription object - %s",
|
|
|
strerror(errno));
|
|
|
return (NULL);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Fill in common data...
|
|
|
*/
|
|
|
|
|
|
if (sub_id)
|
|
|
{
|
|
|
temp->id = sub_id;
|
|
|
|
|
|
if (sub_id >= NextSubscriptionId)
|
|
|
NextSubscriptionId = sub_id + 1;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
temp->id = NextSubscriptionId;
|
|
|
|
|
|
NextSubscriptionId ++;
|
|
|
}
|
|
|
|
|
|
temp->mask = mask;
|
|
|
temp->dest = dest;
|
|
|
temp->job = job;
|
|
|
temp->pipe = -1;
|
|
|
temp->first_event_id = 1;
|
|
|
temp->next_event_id = 1;
|
|
|
|
|
|
cupsdSetString(&(temp->recipient), uri);
|
|
|
|
|
|
/*
|
|
|
* Add the subscription to the array...
|
|
|
*/
|
|
|
|
|
|
cupsArrayAdd(Subscriptions, temp);
|
|
|
|
|
|
/*
|
|
|
* For RSS subscriptions, run the notifier immediately...
|
|
|
*/
|
|
|
|
|
|
if (uri && !strncmp(uri, "rss:", 4))
|
|
|
cupsd_start_notifier(temp);
|
|
|
|
|
|
return (temp);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdDeleteAllSubscriptions()' - Delete all subscriptions.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdDeleteAllSubscriptions(void)
|
|
|
{
|
|
|
cupsd_subscription_t *sub; /* Subscription */
|
|
|
|
|
|
|
|
|
if (!Subscriptions)
|
|
|
return;
|
|
|
|
|
|
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
|
|
|
sub;
|
|
|
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
cupsdDeleteSubscription(sub, 0);
|
|
|
|
|
|
cupsArrayDelete(Subscriptions);
|
|
|
Subscriptions = NULL;
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdDeleteSubscription()' - Delete a subscription object.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdDeleteSubscription(
|
|
|
cupsd_subscription_t *sub, /* I - Subscription object */
|
|
|
int update) /* I - 1 = update subscriptions.conf */
|
|
|
{
|
|
|
/*
|
|
|
* Close the pipe to the notifier as needed...
|
|
|
*/
|
|
|
|
|
|
if (sub->pipe >= 0)
|
|
|
close(sub->pipe);
|
|
|
|
|
|
/*
|
|
|
* Remove subscription from array...
|
|
|
*/
|
|
|
|
|
|
cupsArrayRemove(Subscriptions, sub);
|
|
|
|
|
|
/*
|
|
|
* Free memory...
|
|
|
*/
|
|
|
|
|
|
cupsdClearString(&(sub->owner));
|
|
|
cupsdClearString(&(sub->recipient));
|
|
|
|
|
|
cupsArrayDelete(sub->events);
|
|
|
|
|
|
free(sub);
|
|
|
|
|
|
/*
|
|
|
* Update the subscriptions as needed...
|
|
|
*/
|
|
|
|
|
|
if (update)
|
|
|
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdEventName()' - Return a single event name.
|
|
|
*/
|
|
|
|
|
|
const char * /* O - Event name */
|
|
|
cupsdEventName(
|
|
|
cupsd_eventmask_t event) /* I - Event value */
|
|
|
{
|
|
|
switch (event)
|
|
|
{
|
|
|
default :
|
|
|
return (NULL);
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_RESTARTED :
|
|
|
return ("printer-restarted");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_SHUTDOWN :
|
|
|
return ("printer-shutdown");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_STOPPED :
|
|
|
return ("printer-stopped");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED :
|
|
|
return ("printer-finishings-changed");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_MEDIA_CHANGED :
|
|
|
return ("printer-media-changed");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_ADDED :
|
|
|
return ("printer-added");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_DELETED :
|
|
|
return ("printer-deleted");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_MODIFIED :
|
|
|
return ("printer-modified");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED :
|
|
|
return ("printer-queue-order-changed");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_STATE :
|
|
|
case CUPSD_EVENT_PRINTER_STATE_CHANGED :
|
|
|
return ("printer-state-changed");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_CONFIG :
|
|
|
case CUPSD_EVENT_PRINTER_CONFIG_CHANGED :
|
|
|
return ("printer-config-changed");
|
|
|
|
|
|
case CUPSD_EVENT_PRINTER_CHANGED :
|
|
|
return ("printer-changed");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_CREATED :
|
|
|
return ("job-created");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_COMPLETED :
|
|
|
return ("job-completed");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_STOPPED :
|
|
|
return ("job-stopped");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_CONFIG_CHANGED :
|
|
|
return ("job-config-changed");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_PROGRESS :
|
|
|
return ("job-progress");
|
|
|
|
|
|
case CUPSD_EVENT_JOB_STATE :
|
|
|
case CUPSD_EVENT_JOB_STATE_CHANGED :
|
|
|
return ("job-state-changed");
|
|
|
|
|
|
case CUPSD_EVENT_SERVER_RESTARTED :
|
|
|
return ("server-restarted");
|
|
|
|
|
|
case CUPSD_EVENT_SERVER_STARTED :
|
|
|
return ("server-started");
|
|
|
|
|
|
case CUPSD_EVENT_SERVER_STOPPED :
|
|
|
return ("server-stopped");
|
|
|
|
|
|
case CUPSD_EVENT_SERVER_AUDIT :
|
|
|
return ("server-audit");
|
|
|
|
|
|
case CUPSD_EVENT_ALL :
|
|
|
return ("all");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdEventValue()' - Return the event mask value for a name.
|
|
|
*/
|
|
|
|
|
|
cupsd_eventmask_t /* O - Event mask value */
|
|
|
cupsdEventValue(const char *name) /* I - Name of event */
|
|
|
{
|
|
|
if (!strcmp(name, "all"))
|
|
|
return (CUPSD_EVENT_ALL);
|
|
|
else if (!strcmp(name, "printer-restarted"))
|
|
|
return (CUPSD_EVENT_PRINTER_RESTARTED);
|
|
|
else if (!strcmp(name, "printer-shutdown"))
|
|
|
return (CUPSD_EVENT_PRINTER_SHUTDOWN);
|
|
|
else if (!strcmp(name, "printer-stopped"))
|
|
|
return (CUPSD_EVENT_PRINTER_STOPPED);
|
|
|
else if (!strcmp(name, "printer-finishings-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_FINISHINGS_CHANGED);
|
|
|
else if (!strcmp(name, "printer-media-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_MEDIA_CHANGED);
|
|
|
else if (!strcmp(name, "printer-added"))
|
|
|
return (CUPSD_EVENT_PRINTER_ADDED);
|
|
|
else if (!strcmp(name, "printer-deleted"))
|
|
|
return (CUPSD_EVENT_PRINTER_DELETED);
|
|
|
else if (!strcmp(name, "printer-modified"))
|
|
|
return (CUPSD_EVENT_PRINTER_MODIFIED);
|
|
|
else if (!strcmp(name, "printer-queue-order-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_QUEUE_ORDER_CHANGED);
|
|
|
else if (!strcmp(name, "printer-state-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_STATE_CHANGED);
|
|
|
else if (!strcmp(name, "printer-config-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_CONFIG_CHANGED);
|
|
|
else if (!strcmp(name, "printer-changed"))
|
|
|
return (CUPSD_EVENT_PRINTER_CHANGED);
|
|
|
else if (!strcmp(name, "job-created"))
|
|
|
return (CUPSD_EVENT_JOB_CREATED);
|
|
|
else if (!strcmp(name, "job-completed"))
|
|
|
return (CUPSD_EVENT_JOB_COMPLETED);
|
|
|
else if (!strcmp(name, "job-stopped"))
|
|
|
return (CUPSD_EVENT_JOB_STOPPED);
|
|
|
else if (!strcmp(name, "job-config-changed"))
|
|
|
return (CUPSD_EVENT_JOB_CONFIG_CHANGED);
|
|
|
else if (!strcmp(name, "job-progress"))
|
|
|
return (CUPSD_EVENT_JOB_PROGRESS);
|
|
|
else if (!strcmp(name, "job-state-changed"))
|
|
|
return (CUPSD_EVENT_JOB_STATE_CHANGED);
|
|
|
else if (!strcmp(name, "server-restarted"))
|
|
|
return (CUPSD_EVENT_SERVER_RESTARTED);
|
|
|
else if (!strcmp(name, "server-started"))
|
|
|
return (CUPSD_EVENT_SERVER_STARTED);
|
|
|
else if (!strcmp(name, "server-stopped"))
|
|
|
return (CUPSD_EVENT_SERVER_STOPPED);
|
|
|
else if (!strcmp(name, "server-audit"))
|
|
|
return (CUPSD_EVENT_SERVER_AUDIT);
|
|
|
else
|
|
|
return (CUPSD_EVENT_NONE);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdExpireSubscriptions()' - Expire old subscription objects.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdExpireSubscriptions(
|
|
|
cupsd_printer_t *dest, /* I - Printer, if any */
|
|
|
cupsd_job_t *job) /* I - Job, if any */
|
|
|
{
|
|
|
cupsd_subscription_t *sub; /* Current subscription */
|
|
|
int update; /* Update subscriptions.conf? */
|
|
|
time_t curtime; /* Current time */
|
|
|
|
|
|
|
|
|
if (cupsArrayCount(Subscriptions) == 0)
|
|
|
return;
|
|
|
|
|
|
curtime = time(NULL);
|
|
|
update = 0;
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_INFO, "Expiring subscriptions...");
|
|
|
|
|
|
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
|
|
|
sub;
|
|
|
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
if ((!sub->job && !dest && sub->expire && sub->expire <= curtime) ||
|
|
|
(dest && sub->dest == dest) ||
|
|
|
(job && sub->job == job))
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_INFO, "Subscription %d has expired...",
|
|
|
sub->id);
|
|
|
|
|
|
cupsdDeleteSubscription(sub, 0);
|
|
|
|
|
|
update = 1;
|
|
|
}
|
|
|
|
|
|
if (update)
|
|
|
cupsdMarkDirty(CUPSD_DIRTY_SUBSCRIPTIONS);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdFindSubscription()' - Find a subscription by ID.
|
|
|
*/
|
|
|
|
|
|
cupsd_subscription_t * /* O - Subscription object */
|
|
|
cupsdFindSubscription(int id) /* I - Subscription ID */
|
|
|
{
|
|
|
cupsd_subscription_t sub; /* Subscription template */
|
|
|
|
|
|
|
|
|
sub.id = id;
|
|
|
|
|
|
return ((cupsd_subscription_t *)cupsArrayFind(Subscriptions, &sub));
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdLoadAllSubscriptions()' - Load all subscriptions from the .conf file.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdLoadAllSubscriptions(void)
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
cups_file_t *fp; /* subscriptions.conf file */
|
|
|
int linenum; /* Current line number */
|
|
|
char line[1024], /* Line from file */
|
|
|
*value, /* Pointer to value */
|
|
|
*valueptr; /* Pointer into value */
|
|
|
cupsd_subscription_t *sub; /* Current subscription */
|
|
|
int hex; /* Non-zero if reading hex data */
|
|
|
int delete_sub; /* Delete subscription? */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Open the subscriptions.conf file...
|
|
|
*/
|
|
|
|
|
|
snprintf(line, sizeof(line), "%s/subscriptions.conf", ServerRoot);
|
|
|
if ((fp = cupsdOpenConfFile(line)) == NULL)
|
|
|
return;
|
|
|
|
|
|
/*
|
|
|
* Read all of the lines from the file...
|
|
|
*/
|
|
|
|
|
|
linenum = 0;
|
|
|
sub = NULL;
|
|
|
delete_sub = 0;
|
|
|
|
|
|
while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
|
|
|
{
|
|
|
if (!_cups_strcasecmp(line, "NextSubscriptionId") && value)
|
|
|
{
|
|
|
/*
|
|
|
* NextSubscriptionId NNN
|
|
|
*/
|
|
|
|
|
|
i = atoi(value);
|
|
|
if (i >= NextSubscriptionId && i > 0)
|
|
|
NextSubscriptionId = i;
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "<Subscription"))
|
|
|
{
|
|
|
/*
|
|
|
* <Subscription #>
|
|
|
*/
|
|
|
|
|
|
if (!sub && value && isdigit(value[0] & 255))
|
|
|
{
|
|
|
sub = cupsdAddSubscription(CUPSD_EVENT_NONE, NULL, NULL, NULL,
|
|
|
atoi(value));
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "</Subscription>"))
|
|
|
{
|
|
|
if (!sub)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
if (delete_sub)
|
|
|
cupsdDeleteSubscription(sub, 0);
|
|
|
|
|
|
sub = NULL;
|
|
|
delete_sub = 0;
|
|
|
}
|
|
|
else if (!sub)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "Events"))
|
|
|
{
|
|
|
/*
|
|
|
* Events name
|
|
|
* Events name name name ...
|
|
|
*/
|
|
|
|
|
|
if (!value)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
while (*value)
|
|
|
{
|
|
|
/*
|
|
|
* Separate event names...
|
|
|
*/
|
|
|
|
|
|
for (valueptr = value; !isspace(*valueptr) && *valueptr; valueptr ++);
|
|
|
|
|
|
while (isspace(*valueptr & 255))
|
|
|
*valueptr++ = '\0';
|
|
|
|
|
|
/*
|
|
|
* See if the name exists...
|
|
|
*/
|
|
|
|
|
|
if ((sub->mask |= cupsdEventValue(value)) == CUPSD_EVENT_NONE)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Unknown event name \'%s\' on line %d of subscriptions.conf.",
|
|
|
value, linenum);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
value = valueptr;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "Owner"))
|
|
|
{
|
|
|
/*
|
|
|
* Owner
|
|
|
*/
|
|
|
|
|
|
if (value)
|
|
|
cupsdSetString(&sub->owner, value);
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "Recipient"))
|
|
|
{
|
|
|
/*
|
|
|
* Recipient uri
|
|
|
*/
|
|
|
|
|
|
if (value)
|
|
|
cupsdSetString(&sub->recipient, value);
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "JobId"))
|
|
|
{
|
|
|
/*
|
|
|
* JobId #
|
|
|
*/
|
|
|
|
|
|
if (value && isdigit(*value & 255))
|
|
|
{
|
|
|
if ((sub->job = cupsdFindJob(atoi(value))) == NULL)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Job %s not found on line %d of subscriptions.conf.",
|
|
|
value, linenum);
|
|
|
delete_sub = 1;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "PrinterName"))
|
|
|
{
|
|
|
/*
|
|
|
* PrinterName name
|
|
|
*/
|
|
|
|
|
|
if (value)
|
|
|
{
|
|
|
if ((sub->dest = cupsdFindDest(value)) == NULL)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Printer \'%s\' not found on line %d of subscriptions.conf.",
|
|
|
value, linenum);
|
|
|
delete_sub = 1;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "UserData"))
|
|
|
{
|
|
|
/*
|
|
|
* UserData encoded-string
|
|
|
*/
|
|
|
|
|
|
if (value)
|
|
|
{
|
|
|
for (i = 0, valueptr = value, hex = 0; i < 63 && *valueptr; i ++)
|
|
|
{
|
|
|
if (*valueptr == '<' && !hex)
|
|
|
{
|
|
|
hex = 1;
|
|
|
valueptr ++;
|
|
|
}
|
|
|
|
|
|
if (hex)
|
|
|
{
|
|
|
if (isxdigit(valueptr[0]) && isxdigit(valueptr[1]))
|
|
|
{
|
|
|
if (isdigit(valueptr[0]))
|
|
|
sub->user_data[i] = (unsigned char)((valueptr[0] - '0') << 4);
|
|
|
else
|
|
|
sub->user_data[i] = (unsigned char)((tolower(valueptr[0]) - 'a' + 10) << 4);
|
|
|
|
|
|
if (isdigit(valueptr[1]))
|
|
|
sub->user_data[i] |= valueptr[1] - '0';
|
|
|
else
|
|
|
sub->user_data[i] |= tolower(valueptr[1]) - 'a' + 10;
|
|
|
|
|
|
valueptr += 2;
|
|
|
|
|
|
if (*valueptr == '>')
|
|
|
{
|
|
|
hex = 0;
|
|
|
valueptr ++;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
break;
|
|
|
}
|
|
|
else
|
|
|
sub->user_data[i] = (unsigned char)*valueptr++;
|
|
|
}
|
|
|
|
|
|
if (*valueptr)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Bad UserData \'%s\' on line %d of subscriptions.conf.",
|
|
|
value, linenum);
|
|
|
}
|
|
|
else
|
|
|
sub->user_data_len = i;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "LeaseDuration"))
|
|
|
{
|
|
|
/*
|
|
|
* LeaseDuration #
|
|
|
*/
|
|
|
|
|
|
if (value && isdigit(*value & 255))
|
|
|
{
|
|
|
sub->lease = atoi(value);
|
|
|
sub->expire = sub->lease ? time(NULL) + sub->lease : 0;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "Interval"))
|
|
|
{
|
|
|
/*
|
|
|
* Interval #
|
|
|
*/
|
|
|
|
|
|
if (value && isdigit(*value & 255))
|
|
|
sub->interval = atoi(value);
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "ExpirationTime"))
|
|
|
{
|
|
|
/*
|
|
|
* ExpirationTime #
|
|
|
*/
|
|
|
|
|
|
if (value && isdigit(*value & 255))
|
|
|
sub->expire = atoi(value);
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else if (!_cups_strcasecmp(line, "NextEventId"))
|
|
|
{
|
|
|
/*
|
|
|
* NextEventId #
|
|
|
*/
|
|
|
|
|
|
if (value && isdigit(*value & 255))
|
|
|
sub->next_event_id = sub->first_event_id = atoi(value);
|
|
|
else
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Syntax error on line %d of subscriptions.conf.",
|
|
|
linenum);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/*
|
|
|
* Something else we don't understand...
|
|
|
*/
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Unknown configuration directive %s on line %d of subscriptions.conf.",
|
|
|
line, linenum);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
cupsFileClose(fp);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdSaveAllSubscriptions()' - Save all subscriptions to the .conf file.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdSaveAllSubscriptions(void)
|
|
|
{
|
|
|
int i; /* Looping var */
|
|
|
cups_file_t *fp; /* subscriptions.conf file */
|
|
|
char filename[1024], /* subscriptions.conf filename */
|
|
|
temp[1024]; /* Temporary string */
|
|
|
cupsd_subscription_t *sub; /* Current subscription */
|
|
|
time_t curtime; /* Current time */
|
|
|
struct tm curdate; /* Current date */
|
|
|
unsigned mask; /* Current event mask */
|
|
|
const char *name; /* Current event name */
|
|
|
int hex; /* Non-zero if we are writing hex data */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Create the subscriptions.conf file...
|
|
|
*/
|
|
|
|
|
|
snprintf(filename, sizeof(filename), "%s/subscriptions.conf", ServerRoot);
|
|
|
|
|
|
if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
|
|
|
return;
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_INFO, "Saving subscriptions.conf...");
|
|
|
|
|
|
/*
|
|
|
* Write a small header to the file...
|
|
|
*/
|
|
|
|
|
|
time(&curtime);
|
|
|
localtime_r(&curtime, &curdate);
|
|
|
strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", &curdate);
|
|
|
|
|
|
cupsFilePuts(fp, "# Subscription configuration file for " CUPS_SVERSION "\n");
|
|
|
cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
|
|
|
|
|
|
cupsFilePrintf(fp, "NextSubscriptionId %d\n", NextSubscriptionId);
|
|
|
|
|
|
/*
|
|
|
* Write every subscription known to the system...
|
|
|
*/
|
|
|
|
|
|
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
|
|
|
sub;
|
|
|
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
{
|
|
|
cupsFilePrintf(fp, "<Subscription %d>\n", sub->id);
|
|
|
|
|
|
if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
|
|
|
{
|
|
|
/*
|
|
|
* Simple event list...
|
|
|
*/
|
|
|
|
|
|
cupsFilePrintf(fp, "Events %s\n", name);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/*
|
|
|
* Complex event list...
|
|
|
*/
|
|
|
|
|
|
cupsFilePuts(fp, "Events");
|
|
|
|
|
|
for (mask = 1; mask < CUPSD_EVENT_ALL; mask <<= 1)
|
|
|
if (sub->mask & mask)
|
|
|
cupsFilePrintf(fp, " %s", cupsdEventName((cupsd_eventmask_t)mask));
|
|
|
|
|
|
cupsFilePuts(fp, "\n");
|
|
|
}
|
|
|
|
|
|
if (sub->owner)
|
|
|
cupsFilePrintf(fp, "Owner %s\n", sub->owner);
|
|
|
if (sub->recipient)
|
|
|
cupsFilePrintf(fp, "Recipient %s\n", sub->recipient);
|
|
|
if (sub->job)
|
|
|
cupsFilePrintf(fp, "JobId %d\n", sub->job->id);
|
|
|
if (sub->dest)
|
|
|
cupsFilePrintf(fp, "PrinterName %s\n", sub->dest->name);
|
|
|
|
|
|
if (sub->user_data_len > 0)
|
|
|
{
|
|
|
cupsFilePuts(fp, "UserData ");
|
|
|
|
|
|
for (i = 0, hex = 0; i < sub->user_data_len; i ++)
|
|
|
{
|
|
|
if (sub->user_data[i] < ' ' ||
|
|
|
sub->user_data[i] > 0x7f ||
|
|
|
sub->user_data[i] == '<')
|
|
|
{
|
|
|
if (!hex)
|
|
|
{
|
|
|
cupsFilePrintf(fp, "<%02X", sub->user_data[i]);
|
|
|
hex = 1;
|
|
|
}
|
|
|
else
|
|
|
cupsFilePrintf(fp, "%02X", sub->user_data[i]);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
if (hex)
|
|
|
{
|
|
|
cupsFilePrintf(fp, ">%c", sub->user_data[i]);
|
|
|
hex = 0;
|
|
|
}
|
|
|
else
|
|
|
cupsFilePutChar(fp, sub->user_data[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (hex)
|
|
|
cupsFilePuts(fp, ">\n");
|
|
|
else
|
|
|
cupsFilePutChar(fp, '\n');
|
|
|
}
|
|
|
|
|
|
cupsFilePrintf(fp, "LeaseDuration %d\n", sub->lease);
|
|
|
cupsFilePrintf(fp, "Interval %d\n", sub->interval);
|
|
|
cupsFilePrintf(fp, "ExpirationTime %ld\n", (long)sub->expire);
|
|
|
cupsFilePrintf(fp, "NextEventId %d\n", sub->next_event_id);
|
|
|
|
|
|
cupsFilePuts(fp, "</Subscription>\n");
|
|
|
}
|
|
|
|
|
|
cupsdCloseCreatedConfFile(fp, filename);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsdStopAllNotifiers()' - Stop all notifier processes.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsdStopAllNotifiers(void)
|
|
|
{
|
|
|
cupsd_subscription_t *sub; /* Current subscription */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* See if we have started any notifiers...
|
|
|
*/
|
|
|
|
|
|
if (!NotifierStatusBuffer)
|
|
|
return;
|
|
|
|
|
|
/*
|
|
|
* Yes, kill any processes that are left...
|
|
|
*/
|
|
|
|
|
|
for (sub = (cupsd_subscription_t *)cupsArrayFirst(Subscriptions);
|
|
|
sub;
|
|
|
sub = (cupsd_subscription_t *)cupsArrayNext(Subscriptions))
|
|
|
if (sub->pid)
|
|
|
{
|
|
|
cupsdEndProcess(sub->pid, 0);
|
|
|
|
|
|
close(sub->pipe);
|
|
|
sub->pipe = -1;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Close the status pipes...
|
|
|
*/
|
|
|
|
|
|
if (NotifierPipes[0] >= 0)
|
|
|
{
|
|
|
cupsdRemoveSelect(NotifierPipes[0]);
|
|
|
|
|
|
cupsdStatBufDelete(NotifierStatusBuffer);
|
|
|
|
|
|
close(NotifierPipes[0]);
|
|
|
close(NotifierPipes[1]);
|
|
|
|
|
|
NotifierPipes[0] = -1;
|
|
|
NotifierPipes[1] = -1;
|
|
|
NotifierStatusBuffer = NULL;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsd_compare_subscriptions()' - Compare two subscriptions.
|
|
|
*/
|
|
|
|
|
|
static int /* O - Result of comparison */
|
|
|
cupsd_compare_subscriptions(
|
|
|
cupsd_subscription_t *first, /* I - First subscription object */
|
|
|
cupsd_subscription_t *second, /* I - Second subscription object */
|
|
|
void *unused) /* I - Unused user data pointer */
|
|
|
{
|
|
|
(void)unused;
|
|
|
|
|
|
return (first->id - second->id);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsd_delete_event()' - Delete a single event...
|
|
|
*
|
|
|
* Oldest events must be deleted first, otherwise the subscription cache
|
|
|
* flushing code will not work properly.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
cupsd_delete_event(cupsd_event_t *event)/* I - Event to delete */
|
|
|
{
|
|
|
/*
|
|
|
* Free memory...
|
|
|
*/
|
|
|
|
|
|
ippDelete(event->attrs);
|
|
|
free(event);
|
|
|
}
|
|
|
|
|
|
|
|
|
#ifdef HAVE_DBUS
|
|
|
/*
|
|
|
* 'cupsd_send_dbus()' - Send a DBUS notification...
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
cupsd_send_dbus(cupsd_eventmask_t event,/* I - Event to send */
|
|
|
cupsd_printer_t *dest,/* I - Destination, if any */
|
|
|
cupsd_job_t *job) /* I - Job, if any */
|
|
|
{
|
|
|
DBusError error; /* Error, if any */
|
|
|
DBusMessage *message; /* Message to send */
|
|
|
DBusMessageIter iter; /* Iterator for message data */
|
|
|
const char *what; /* What to send */
|
|
|
static DBusConnection *con = NULL; /* Connection to DBUS server */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Figure out what to send, if anything...
|
|
|
*/
|
|
|
|
|
|
if (event & CUPSD_EVENT_PRINTER_ADDED)
|
|
|
what = "PrinterAdded";
|
|
|
else if (event & CUPSD_EVENT_PRINTER_DELETED)
|
|
|
what = "PrinterRemoved";
|
|
|
else if (event & CUPSD_EVENT_PRINTER_CHANGED)
|
|
|
what = "QueueChanged";
|
|
|
else if (event & CUPSD_EVENT_JOB_CREATED)
|
|
|
what = "JobQueuedLocal";
|
|
|
else if ((event & CUPSD_EVENT_JOB_STATE) && job &&
|
|
|
job->state_value == IPP_JOB_PROCESSING)
|
|
|
what = "JobStartedLocal";
|
|
|
else
|
|
|
return;
|
|
|
|
|
|
/*
|
|
|
* Verify connection to DBUS server...
|
|
|
*/
|
|
|
|
|
|
if (con && !dbus_connection_get_is_connected(con))
|
|
|
{
|
|
|
dbus_connection_unref(con);
|
|
|
con = NULL;
|
|
|
}
|
|
|
|
|
|
if (!con)
|
|
|
{
|
|
|
dbus_error_init(&error);
|
|
|
|
|
|
con = dbus_bus_get(getuid() ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
|
|
|
if (!con)
|
|
|
{
|
|
|
dbus_error_free(&error);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Create and send the new message...
|
|
|
*/
|
|
|
|
|
|
message = dbus_message_new_signal("/com/redhat/PrinterSpooler",
|
|
|
"com.redhat.PrinterSpooler", what);
|
|
|
|
|
|
dbus_message_append_iter_init(message, &iter);
|
|
|
if (dest)
|
|
|
dbus_message_iter_append_string(&iter, dest->name);
|
|
|
if (job)
|
|
|
{
|
|
|
dbus_message_iter_append_uint32(&iter, job->id);
|
|
|
dbus_message_iter_append_string(&iter, job->username);
|
|
|
}
|
|
|
|
|
|
dbus_connection_send(con, message, NULL);
|
|
|
dbus_connection_flush(con);
|
|
|
dbus_message_unref(message);
|
|
|
}
|
|
|
#endif /* HAVE_DBUS */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsd_send_notification()' - Send a notification for the specified event.
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
cupsd_send_notification(
|
|
|
cupsd_subscription_t *sub, /* I - Subscription object */
|
|
|
cupsd_event_t *event) /* I - Event to send */
|
|
|
{
|
|
|
ipp_state_t state; /* IPP event state */
|
|
|
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG2,
|
|
|
"cupsd_send_notification(sub=%p(%d), event=%p(%s))",
|
|
|
sub, sub->id, event, cupsdEventName(event->event));
|
|
|
|
|
|
/*
|
|
|
* Allocate the events array as needed...
|
|
|
*/
|
|
|
|
|
|
if (!sub->events)
|
|
|
{
|
|
|
sub->events = cupsArrayNew3((cups_array_func_t)NULL, NULL,
|
|
|
(cups_ahash_func_t)NULL, 0,
|
|
|
(cups_acopy_func_t)NULL,
|
|
|
(cups_afree_func_t)cupsd_delete_event);
|
|
|
|
|
|
if (!sub->events)
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_CRIT,
|
|
|
"Unable to allocate memory for subscription #%d!",
|
|
|
sub->id);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Purge an old event as needed...
|
|
|
*/
|
|
|
|
|
|
if (cupsArrayCount(sub->events) >= MaxEvents)
|
|
|
{
|
|
|
/*
|
|
|
* Purge the oldest event in the cache...
|
|
|
*/
|
|
|
|
|
|
cupsArrayRemove(sub->events, cupsArrayFirst(sub->events));
|
|
|
|
|
|
sub->first_event_id ++;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Add the event to the subscription. Since the events array is
|
|
|
* always MaxEvents in length, and since we will have already
|
|
|
* removed an event from the subscription cache if we hit the
|
|
|
* event cache limit, we don't need to check for overflow here...
|
|
|
*/
|
|
|
|
|
|
cupsArrayAdd(sub->events, event);
|
|
|
|
|
|
/*
|
|
|
* Deliver the event...
|
|
|
*/
|
|
|
|
|
|
if (sub->recipient)
|
|
|
{
|
|
|
for (;;)
|
|
|
{
|
|
|
if (sub->pipe < 0)
|
|
|
cupsd_start_notifier(sub);
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG2, "sub->pipe=%d", sub->pipe);
|
|
|
|
|
|
if (sub->pipe < 0)
|
|
|
break;
|
|
|
|
|
|
event->attrs->state = IPP_IDLE;
|
|
|
|
|
|
while ((state = ippWriteFile(sub->pipe, event->attrs)) != IPP_DATA)
|
|
|
if (state == IPP_ERROR)
|
|
|
break;
|
|
|
|
|
|
if (state == IPP_ERROR)
|
|
|
{
|
|
|
if (errno == EPIPE)
|
|
|
{
|
|
|
/*
|
|
|
* Notifier died, try restarting it...
|
|
|
*/
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_WARN,
|
|
|
"Notifier for subscription %d (%s) went away, "
|
|
|
"retrying!",
|
|
|
sub->id, sub->recipient);
|
|
|
cupsdEndProcess(sub->pid, 0);
|
|
|
|
|
|
close(sub->pipe);
|
|
|
sub->pipe = -1;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Unable to send event for subscription %d (%s)!",
|
|
|
sub->id, sub->recipient);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* If we get this far, break out of the loop...
|
|
|
*/
|
|
|
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Bump the event sequence number...
|
|
|
*/
|
|
|
|
|
|
sub->next_event_id ++;
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsd_start_notifier()' - Start a notifier subprocess...
|
|
|
*/
|
|
|
|
|
|
static void
|
|
|
cupsd_start_notifier(
|
|
|
cupsd_subscription_t *sub) /* I - Subscription object */
|
|
|
{
|
|
|
int pid; /* Notifier process ID */
|
|
|
int fds[2]; /* Pipe file descriptors */
|
|
|
char *argv[4], /* Command-line arguments */
|
|
|
*envp[MAX_ENV], /* Environment variables */
|
|
|
user_data[128], /* Base-64 encoded user data */
|
|
|
scheme[256], /* notify-recipient-uri scheme */
|
|
|
*ptr, /* Pointer into scheme */
|
|
|
command[1024]; /* Notifier command */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Extract the scheme name from the recipient URI and point to the
|
|
|
* notifier program...
|
|
|
*/
|
|
|
|
|
|
strlcpy(scheme, sub->recipient, sizeof(scheme));
|
|
|
if ((ptr = strchr(scheme, ':')) != NULL)
|
|
|
*ptr = '\0';
|
|
|
|
|
|
snprintf(command, sizeof(command), "%s/notifier/%s", ServerBin, scheme);
|
|
|
|
|
|
/*
|
|
|
* Base-64 encode the user data...
|
|
|
*/
|
|
|
|
|
|
httpEncode64_2(user_data, sizeof(user_data), (char *)sub->user_data,
|
|
|
sub->user_data_len);
|
|
|
|
|
|
/*
|
|
|
* Setup the argument array...
|
|
|
*/
|
|
|
|
|
|
argv[0] = command;
|
|
|
argv[1] = sub->recipient;
|
|
|
argv[2] = user_data;
|
|
|
argv[3] = NULL;
|
|
|
|
|
|
/*
|
|
|
* Setup the environment...
|
|
|
*/
|
|
|
|
|
|
cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
|
|
|
|
|
|
/*
|
|
|
* Create pipes as needed...
|
|
|
*/
|
|
|
|
|
|
if (!NotifierStatusBuffer)
|
|
|
{
|
|
|
/*
|
|
|
* Create the status pipe...
|
|
|
*/
|
|
|
|
|
|
if (cupsdOpenPipe(NotifierPipes))
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Unable to create pipes for notifier status - %s",
|
|
|
strerror(errno));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
NotifierStatusBuffer = cupsdStatBufNew(NotifierPipes[0], "[Notifier]");
|
|
|
|
|
|
cupsdAddSelect(NotifierPipes[0], (cupsd_selfunc_t)cupsd_update_notifier,
|
|
|
NULL, NULL);
|
|
|
}
|
|
|
|
|
|
if (cupsdOpenPipe(fds))
|
|
|
{
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR,
|
|
|
"Unable to create pipes for notifier %s - %s",
|
|
|
scheme, strerror(errno));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Make sure the delivery pipe is non-blocking...
|
|
|
*/
|
|
|
|
|
|
fcntl(fds[1], F_SETFL, fcntl(fds[1], F_GETFL) | O_NONBLOCK);
|
|
|
|
|
|
/*
|
|
|
* Create the notifier process...
|
|
|
*/
|
|
|
|
|
|
if (cupsdStartProcess(command, argv, envp, fds[0], -1, NotifierPipes[1],
|
|
|
-1, -1, 0, DefaultProfile, NULL, &pid) < 0)
|
|
|
{
|
|
|
/*
|
|
|
* Error - can't fork!
|
|
|
*/
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for notifier %s - %s",
|
|
|
scheme, strerror(errno));
|
|
|
|
|
|
cupsdClosePipe(fds);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
/*
|
|
|
* Fork successful - return the PID...
|
|
|
*/
|
|
|
|
|
|
cupsdLogMessage(CUPSD_LOG_DEBUG, "Notifier %s started - PID = %d",
|
|
|
scheme, pid);
|
|
|
|
|
|
sub->pid = pid;
|
|
|
sub->pipe = fds[1];
|
|
|
sub->status = 0;
|
|
|
|
|
|
close(fds[0]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsd_update_notifier()' - Read messages from notifiers.
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
cupsd_update_notifier(void)
|
|
|
{
|
|
|
char message[1024]; /* Pointer to message text */
|
|
|
int loglevel; /* Log level for message */
|
|
|
|
|
|
|
|
|
while (cupsdStatBufUpdate(NotifierStatusBuffer, &loglevel,
|
|
|
message, sizeof(message)))
|
|
|
{
|
|
|
if (loglevel == CUPSD_LOG_INFO)
|
|
|
cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
|
|
|
|
|
|
if (!strchr(NotifierStatusBuffer->buffer, '\n'))
|
|
|
break;
|
|
|
}
|
|
|
}
|