\n"
+ "
%s", papplClientGetLocString(client, title));
+ if (label && path_or_url)
+ papplClientHTMLPrintf(client, " %s ", path_or_url, papplClientGetLocString(client, label));
+ papplClientHTMLPuts(client, " \n");
+ }
+}
+
+
//
// 'papplClientHTMLPrintf()' - Send formatted text to the web browser client,
// escaping as needed.
diff --git a/pappl/client.h b/pappl/client.h
index ec560379..493a8e30 100644
--- a/pappl/client.h
+++ b/pappl/client.h
@@ -42,6 +42,8 @@ extern bool papplClientHTMLAuthorize(pappl_client_t *client) _PAPPL_PUBLIC;
extern void papplClientHTMLEscape(pappl_client_t *client, const char *s, size_t slen) _PAPPL_PUBLIC;
extern void papplClientHTMLFooter(pappl_client_t *client) _PAPPL_PUBLIC;
extern void papplClientHTMLHeader(pappl_client_t *client, const char *title, int refresh) _PAPPL_PUBLIC;
+extern void papplClientHTMLScannerHeader(pappl_client_t *client, pappl_scanner_t *scanner, const char *title, int refresh, const char *label, const char *path_or_url)_PAPPL_PUBLIC;
+extern void papplClientHTMLScannerFooter(pappl_client_t *client)_PAPPL_PUBLIC;
extern void papplClientHTMLPrinterFooter(pappl_client_t *client) _PAPPL_PUBLIC;
extern void papplClientHTMLPrinterHeader(pappl_client_t *client, pappl_printer_t *printer, const char *title, int refresh, const char *label, const char *path_or_url) _PAPPL_PUBLIC;
extern void papplClientHTMLPrintf(pappl_client_t *client, const char *format, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(2, 3);
diff --git a/pappl/dnssd.c b/pappl/dnssd.c
index 6fc23489..6457ece9 100644
--- a/pappl/dnssd.c
+++ b/pappl/dnssd.c
@@ -10,64 +10,61 @@
#include "pappl-private.h"
-
//
// Constants...
//
#ifdef HAVE_AVAHI
-# define AVAHI_DNS_TYPE_LOC 29 // Per RFC 1876
-#endif // HAVE_AVAHI
-
+#define AVAHI_DNS_TYPE_LOC 29 // Per RFC 1876
+#endif // HAVE_AVAHI
//
// Local globals...
//
-static char pappl_dns_sd_hostname[256] = "";
- // Current DNS-SD hostname
-static int pappl_dns_sd_hostname_changes = 0;
- // Number of host name changes/collisions
-static pthread_mutex_t pappl_dns_sd_hostname_mutex = PTHREAD_MUTEX_INITIALIZER;
- // Host name mutex
+static char pappl_dns_sd_hostname[256] = "";
+// Current DNS-SD hostname
+static int pappl_dns_sd_hostname_changes = 0;
+// Number of host name changes/collisions
+static pthread_mutex_t pappl_dns_sd_hostname_mutex = PTHREAD_MUTEX_INITIALIZER;
+// Host name mutex
#ifdef HAVE_MDNSRESPONDER
-static DNSServiceRef pappl_dns_sd_hostname_ref = NULL;
- // Host name query reference
+static DNSServiceRef pappl_dns_sd_hostname_ref = NULL;
+// Host name query reference
#endif // HAVE_MDNSRESPONDER
-static _pappl_dns_sd_t pappl_dns_sd_master = NULL;
- // DNS-SD master reference
-static pthread_mutex_t pappl_dns_sd_mutex = PTHREAD_MUTEX_INITIALIZER;
- // DNS-SD master mutex
+static _pappl_dns_sd_t pappl_dns_sd_master = NULL;
+// DNS-SD master reference
+static pthread_mutex_t pappl_dns_sd_mutex = PTHREAD_MUTEX_INITIALIZER;
+// DNS-SD master mutex
#ifdef HAVE_AVAHI
static AvahiThreadedPoll *pappl_dns_sd_poll = NULL;
- // Avahi background thread
+// Avahi background thread
#endif // HAVE_AVAHI
-
//
// Local functions...
//
-static void dns_sd_geo_to_loc(const char *geo, unsigned char loc[16]);
+static void dns_sd_geo_to_loc(const char *geo, unsigned char loc[16]);
#ifdef HAVE_MDNSRESPONDER
-static void DNSSD_API dns_sd_hostname_callback(DNSServiceRef ref, DNSServiceFlags flags, uint32_t if_index, DNSServiceErrorType error, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context);
-static void DNSSD_API dns_sd_printer_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_printer_t *printer);
-static void *dns_sd_run(void *data);
-static void DNSSD_API dns_sd_system_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_system_t *system);
+static void DNSSD_API dns_sd_hostname_callback(DNSServiceRef ref, DNSServiceFlags flags, uint32_t if_index, DNSServiceErrorType error, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context);
+static void DNSSD_API dns_sd_printer_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_printer_t *printer);
+static void *dns_sd_run(void *data);
+static void DNSSD_API dns_sd_system_callback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *name, const char *regtype, const char *domain, pappl_system_t *system);
#elif defined(HAVE_AVAHI)
-static void dns_sd_client_cb(AvahiClient *c, AvahiClientState state, void *data);
-static void dns_sd_printer_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_printer_t *printer);
-static void dns_sd_system_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_system_t *system);
+static void dns_sd_client_cb(AvahiClient *c, AvahiClientState state, void *data);
+static void dns_sd_printer_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_printer_t *printer);
+static void dns_sd_scanner_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_scanner_t *scanner);
+static void dns_sd_system_callback(AvahiEntryGroup *p, AvahiEntryGroupState state, pappl_system_t *system);
#endif // HAVE_MDNSRESPONDER
-
//
// '_papplDNSSDCopyHostName()' - Copy the current DNS-SD hostname.
//
-const char * // O - Current DNS-SD hostname
-_papplDNSSDCopyHostName(char *buffer, // I - Hostname buffer
- size_t bufsize) // I - Size of hostname buffer
+const char * // O - Current DNS-SD hostname
+_papplDNSSDCopyHostName(char *buffer, // I - Hostname buffer
+ size_t bufsize) // I - Size of hostname buffer
{
pthread_mutex_lock(&pappl_dns_sd_hostname_mutex);
if (pappl_dns_sd_hostname[0])
@@ -79,16 +76,14 @@ _papplDNSSDCopyHostName(char *buffer, // I - Hostname buffer
return (buffer);
}
-
//
// '_papplDNSSDGetHostChanges()' - Get the number of host name changes/collisions so far.
//
-int // O - Number of host name changes/collisions
+int // O - Number of host name changes/collisions
_papplDNSSDGetHostChanges(void)
{
- int changes; // Return value
-
+ int changes; // Return value
pthread_mutex_lock(&pappl_dns_sd_hostname_mutex);
changes = pappl_dns_sd_hostname_changes;
@@ -97,19 +92,17 @@ _papplDNSSDGetHostChanges(void)
return (changes);
}
-
//
// '_papplDNSSDInit()' - Initialize DNS-SD services.
//
-_pappl_dns_sd_t // O - DNS-SD master reference
+_pappl_dns_sd_t // O - DNS-SD master reference
_papplDNSSDInit(
- pappl_system_t *system) // I - System
+ pappl_system_t *system) // I - System
{
#ifdef HAVE_MDNSRESPONDER
- int error; // Error code, if any
- pthread_t tid; // Thread ID
-
+ int error; // Error code, if any
+ pthread_t tid; // Thread ID
pthread_mutex_lock(&pappl_dns_sd_mutex);
@@ -152,8 +145,7 @@ _papplDNSSDInit(
pthread_mutex_unlock(&pappl_dns_sd_mutex);
#elif defined(HAVE_AVAHI)
- int error; // Error code, if any
-
+ int error; // Error code, if any
pthread_mutex_lock(&pappl_dns_sd_mutex);
@@ -179,7 +171,7 @@ _papplDNSSDInit(
{
// Get the current mDNS hostname...
const char *dns_sd_hostname = avahi_client_get_host_name_fqdn(pappl_dns_sd_master);
- // mDNS hostname
+ // mDNS hostname
if (dns_sd_hostname)
papplCopyString(pappl_dns_sd_hostname, dns_sd_hostname, sizeof(pappl_dns_sd_hostname));
@@ -196,13 +188,11 @@ _papplDNSSDInit(
return (pappl_dns_sd_master);
}
-
//
// '_papplDNSSDLock()' - Grab a lock to make DNS-SD changes.
//
-void
-_papplDNSSDLock(void)
+void _papplDNSSDLock(void)
{
#ifdef HAVE_AVAHI
if (pappl_dns_sd_poll)
@@ -210,116 +200,115 @@ _papplDNSSDLock(void)
#endif // HAVE_AVAHI
}
-
//
// '_papplDNSSDStrError()' - Return a string for the given DNS-SD error code.
//
-const char * // O - Error message
-_papplDNSSDStrError(int error) // I - Error code
+const char * // O - Error message
+_papplDNSSDStrError(int error) // I - Error code
{
#ifdef HAVE_MDNSRESPONDER
switch (error)
{
- case kDNSServiceErr_NoError :
- return ("No error");
+ case kDNSServiceErr_NoError:
+ return ("No error");
- case kDNSServiceErr_Unknown :
- default :
- return ("Unknown error");
+ case kDNSServiceErr_Unknown:
+ default:
+ return ("Unknown error");
- case kDNSServiceErr_NoSuchName :
- return ("Name not found");
+ case kDNSServiceErr_NoSuchName:
+ return ("Name not found");
- case kDNSServiceErr_NoMemory :
- return ("Out of memory");
+ case kDNSServiceErr_NoMemory:
+ return ("Out of memory");
- case kDNSServiceErr_BadParam :
- return ("Bad parameter");
+ case kDNSServiceErr_BadParam:
+ return ("Bad parameter");
- case kDNSServiceErr_BadReference :
- return ("Bad service reference");
+ case kDNSServiceErr_BadReference:
+ return ("Bad service reference");
- case kDNSServiceErr_BadState :
- return ("Bad state");
+ case kDNSServiceErr_BadState:
+ return ("Bad state");
- case kDNSServiceErr_BadFlags :
- return ("Bad flags argument");
+ case kDNSServiceErr_BadFlags:
+ return ("Bad flags argument");
- case kDNSServiceErr_Unsupported :
- return ("Unsupported feature");
+ case kDNSServiceErr_Unsupported:
+ return ("Unsupported feature");
- case kDNSServiceErr_NotInitialized :
- return ("Not initialized");
+ case kDNSServiceErr_NotInitialized:
+ return ("Not initialized");
- case kDNSServiceErr_AlreadyRegistered :
- return ("Name already registered");
+ case kDNSServiceErr_AlreadyRegistered:
+ return ("Name already registered");
- case kDNSServiceErr_NameConflict :
- return ("Name conflicts");
+ case kDNSServiceErr_NameConflict:
+ return ("Name conflicts");
- case kDNSServiceErr_Invalid :
- return ("Invalid argument");
+ case kDNSServiceErr_Invalid:
+ return ("Invalid argument");
- case kDNSServiceErr_Firewall :
- return ("Firewall prevents access");
+ case kDNSServiceErr_Firewall:
+ return ("Firewall prevents access");
- case kDNSServiceErr_Incompatible :
- return ("Client library incompatible with background daemon");
+ case kDNSServiceErr_Incompatible:
+ return ("Client library incompatible with background daemon");
- case kDNSServiceErr_BadInterfaceIndex :
- return ("Bad interface index");
+ case kDNSServiceErr_BadInterfaceIndex:
+ return ("Bad interface index");
- case kDNSServiceErr_Refused :
- return ("Connection refused");
+ case kDNSServiceErr_Refused:
+ return ("Connection refused");
- case kDNSServiceErr_NoSuchRecord :
- return ("DNS record not found");
+ case kDNSServiceErr_NoSuchRecord:
+ return ("DNS record not found");
- case kDNSServiceErr_NoAuth :
- return ("No authoritative answer");
+ case kDNSServiceErr_NoAuth:
+ return ("No authoritative answer");
- case kDNSServiceErr_NoSuchKey :
- return ("TXT record key not found");
+ case kDNSServiceErr_NoSuchKey:
+ return ("TXT record key not found");
- case kDNSServiceErr_NATTraversal :
- return ("Unable to traverse via NAT");
+ case kDNSServiceErr_NATTraversal:
+ return ("Unable to traverse via NAT");
- case kDNSServiceErr_DoubleNAT :
- return ("Double NAT is in use");
+ case kDNSServiceErr_DoubleNAT:
+ return ("Double NAT is in use");
- case kDNSServiceErr_BadTime :
- return ("Bad time value");
+ case kDNSServiceErr_BadTime:
+ return ("Bad time value");
- case kDNSServiceErr_BadSig :
- return ("Bad signal");
+ case kDNSServiceErr_BadSig:
+ return ("Bad signal");
- case kDNSServiceErr_BadKey :
- return ("Bad TXT record key");
+ case kDNSServiceErr_BadKey:
+ return ("Bad TXT record key");
- case kDNSServiceErr_Transient :
- return ("Transient error");
+ case kDNSServiceErr_Transient:
+ return ("Transient error");
- case kDNSServiceErr_ServiceNotRunning :
- return ("Background daemon not running");
+ case kDNSServiceErr_ServiceNotRunning:
+ return ("Background daemon not running");
- case kDNSServiceErr_NATPortMappingUnsupported :
- return ("NAT doesn't support PCP, NAT-PMP or UPnP");
+ case kDNSServiceErr_NATPortMappingUnsupported:
+ return ("NAT doesn't support PCP, NAT-PMP or UPnP");
- case kDNSServiceErr_NATPortMappingDisabled :
- return ("NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator");
+ case kDNSServiceErr_NATPortMappingDisabled:
+ return ("NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator");
- case kDNSServiceErr_NoRouter :
- return ("No router configured, probably no network connectivity");
+ case kDNSServiceErr_NoRouter:
+ return ("No router configured, probably no network connectivity");
- case kDNSServiceErr_PollingMode :
- return ("Polling error");
+ case kDNSServiceErr_PollingMode:
+ return ("Polling error");
- case kDNSServiceErr_Timeout :
- return ("Timeout");
+ case kDNSServiceErr_Timeout:
+ return ("Timeout");
#if !_WIN32
- case kDNSServiceErr_DefunctConnection :
- return ("Connection lost");
+ case kDNSServiceErr_DefunctConnection:
+ return ("Connection lost");
#endif // !_WIN32
}
@@ -331,13 +320,11 @@ _papplDNSSDStrError(int error) // I - Error code
#endif // HAVE_MDNSRESPONDER
}
-
//
// '_papplDNSSDUnlock()' - Release a lock after making DNS-SD changes.
//
-void
-_papplDNSSDUnlock(void)
+void _papplDNSSDUnlock(void)
{
#ifdef HAVE_AVAHI
if (pappl_dns_sd_poll)
@@ -345,66 +332,64 @@ _papplDNSSDUnlock(void)
#endif // HAVE_AVAHI
}
-
//
// '_papplPrinterRegisterDNSSDNoLock()' - Register a printer's DNS-SD service.
//
-bool // O - `true` on success, `false` on failure
+bool // O - `true` on success, `false` on failure
_papplPrinterRegisterDNSSDNoLock(
- pappl_printer_t *printer) // I - Printer
+ pappl_printer_t *printer) // I - Printer
{
- bool ret = true; // Return value
+ bool ret = true; // Return value
#ifdef HAVE_DNSSD
- pappl_system_t *system = printer->system;
- // System
- uint32_t if_index; // Interface index
- _pappl_txt_t txt; // DNS-SD TXT record
- cups_len_t i, // Looping var
- count; // Number of values
- ipp_attribute_t *color_supported,
- *document_format_supported,
- *printer_kind,
- *printer_uuid,
- *urf_supported; // Printer attributes
- const char *value; // Value string
- char adminurl[246], // Admin URL
- formats[252], // List of supported formats
- kind[251], // List of printer-kind values
- urf[252], // List of supported URF values
- *ptr; // Pointer into string
- char regtype[256]; // DNS-SD service type
- char product[248]; // Make and model (legacy)
- int max_width; // Maximum media width (legacy)
- const char *papermax; // PaperMax string value (legacy)
-# ifdef HAVE_MDNSRESPONDER
- DNSServiceErrorType error; // Error from mDNSResponder
-# else
- int error; // Error from Avahi
- char fullname[256]; // Full service name
-# endif // HAVE_MDNSRESPONDER
- _pappl_dns_sd_t master; // DNS-SD master reference
-
+ pappl_system_t *system = printer->system;
+ // System
+ uint32_t if_index; // Interface index
+ _pappl_txt_t txt; // DNS-SD TXT record
+ cups_len_t i, // Looping var
+ count; // Number of values
+ ipp_attribute_t *color_supported,
+ *document_format_supported,
+ *printer_kind,
+ *printer_uuid,
+ *urf_supported; // Printer attributes
+ const char *value; // Value string
+ char adminurl[246], // Admin URL
+ formats[252], // List of supported formats
+ kind[251], // List of printer-kind values
+ urf[252], // List of supported URF values
+ *ptr; // Pointer into string
+ char regtype[256]; // DNS-SD service type
+ char product[248]; // Make and model (legacy)
+ int max_width; // Maximum media width (legacy)
+ const char *papermax; // PaperMax string value (legacy)
+#ifdef HAVE_MDNSRESPONDER
+ DNSServiceErrorType error; // Error from mDNSResponder
+#else
+ int error; // Error from Avahi
+ char fullname[256]; // Full service name
+#endif // HAVE_MDNSRESPONDER
+ _pappl_dns_sd_t master; // DNS-SD master reference
if (!printer->dns_sd_name || !printer->system->is_running)
return (false);
papplLogPrinter(printer, PAPPL_LOGLEVEL_DEBUG, "Registering DNS-SD name '%s'.", printer->dns_sd_name);
-# ifdef HAVE_MDNSRESPONDER
+#ifdef HAVE_MDNSRESPONDER
if_index = !strcmp(system->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
-# else
+#else
if_index = !strcmp(system->hostname, "localhost") ? if_nametoindex("lo") : AVAHI_IF_UNSPEC;
-# endif // HAVE_MDNSRESPONDER
+#endif // HAVE_MDNSRESPONDER
// Get attributes and values for the TXT record...
- color_supported = ippFindAttribute(printer->driver_attrs, "color-supported", IPP_TAG_BOOLEAN);
+ color_supported = ippFindAttribute(printer->driver_attrs, "color-supported", IPP_TAG_BOOLEAN);
document_format_supported = ippFindAttribute(printer->driver_attrs, "document-format-supported", IPP_TAG_MIMETYPE);
- printer_kind = ippFindAttribute(printer->driver_attrs, "printer-kind", IPP_TAG_KEYWORD);
- printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
- urf_supported = ippFindAttribute(printer->driver_attrs, "urf-supported", IPP_TAG_KEYWORD);
+ printer_kind = ippFindAttribute(printer->driver_attrs, "printer-kind", IPP_TAG_KEYWORD);
+ printer_uuid = ippFindAttribute(printer->attrs, "printer-uuid", IPP_TAG_URI);
+ urf_supported = ippFindAttribute(printer->driver_attrs, "urf-supported", IPP_TAG_KEYWORD);
- for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i ++)
+ for (i = 0, count = ippGetCount(document_format_supported), ptr = formats; i < count; i++)
{
value = ippGetString(document_format_supported, i, NULL);
@@ -422,7 +407,7 @@ _papplPrinterRegisterDNSSDNoLock(
}
kind[0] = '\0';
- for (i = 0, count = ippGetCount(printer_kind), ptr = kind; i < count; i ++)
+ for (i = 0, count = ippGetCount(printer_kind), ptr = kind; i < count; i++)
{
value = ippGetString(printer_kind, i, NULL);
@@ -438,10 +423,10 @@ _papplPrinterRegisterDNSSDNoLock(
snprintf(product, sizeof(product), "(%s)", printer->driver_data.make_and_model);
- for (i = 0, max_width = 0; i < (cups_len_t)printer->driver_data.num_media; i ++)
+ for (i = 0, max_width = 0; i < (cups_len_t)printer->driver_data.num_media; i++)
{
pwg_media_t *media = pwgMediaForPWG(printer->driver_data.media[i]);
- // Current media size
+ // Current media size
if (media && media->width > max_width)
max_width = media->width;
@@ -459,7 +444,7 @@ _papplPrinterRegisterDNSSDNoLock(
papermax = ">isoC-A2";
urf[0] = '\0';
- for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i ++)
+ for (i = 0, count = ippGetCount(urf_supported), ptr = urf; i < count; i++)
{
value = ippGetString(urf_supported, i, NULL);
@@ -481,26 +466,26 @@ _papplPrinterRegisterDNSSDNoLock(
// Rename the service as needed...
if (printer->dns_sd_collision)
{
- char new_dns_sd_name[256]; // New DNS-SD name
- const char *serial = strstr(printer->device_uri, "?serial=");
- // Serial number
- const char *uuid = ippGetString(printer_uuid, 0, NULL);
- // "printer-uuid" value
+ char new_dns_sd_name[256]; // New DNS-SD name
+ const char *serial = strstr(printer->device_uri, "?serial=");
+ // Serial number
+ const char *uuid = ippGetString(printer_uuid, 0, NULL);
+ // "printer-uuid" value
- printer->dns_sd_serial ++;
+ printer->dns_sd_serial++;
if (printer->dns_sd_serial == 1)
{
if (printer->system->options & PAPPL_SOPTIONS_DNSSD_HOST)
- snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, printer->system->hostname);
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, printer->system->hostname);
else if (serial)
- snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, serial + 8);
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", printer->dns_sd_name, serial + 8);
else
- snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", printer->dns_sd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44]));
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", printer->dns_sd_name, toupper(uuid[39]), toupper(uuid[40]), toupper(uuid[41]), toupper(uuid[42]), toupper(uuid[43]), toupper(uuid[44]));
}
else
{
- char base_dns_sd_name[256]; // Base DNS-SD name
+ char base_dns_sd_name[256]; // Base DNS-SD name
papplCopyString(base_dns_sd_name, printer->dns_sd_name, sizeof(base_dns_sd_name));
if ((ptr = strrchr(base_dns_sd_name, '(')) != NULL)
@@ -566,7 +551,7 @@ _papplPrinterRegisterDNSSDNoLock(
printer->dns_sd_printer_ref = master;
- if ((error = DNSServiceRegister(&printer->dns_sd_printer_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_printer._tcp", NULL /* domain */, /*hostname*/NULL, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&printer->dns_sd_printer_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_printer._tcp", NULL /* domain */, /*hostname*/ NULL, 0 /* port */, 0 /* txtLen */, NULL /* txtRecord */, (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._printer._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -584,7 +569,7 @@ _papplPrinterRegisterDNSSDNoLock(
else
papplCopyString(regtype, "_ipp._tcp", sizeof(regtype));
- if ((error = DNSServiceRegister(&printer->dns_sd_ipp_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, regtype, NULL /* domain */, /*hostname*/NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&printer->dns_sd_ipp_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, regtype, NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
ret = false;
@@ -611,7 +596,7 @@ _papplPrinterRegisterDNSSDNoLock(
else
papplCopyString(regtype, "_ipps._tcp", sizeof(regtype));
- if ((error = DNSServiceRegister(&printer->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, regtype, NULL /* domain */, /*hostname*/NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&printer->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, regtype, NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
ret = false;
@@ -621,8 +606,8 @@ _papplPrinterRegisterDNSSDNoLock(
{
if ((error = DNSServiceAddRecord(printer->dns_sd_ipps_ref, &printer->dns_sd_ipps_loc_ref, 0, kDNSServiceType_LOC, sizeof(printer->dns_sd_loc), printer->dns_sd_loc, 0)) != kDNSServiceErr_NoError)
{
- papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
- ret = false;
+ papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
+ ret = false;
}
}
}
@@ -662,7 +647,7 @@ _papplPrinterRegisterDNSSDNoLock(
printer->dns_sd_pdl_ref = master;
- if ((error = DNSServiceRegister(&printer->dns_sd_pdl_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_pdl-datastream._tcp", NULL /* domain */, /*hostname*/NULL, htons(9099 + printer->printer_id), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&printer->dns_sd_pdl_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_pdl-datastream._tcp", NULL /* domain */, /*hostname*/ NULL, htons(9099 + printer->printer_id), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, "_pdl-datastream._tcp", _papplDNSSDStrError(error));
ret = false;
@@ -683,7 +668,7 @@ _papplPrinterRegisterDNSSDNoLock(
printer->dns_sd_http_ref = master;
- if ((error = DNSServiceRegister(&printer->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, /*hostname*/NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&printer->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, printer->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_printer_callback, printer)) != kDNSServiceErr_NoError)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, "_http._tcp,_printer", _papplDNSSDStrError(error));
ret = false;
@@ -741,7 +726,7 @@ _papplPrinterRegisterDNSSDNoLock(
}
// Then register the IPP/IPPS services...
- if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipp._tcp", NULL, /*hostname*/NULL, system->port, txt)) < 0)
+ if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipp._tcp", NULL, /*hostname*/ NULL, system->port, txt)) < 0)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipp._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -750,14 +735,14 @@ _papplPrinterRegisterDNSSDNoLock(
if (system->subtypes && *system->subtypes)
{
char *temptypes = strdup(system->subtypes), *start, *end;
- // Pointers into sub-types...
+ // Pointers into sub-types...
for (start = temptypes; start && *start; start = end)
{
if ((end = strchr(start, ',')) != NULL)
- *end++ = '\0';
+ *end++ = '\0';
else
- end = start + strlen(start);
+ end = start + strlen(start);
snprintf(regtype, sizeof(regtype), "%s._sub._ipp._tcp", start);
if ((error = avahi_entry_group_add_service_subtype(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipp._tcp", NULL, regtype)) < 0)
@@ -772,7 +757,7 @@ _papplPrinterRegisterDNSSDNoLock(
if (!(printer->system->options & PAPPL_SOPTIONS_NO_TLS))
{
- if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, /*hostname*/NULL, system->port, txt)) < 0)
+ if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, /*hostname*/ NULL, system->port, txt)) < 0)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -781,21 +766,21 @@ _papplPrinterRegisterDNSSDNoLock(
if (system->subtypes && *system->subtypes)
{
char *temptypes = strdup(system->subtypes), *start, *end;
- // Pointers into sub-types...
+ // Pointers into sub-types...
for (start = temptypes; start && *start; start = end)
{
- if ((end = strchr(start, ',')) != NULL)
- *end++ = '\0';
- else
- end = start + strlen(start);
-
- snprintf(regtype, sizeof(regtype), "%s._sub._ipps._tcp", start);
- if ((error = avahi_entry_group_add_service_subtype(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, regtype)) < 0)
- {
- papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
- ret = false;
- }
+ if ((end = strchr(start, ',')) != NULL)
+ *end++ = '\0';
+ else
+ end = start + strlen(start);
+
+ snprintf(regtype, sizeof(regtype), "%s._sub._ipps._tcp", start);
+ if ((error = avahi_entry_group_add_service_subtype(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_ipps._tcp", NULL, regtype)) < 0)
+ {
+ papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", printer->dns_sd_name, regtype, _papplDNSSDStrError(error));
+ ret = false;
+ }
}
free(temptypes);
@@ -827,7 +812,7 @@ _papplPrinterRegisterDNSSDNoLock(
txt = avahi_string_list_add_printf(txt, "PaperMax=%s", papermax);
txt = avahi_string_list_add_printf(txt, "Scan=F");
- if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_pdl-datastream._tcp", NULL, /*hostname*/NULL, 9099 + printer->printer_id, txt)) < 0)
+ if ((error = avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_pdl-datastream._tcp", NULL, /*hostname*/ NULL, 9099 + printer->printer_id, txt)) < 0)
{
papplLogPrinter(printer, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._pdl-datastream._tcp': %s", printer->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -860,7 +845,7 @@ _papplPrinterRegisterDNSSDNoLock(
txt = NULL;
txt = avahi_string_list_add_printf(txt, "path=%s/", printer->uriname);
- avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_http._tcp", NULL, /*hostname*/NULL, system->port, txt);
+ avahi_entry_group_add_service_strlst(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_http._tcp", NULL, /*hostname*/ NULL, system->port, txt);
avahi_entry_group_add_service_subtype(printer->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, printer->dns_sd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
avahi_string_list_free(txt);
@@ -873,14 +858,12 @@ _papplPrinterRegisterDNSSDNoLock(
return (ret);
}
-
//
// '_papplPrinterUnregisterDNSSDNoLock()' - Unregister a printer's DNS-SD service.
//
-void
-_papplPrinterUnregisterDNSSDNoLock(
- pappl_printer_t *printer) // I - Printer
+void _papplPrinterUnregisterDNSSDNoLock(
+ pappl_printer_t *printer) // I - Printer
{
#if HAVE_MDNSRESPONDER
if (printer->dns_sd_printer_ref)
@@ -891,13 +874,13 @@ _papplPrinterUnregisterDNSSDNoLock(
if (printer->dns_sd_ipp_ref)
{
DNSServiceRefDeallocate(printer->dns_sd_ipp_ref);
- printer->dns_sd_ipp_ref = NULL;
+ printer->dns_sd_ipp_ref = NULL;
printer->dns_sd_ipp_loc_ref = NULL;
}
if (printer->dns_sd_ipps_ref)
{
DNSServiceRefDeallocate(printer->dns_sd_ipps_ref);
- printer->dns_sd_ipps_ref = NULL;
+ printer->dns_sd_ipps_ref = NULL;
printer->dns_sd_ipps_loc_ref = NULL;
}
if (printer->dns_sd_http_ref)
@@ -922,27 +905,469 @@ _papplPrinterUnregisterDNSSDNoLock(
#endif // HAVE_MDNSRESPONDER
}
+//
+// _papplScannerRegisterDNSSDNoLock - Register a scanner's DNS-SD service.
+//
+
+bool _papplScannerRegisterDNSSDNoLock(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ bool ret = true; // Return value
+#ifdef HAVE_DNSSD
+ pappl_system_t *system = scanner->system; // System
+ uint32_t if_index; // Interface index
+ _pappl_txt_t txt; // DNS-SD TXT record
+ cups_len_t i, // Looping var
+ count; // Number of values
+ char adminurl[246], // Admin URL
+ formats[252], // List of supported formats
+ sources[252], // List of input sources
+ intents[252], // List of scanning intents
+ colorspaces[252], // List of color spaces
+ color_modes[252], // List of color modes
+ *ptr; // Pointer into string
+#endif
+#ifdef HAVE_MDNSRESPONDER
+ DNSServiceErrorType error; // Error from mDNSResponder
+#else
+ int error; // Error from Avahi
+ char fullname[256]; // Full service name
+#endif // HAVE_MDNSRESPONDER
+ _pappl_dns_sd_t master; // DNS-SD master reference
+
+ if (!scanner->dns_sd_name || !scanner->system->is_running)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_DEBUG, "DNS-SD name not set or system not running.");
+ return (false);
+ }
+
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_DEBUG, "Registering DNS-SD name '%s'.", scanner->dns_sd_name);
+
+#ifdef HAVE_MDNSRESPONDER
+ if_index = !strcmp(system->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
+#else
+ if_index = !strcmp(system->hostname, "localhost") ? if_nametoindex("lo") : AVAHI_IF_UNSPEC;
+#endif // HAVE_MDNSRESPONDER
+
+ // Build formats string
+ for (i = 0, count = 0, ptr = formats; scanner->driver_data.document_formats_supported[i] && i < PAPPL_MAX_FORMATS; i++)
+ {
+ if (count > 0 && ptr < (formats + sizeof(formats) - 1))
+ *ptr++ = ',';
+
+ papplCopyString(ptr, scanner->driver_data.document_formats_supported[i], sizeof(formats) - (size_t)(ptr - formats));
+ ptr += strlen(ptr);
+ count++;
+
+ if (ptr >= (formats + sizeof(formats) - 1))
+ break;
+ }
+
+ // Build input sources string
+ sources[0] = '\0';
+ ptr = sources;
+ for (i = 0; i < PAPPL_MAX_SOURCES && scanner->driver_data.input_sources_supported[i]; i++)
+ {
+ if (i > 0)
+ *ptr++ = ',';
+ if (scanner->driver_data.input_sources_supported[i] == PAPPL_FLATBED)
+ ptr += snprintf(ptr, sizeof(sources) - (size_t)(ptr - sources), "platen");
+ else if (scanner->driver_data.input_sources_supported[i] == PAPPL_ADF)
+ ptr += snprintf(ptr, sizeof(sources) - (size_t)(ptr - sources), "adf");
+ }
+
+ // Build color spaces string
+ for (i = 0, count = 0, ptr = colorspaces; scanner->driver_data.color_spaces_supported[i] && i < PAPPL_MAX_COLOR_SPACES; i++)
+ {
+ if (count > 0)
+ *ptr++ = ',';
+ papplCopyString(ptr, scanner->driver_data.color_spaces_supported[i], sizeof(colorspaces) - (size_t)(ptr - colorspaces));
+ ptr += strlen(ptr);
+ count++;
+ }
+
+ // Build intents string (combining mandatory and optional)
+ intents[0] = '\0';
+ ptr = intents;
+ for (i = 0; i < 5 && scanner->driver_data.mandatory_intents[i]; i++)
+ {
+ if (i > 0)
+ *ptr++ = ',';
+ ptr += snprintf(ptr, sizeof(intents) - (size_t)(ptr - intents), "%s", scanner->driver_data.mandatory_intents[i]);
+ }
+ for (i = 0; i < 5 && scanner->driver_data.optional_intents[i]; i++)
+ {
+ if (ptr > intents)
+ *ptr++ = ',';
+ ptr += snprintf(ptr, sizeof(intents) - (size_t)(ptr - intents), "%s", scanner->driver_data.optional_intents[i]);
+ }
+
+ httpAssembleURIf(HTTP_URI_CODING_ALL, adminurl, sizeof(adminurl), "http", NULL, scanner->system->hostname, scanner->system->port, "%s/", scanner->uriname);
+
+ if (scanner->geo_location)
+ dns_sd_geo_to_loc(scanner->geo_location, scanner->dns_sd_loc);
+
+ // Handle name collisions
+ if (scanner->dns_sd_collision)
+ {
+ char new_dns_sd_name[256]; // New DNS-SD name
+
+ scanner->dns_sd_serial++;
+
+ if (scanner->dns_sd_serial == 1)
+ {
+ if (scanner->system->options & PAPPL_SOPTIONS_DNSSD_HOST)
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", scanner->dns_sd_name, scanner->system->hostname);
+ else
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%d)", scanner->dns_sd_name, scanner->scanner_id);
+ }
+ else
+ {
+ char base_dns_sd_name[256]; // Base DNS-SD name
+
+ papplCopyString(base_dns_sd_name, scanner->dns_sd_name, sizeof(base_dns_sd_name));
+ if ((ptr = strrchr(base_dns_sd_name, '(')) != NULL)
+ *ptr = '\0';
+
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s(%d)", base_dns_sd_name, scanner->dns_sd_serial);
+ }
+
+ free(scanner->dns_sd_name);
+ if ((scanner->dns_sd_name = strdup(new_dns_sd_name)) != NULL)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_INFO, "DNS-SD name collision, trying new DNS-SD service name '%s'.", scanner->dns_sd_name);
+ scanner->dns_sd_collision = false;
+ }
+ else
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "DNS-SD name collision, failed to allocate new DNS-SD service name.");
+ return (false);
+ }
+ }
+
+ if ((master = _papplDNSSDInit(scanner->system)) == NULL)
+ return (false);
+
+#ifdef HAVE_MDNSRESPONDER
+ // Build the TXT record for eSCL
+ TXTRecordCreate(&txt, 1024, NULL);
+
+ // Add make and model if available
+ if (scanner->driver_data.make_and_model[0])
+ TXTRecordSetValue(&txt, "ty", (uint8_t)strlen(scanner->driver_data.make_and_model), scanner->driver_data.make_and_model);
+
+ // Add admin URL
+ TXTRecordSetValue(&txt, "adminurl", (uint8_t)strlen(adminurl), adminurl);
+
+ // Add location if available
+ TXTRecordSetValue(&txt, "note", (uint8_t)strlen(scanner->location ? scanner->location : ""), scanner->location ? scanner->location : "");
+
+ // Add supported formats
+ TXTRecordSetValue(&txt, "formats", (uint8_t)strlen(formats), formats);
+
+ // Add UUID if available
+ if (scanner->uuid)
+ TXTRecordSetValue(&txt, "uuid", (uint8_t)strlen(scanner->uuid) - 9, scanner->uuid + 9);
+
+ // Add input sources
+ if (sources[0])
+ TXTRecordSetValue(&txt, "is", (uint8_t)strlen(sources), sources);
+
+ // Add color spaces
+ if (colorspaces[0])
+ TXTRecordSetValue(&txt, "cs", (uint8_t)strlen(colorspaces), colorspaces);
+
+ // Add duplex support
+ TXTRecordSetValue(&txt, "duplex", 1, scanner->driver_data.duplex_supported ? "T" : "F");
+
+ // Add scanning intents
+ if (intents[0])
+ TXTRecordSetValue(&txt, "intents", (uint8_t)strlen(intents), intents);
+
+ // Add resolutions
+ char res_str[252] = "";
+ ptr = res_str;
+ for (i = 0; i < MAX_RESOLUTIONS && scanner->driver_data.resolutions[i]; i++)
+ {
+ if (i > 0)
+ ptr += snprintf(ptr, sizeof(res_str) - (size_t)(ptr - res_str), ",");
+ ptr += snprintf(ptr, sizeof(res_str) - (size_t)(ptr - res_str), "%d", scanner->driver_data.resolutions[i]);
+ }
+ if (res_str[0])
+ TXTRecordSetValue(&txt, "rs", (uint8_t)strlen(res_str), res_str);
+
+ // Add scan area
+ char area_str[32];
+ snprintf(area_str, sizeof(area_str), "%dx%d", scanner->driver_data.max_scan_area[0], scanner->driver_data.max_scan_area[1]);
+ TXTRecordSetValue(&txt, "area", (uint8_t)strlen(area_str), area_str);
+
+ // Add color modes
+ char color_modes[252] = "";
+ ptr = color_modes;
+ for (i = 0; i < PAPPL_MAX_COLOR_MODES && scanner->driver_data.color_modes_supported[i]; i++)
+ {
+ if (i > 0)
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), ",");
+
+ switch (scanner->driver_data.color_modes_supported[i])
+ {
+ case PAPPL_BLACKANDWHITE1:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "BlackAndWhite1");
+ break;
+ case PAPPL_GRAYSCALE8:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "Grayscale8");
+ break;
+ case PAPPL_RGB24:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "RGB24");
+ break;
+ }
+ }
+ if (color_modes[0])
+ TXTRecordSetValue(&txt, "modes", (uint8_t)strlen(color_modes), color_modes);
+
+ // Add default values
+ char defaults[252];
+ snprintf(defaults, sizeof(defaults), "dpi=%d", scanner->driver_data.default_resolution);
+ TXTRecordSetValue(&txt, "defaults", (uint8_t)strlen(defaults), defaults);
+
+ // Add scan region
+ char region[64];
+ snprintf(region, sizeof(region), "%d,%d,%d,%d",
+ scanner->driver_data.scan_region_supported[0],
+ scanner->driver_data.scan_region_supported[1],
+ scanner->driver_data.scan_region_supported[2],
+ scanner->driver_data.scan_region_supported[3]);
+ TXTRecordSetValue(&txt, "region", (uint8_t)strlen(region), region);
+
+ // Add standard required fields
+ TXTRecordSetValue(&txt, "txtvers", 1, "1");
+ TXTRecordSetValue(&txt, "TLS", 3, "1.2");
+
+ // Register eSCL service
+ if (scanner->dns_sd_escl_ref)
+ DNSServiceRefDeallocate(scanner->dns_sd_escl_ref);
+
+ scanner->dns_sd_escl_ref = master;
+
+ if ((error = DNSServiceRegister(&scanner->dns_sd_escl_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, scanner->dns_sd_name, "_uscan._tcp", NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_scanner_callback, scanner)) != kDNSServiceErr_NoError)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._uscan._tcp': %s", scanner->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
+ }
+
+ if (scanner->geo_location && ret)
+ {
+ if ((error = DNSServiceAddRecord(scanner->dns_sd_escl_ref, &scanner->dns_sd_escl_loc_ref, 0, kDNSServiceType_LOC, sizeof(scanner->dns_sd_loc), scanner->dns_sd_loc, 0)) != kDNSServiceErr_NoError)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s._uscan._tcp': %s", scanner->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
+ }
+ }
+
+ TXTRecordDeallocate(&txt);
+
+ // Register HTTP service
+ if (scanner->dns_sd_http_ref)
+ DNSServiceRefDeallocate(scanner->dns_sd_http_ref);
+
+ TXTRecordCreate(&txt, 1024, NULL);
+ snprintf(adminurl, sizeof(adminurl), "%s/", scanner->uriname);
+ TXTRecordSetValue(&txt, "path", (uint8_t)strlen(adminurl), adminurl);
+
+ scanner->dns_sd_http_ref = master;
+
+ if ((error = DNSServiceRegister(&scanner->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, scanner->dns_sd_name, "_http._tcp,_scanner", NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_scanner_callback, scanner)) != kDNSServiceErr_NoError)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._http._tcp,_scanner': %s", scanner->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
+ }
+
+ TXTRecordDeallocate(&txt);
+
+#elif defined(HAVE_AVAHI)
+ // Create the TXT record for Avahi
+ txt = NULL;
+ if (scanner->driver_data.make_and_model[0])
+ txt = avahi_string_list_add_printf(txt, "ty=%s", scanner->driver_data.make_and_model);
+ txt = avahi_string_list_add_printf(txt, "adminurl=%s", adminurl);
+ txt = avahi_string_list_add_printf(txt, "note=%s", scanner->location ? scanner->location : "");
+ txt = avahi_string_list_add_printf(txt, "formats=%s", formats);
+ if (scanner->uuid)
+ txt = avahi_string_list_add_printf(txt, "uuid=%s", scanner->uuid + 9);
+ if (sources[0])
+ txt = avahi_string_list_add_printf(txt, "is=%s", sources);
+ if (colorspaces[0])
+ txt = avahi_string_list_add_printf(txt, "cs=%s", colorspaces);
+ txt = avahi_string_list_add_printf(txt, "duplex=%s", scanner->driver_data.duplex_supported ? "T" : "F");
+ if (intents[0])
+ txt = avahi_string_list_add_printf(txt, "intents=%s", intents);
+
+ // Add resolutions
+ char res_str[252] = "";
+ ptr = res_str;
+ for (i = 0; i < MAX_RESOLUTIONS && scanner->driver_data.resolutions[i]; i++)
+ {
+ if (i > 0)
+ ptr += snprintf(ptr, sizeof(res_str) - (size_t)(ptr - res_str), ",");
+ ptr += snprintf(ptr, sizeof(res_str) - (size_t)(ptr - res_str), "%d", scanner->driver_data.resolutions[i]);
+ }
+ if (res_str[0])
+ txt = avahi_string_list_add_printf(txt, "rs=%s", res_str);
+
+ // Add scan area
+ txt = avahi_string_list_add_printf(txt, "area=%dx%d", scanner->driver_data.max_scan_area[0], scanner->driver_data.max_scan_area[1]);
+
+ // Add color modes
+ // color_modes[252] = "";
+ ptr = color_modes;
+ for (i = 0; i < PAPPL_MAX_COLOR_MODES && scanner->driver_data.color_modes_supported[i]; i++)
+ {
+ if (i > 0)
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), ",");
+
+ switch (scanner->driver_data.color_modes_supported[i])
+ {
+ case PAPPL_BLACKANDWHITE1:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "BlackAndWhite1");
+ break;
+ case PAPPL_GRAYSCALE8:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "Grayscale8");
+ break;
+ case PAPPL_RGB24:
+ ptr += snprintf(ptr, sizeof(color_modes) - (size_t)(ptr - color_modes), "RGB24");
+ break;
+ }
+ }
+ if (color_modes[0])
+ txt = avahi_string_list_add_printf(txt, "modes=%s", color_modes);
+
+ // Add default values
+ txt = avahi_string_list_add_printf(txt, "defaults=dpi=%d", scanner->driver_data.default_resolution);
+
+ // Add scan region
+ txt = avahi_string_list_add_printf(txt, "region=%d,%d,%d,%d",
+ scanner->driver_data.scan_region_supported[0],
+ scanner->driver_data.scan_region_supported[1],
+ scanner->driver_data.scan_region_supported[2],
+ scanner->driver_data.scan_region_supported[3]);
+
+ // Add standard required fields
+ txt = avahi_string_list_add_printf(txt, "txtvers=1");
+ txt = avahi_string_list_add_printf(txt, "TLS=1.2");
+
+ _papplDNSSDLock();
+
+ if (scanner->dns_sd_ref)
+ avahi_entry_group_free(scanner->dns_sd_ref);
+
+ if ((scanner->dns_sd_ref = avahi_entry_group_new(master, (AvahiEntryGroupCallback)dns_sd_scanner_callback, scanner)) == NULL)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register scanner, is the Avahi daemon running?");
+ _papplDNSSDUnlock();
+ avahi_string_list_free(txt);
+ return (false);
+ }
+
+ // Register eSCL service
+ if ((error = avahi_entry_group_add_service_strlst(scanner->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, scanner->dns_sd_name, "_uscan._tcp", NULL, /*hostname*/ NULL, system->port, txt)) < 0)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._uscan._tcp': %s", scanner->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
+ }
+
+ // Add geolocation record if available
+ if (scanner->geo_location && ret)
+ {
+ snprintf(fullname, sizeof(fullname), "%s._uscan._tcp.local.", scanner->dns_sd_name);
+
+ if ((error = avahi_entry_group_add_record(scanner->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, 75 * 60, scanner->dns_sd_loc, sizeof(scanner->dns_sd_loc))) < 0)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error));
+ ret = false;
+ }
+ }
+
+ avahi_string_list_free(txt);
+
+ // Register HTTP service
+ txt = NULL;
+ txt = avahi_string_list_add_printf(txt, "path=%s/", scanner->uriname);
+
+ if ((error = avahi_entry_group_add_service_strlst(scanner->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, scanner->dns_sd_name, "_http._tcp", NULL, /*hostname*/ NULL, system->port, txt)) < 0)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._http._tcp': %s", scanner->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
+ }
+
+ // Add scanner subtype for HTTP service
+ avahi_entry_group_add_service_subtype(scanner->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, scanner->dns_sd_name, "_http._tcp", NULL, "_scanner._sub._http._tcp");
+
+ avahi_string_list_free(txt);
+
+ // Commit the group
+ avahi_entry_group_commit(scanner->dns_sd_ref);
+ _papplDNSSDUnlock();
+
+#endif // HAVE_MDNSRESPONDER
+
+ return (ret);
+}
+
+//
+// '_papplScannerUnregisterDNSSDNoLock()' - Unregister a scanner's DNS-SD service.
+//
+void _papplScannerUnregisterDNSSDNoLock(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+#if HAVE_MDNSRESPONDER
+ if (scanner->dns_sd_escl_ref)
+ {
+ DNSServiceRefDeallocate(scanner->dns_sd_escl_ref);
+ scanner->dns_sd_escl_ref = NULL;
+ scanner->dns_sd_escl_loc_ref = NULL;
+ }
+
+ if (scanner->dns_sd_http_ref)
+ {
+ DNSServiceRefDeallocate(scanner->dns_sd_http_ref);
+ scanner->dns_sd_http_ref = NULL;
+ }
+
+#elif defined(HAVE_AVAHI)
+ _papplDNSSDLock();
+
+ if (scanner->dns_sd_ref)
+ {
+ avahi_entry_group_free(scanner->dns_sd_ref);
+ scanner->dns_sd_ref = NULL;
+ }
+
+ _papplDNSSDUnlock();
+
+#else
+ (void)scanner;
+#endif // HAVE_MDNSRESPONDER
+}
//
// '_papplSystemRegisterDNSSDNoLock()' - Register a system's DNS-SD service.
//
-bool // O - `true` on success, `false` on failure
+bool // O - `true` on success, `false` on failure
_papplSystemRegisterDNSSDNoLock(
- pappl_system_t *system) // I - System
+ pappl_system_t *system) // I - System
{
- bool ret = true; // Return value
+ bool ret = true; // Return value
#ifdef HAVE_DNSSD
- _pappl_dns_sd_t master; // DNS-SD master reference
- _pappl_txt_t txt; // DNS-SD TXT record
- uint32_t if_index; // Interface index
-# ifdef HAVE_MDNSRESPONDER
- DNSServiceErrorType error; // Error from mDNSResponder
-# else
- int error; // Error from Avahi
- char fullname[256]; // Full name of services
-# endif // HAVE_MDNSRESPONDER
-
+ _pappl_dns_sd_t master; // DNS-SD master reference
+ _pappl_txt_t txt; // DNS-SD TXT record
+ uint32_t if_index; // Interface index
+#ifdef HAVE_MDNSRESPONDER
+ DNSServiceErrorType error; // Error from mDNSResponder
+#else
+ int error; // Error from Avahi
+ char fullname[256]; // Full name of services
+#endif // HAVE_MDNSRESPONDER
// Make sure we have all of the necessary information to register the system...
if (!system->dns_sd_name || !system->hostname || !system->uuid || !system->is_running)
@@ -950,11 +1375,11 @@ _papplSystemRegisterDNSSDNoLock(
papplLog(system, PAPPL_LOGLEVEL_DEBUG, "Registering DNS-SD name '%s'.", system->dns_sd_name);
-# ifdef HAVE_MDNSRESPONDER
+#ifdef HAVE_MDNSRESPONDER
if_index = !strcmp(system->hostname, "localhost") ? kDNSServiceInterfaceIndexLocalOnly : kDNSServiceInterfaceIndexAny;
-# else
+#else
if_index = !strcmp(system->hostname, "localhost") ? if_nametoindex("lo") : AVAHI_IF_UNSPEC;
-# endif // HAVE_MDNSRESPONDER
+#endif // HAVE_MDNSRESPONDER
if (system->geo_location)
dns_sd_geo_to_loc(system->geo_location, system->dns_sd_loc);
@@ -962,21 +1387,21 @@ _papplSystemRegisterDNSSDNoLock(
// Rename the service as needed...
if (system->dns_sd_collision)
{
- char new_dns_sd_name[256]; // New DNS-SD name
+ char new_dns_sd_name[256]; // New DNS-SD name
- system->dns_sd_serial ++;
+ system->dns_sd_serial++;
if (system->dns_sd_serial == 1)
{
if (system->options & PAPPL_SOPTIONS_DNSSD_HOST)
- snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", system->dns_sd_name, system->hostname);
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%s)", system->dns_sd_name, system->hostname);
else
- snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", system->dns_sd_name, toupper(system->uuid[39]), toupper(system->uuid[40]), toupper(system->uuid[41]), toupper(system->uuid[42]), toupper(system->uuid[43]), toupper(system->uuid[44]));
+ snprintf(new_dns_sd_name, sizeof(new_dns_sd_name), "%s (%c%c%c%c%c%c)", system->dns_sd_name, toupper(system->uuid[39]), toupper(system->uuid[40]), toupper(system->uuid[41]), toupper(system->uuid[42]), toupper(system->uuid[43]), toupper(system->uuid[44]));
}
else
{
- char base_dns_sd_name[256], // Base DNS-SD name
- *ptr; // Pointer into name
+ char base_dns_sd_name[256], // Base DNS-SD name
+ *ptr; // Pointer into name
papplCopyString(base_dns_sd_name, system->dns_sd_name, sizeof(base_dns_sd_name));
if ((ptr = strrchr(base_dns_sd_name, '(')) != NULL)
@@ -1019,7 +1444,7 @@ _papplSystemRegisterDNSSDNoLock(
{
system->dns_sd_ipps_ref = master;
- if ((error = DNSServiceRegister(&system->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, system->dns_sd_name, "_ipps-system._tcp", NULL /* domain */, /*hostname*/NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&system->dns_sd_ipps_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, system->dns_sd_name, "_ipps-system._tcp", NULL /* domain */, /*hostname*/ NULL, htons(system->port), TXTRecordGetLength(&txt), TXTRecordGetBytesPtr(&txt), (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError)
{
papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -1031,8 +1456,8 @@ _papplSystemRegisterDNSSDNoLock(
if ((error = DNSServiceAddRecord(system->dns_sd_ipps_ref, &system->dns_sd_loc_ref, 0, kDNSServiceType_LOC, sizeof(system->dns_sd_loc), system->dns_sd_loc, 0)) != kDNSServiceErr_NoError)
{
- papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error));
- ret = false;
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error));
+ ret = false;
}
}
}
@@ -1050,7 +1475,7 @@ _papplSystemRegisterDNSSDNoLock(
system->dns_sd_http_ref = master;
- if ((error = DNSServiceRegister(&system->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, system->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, /*hostname*/NULL, htons(system->port), 0 /* txtlen */, NULL /* txt */, (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError)
+ if ((error = DNSServiceRegister(&system->dns_sd_http_ref, kDNSServiceFlagsShareConnection | kDNSServiceFlagsNoAutoRename, if_index, system->dns_sd_name, "_http._tcp,_printer", NULL /* domain */, /*hostname*/ NULL, htons(system->port), 0 /* txtlen */, NULL /* txt */, (DNSServiceRegisterReply)dns_sd_system_callback, system)) != kDNSServiceErr_NoError)
{
papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s.%s': %s", system->dns_sd_name, "_http._tcp,_printer", _papplDNSSDStrError(error));
ret = false;
@@ -1080,7 +1505,7 @@ _papplSystemRegisterDNSSDNoLock(
if (!(system->options & PAPPL_SOPTIONS_NO_TLS))
{
- if ((error = avahi_entry_group_add_service_strlst(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_ipps-system._tcp", NULL, /*hostname*/NULL, system->port, txt)) < 0)
+ if ((error = avahi_entry_group_add_service_strlst(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_ipps-system._tcp", NULL, /*hostname*/ NULL, system->port, txt)) < 0)
{
papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register '%s._ipps-system._tcp': %s", system->dns_sd_name, _papplDNSSDStrError(error));
ret = false;
@@ -1095,8 +1520,8 @@ _papplSystemRegisterDNSSDNoLock(
if ((error = avahi_entry_group_add_record(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, fullname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_LOC, 75 * 60, system->dns_sd_loc, sizeof(system->dns_sd_loc))) < 0)
{
- papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error));
- ret = false;
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to register LOC record for '%s': %s", fullname, _papplDNSSDStrError(error));
+ ret = false;
}
}
}
@@ -1104,7 +1529,7 @@ _papplSystemRegisterDNSSDNoLock(
// Finally _http.tcp (HTTP) for the web interface...
if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE)
{
- avahi_entry_group_add_service_strlst(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_http._tcp", NULL, /*hostname*/NULL, system->port, NULL);
+ avahi_entry_group_add_service_strlst(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_http._tcp", NULL, /*hostname*/ NULL, system->port, NULL);
avahi_entry_group_add_service_subtype(system->dns_sd_ref, if_index, AVAHI_PROTO_UNSPEC, 0, system->dns_sd_name, "_http._tcp", NULL, "_printer._sub._http._tcp");
}
@@ -1118,14 +1543,12 @@ _papplSystemRegisterDNSSDNoLock(
return (ret);
}
-
//
// '_papplSystemUnregisterDNSSDNoLock()' - Unregister a printer's DNS-SD service.
//
-void
-_papplSystemUnregisterDNSSDNoLock(
- pappl_system_t *system) // I - System
+void _papplSystemUnregisterDNSSDNoLock(
+ pappl_system_t *system) // I - System
{
#if HAVE_MDNSRESPONDER
if (system->dns_sd_ipps_ref)
@@ -1156,50 +1579,48 @@ _papplSystemUnregisterDNSSDNoLock(
#endif // HAVE_MDNSRESPONDER
}
-
//
// 'dns_sd_geo_to_loc()' - Convert a "geo:" URI to a DNS LOC record.
//
static void
-dns_sd_geo_to_loc(const char *geo, // I - "geo:" URI
- unsigned char loc[16])// O - DNS LOC record
+dns_sd_geo_to_loc(const char *geo, // I - "geo:" URI
+ unsigned char loc[16]) // O - DNS LOC record
{
- double lat = 0.0, lon = 0.0; // Latitude and longitude in degrees
- double alt = 0.0; // Altitude in meters
- unsigned int lat_ksec, lon_ksec; // Latitude and longitude in thousandths of arc seconds, biased by 2^31
- unsigned int alt_cm; // Altitude in centimeters, biased by 10,000,000cm
+ double lat = 0.0, lon = 0.0; // Latitude and longitude in degrees
+ double alt = 0.0; // Altitude in meters
+ unsigned int lat_ksec, lon_ksec; // Latitude and longitude in thousandths of arc seconds, biased by 2^31
+ unsigned int alt_cm; // Altitude in centimeters, biased by 10,000,000cm
// Pull apart the "geo:" URI and convert to the integer representation for
// the LOC record...
sscanf(geo, "geo:%lf,%lf,%lf", &lat, &lon, &alt);
lat_ksec = (unsigned)((int)(lat * 3600000.0) + 0x40000000 + 0x40000000);
lon_ksec = (unsigned)((int)(lon * 3600000.0) + 0x40000000 + 0x40000000);
- alt_cm = (unsigned)((int)(alt * 100.0) + 10000000);
+ alt_cm = (unsigned)((int)(alt * 100.0) + 10000000);
// Build the LOC record...
- loc[0] = 0x00; // Version
- loc[1] = 0x11; // Size (10cm)
- loc[2] = 0x11; // Horizontal precision (10cm)
- loc[3] = 0x11; // Vertical precision (10cm)
- loc[4] = (unsigned char)(lat_ksec >> 24);
- // Latitude (32-bit big-endian)
- loc[5] = (unsigned char)(lat_ksec >> 16);
- loc[6] = (unsigned char)(lat_ksec >> 8);
- loc[7] = (unsigned char)(lat_ksec);
- loc[8] = (unsigned char)(lon_ksec >> 24);
- // Latitude (32-bit big-endian)
- loc[9] = (unsigned char)(lon_ksec >> 16);
+ loc[0] = 0x00; // Version
+ loc[1] = 0x11; // Size (10cm)
+ loc[2] = 0x11; // Horizontal precision (10cm)
+ loc[3] = 0x11; // Vertical precision (10cm)
+ loc[4] = (unsigned char)(lat_ksec >> 24);
+ // Latitude (32-bit big-endian)
+ loc[5] = (unsigned char)(lat_ksec >> 16);
+ loc[6] = (unsigned char)(lat_ksec >> 8);
+ loc[7] = (unsigned char)(lat_ksec);
+ loc[8] = (unsigned char)(lon_ksec >> 24);
+ // Latitude (32-bit big-endian)
+ loc[9] = (unsigned char)(lon_ksec >> 16);
loc[10] = (unsigned char)(lon_ksec >> 8);
loc[11] = (unsigned char)(lon_ksec);
loc[12] = (unsigned char)(alt_cm >> 24);
- // Altitude (32-bit big-endian)
+ // Altitude (32-bit big-endian)
loc[13] = (unsigned char)(alt_cm >> 16);
loc[14] = (unsigned char)(alt_cm >> 8);
loc[15] = (unsigned char)(alt_cm);
}
-
#ifdef HAVE_MDNSRESPONDER
//
// 'dns_sd_hostname_callback()' - Track changes to the mDNS hostname...
@@ -1207,23 +1628,22 @@ dns_sd_geo_to_loc(const char *geo, // I - "geo:" URI
static void DNSSD_API
dns_sd_hostname_callback(
- DNSServiceRef ref, // I - Service reference (unused)
- DNSServiceFlags flags, // I - Flags (unused)
- uint32_t if_index, // I - Interface index (unused)
- DNSServiceErrorType error, // I - Error code, if any
- const char *fullname, // I - Search name (unused)
- uint16_t rrtype, // I - Record type (unused)
- uint16_t rrclass, // I - Record class (unused)
- uint16_t rdlen, // I - Record data length
- const void *rdata, // I - Record data
- uint32_t ttl, // I - Time-to-live (unused)
- void *context) // I - Context (unused)
+ DNSServiceRef ref, // I - Service reference (unused)
+ DNSServiceFlags flags, // I - Flags (unused)
+ uint32_t if_index, // I - Interface index (unused)
+ DNSServiceErrorType error, // I - Error code, if any
+ const char *fullname, // I - Search name (unused)
+ uint16_t rrtype, // I - Record type (unused)
+ uint16_t rrclass, // I - Record class (unused)
+ uint16_t rdlen, // I - Record data length
+ const void *rdata, // I - Record data
+ uint32_t ttl, // I - Time-to-live (unused)
+ void *context) // I - Context (unused)
{
- uint8_t *rdataptr, // Pointer into record data
- lablen; // Length of current label
- char temp[1024], // Temporary hostname string
- *tempptr; // Pointer into temporary string
-
+ uint8_t *rdataptr, // Pointer into record data
+ lablen; // Length of current label
+ char temp[1024], // Temporary hostname string
+ *tempptr; // Pointer into temporary string
(void)ref;
(void)flags;
@@ -1242,7 +1662,7 @@ dns_sd_hostname_callback(
for (rdataptr = (uint8_t *)rdata, tempptr = temp; rdlen > 0 && tempptr < (temp + sizeof(temp) - 2); rdlen -= lablen, rdataptr += lablen)
{
lablen = *rdataptr++;
- rdlen --;
+ rdlen--;
if (!rdlen || rdlen < lablen)
break;
@@ -1268,25 +1688,24 @@ dns_sd_hostname_callback(
if (strcmp(temp, pappl_dns_sd_hostname))
{
papplCopyString(pappl_dns_sd_hostname, temp, sizeof(pappl_dns_sd_hostname));
- pappl_dns_sd_hostname_changes ++;
+ pappl_dns_sd_hostname_changes++;
}
pthread_mutex_unlock(&pappl_dns_sd_hostname_mutex);
}
-
//
// 'dns_sd_printer_callback()' - Handle DNS-SD printer registration events.
//
static void DNSSD_API
dns_sd_printer_callback(
- DNSServiceRef sdRef, // I - Service reference
- DNSServiceFlags flags, // I - Status flags
- DNSServiceErrorType errorCode, // I - Error, if any
- const char *name, // I - Service name
- const char *regtype, // I - Service type
- const char *domain, // I - Domain for service
- pappl_printer_t *printer) // I - Printer
+ DNSServiceRef sdRef, // I - Service reference
+ DNSServiceFlags flags, // I - Status flags
+ DNSServiceErrorType errorCode, // I - Error, if any
+ const char *name, // I - Service name
+ const char *regtype, // I - Service type
+ const char *domain, // I - Domain for service
+ pappl_printer_t *printer) // I - Printer
{
(void)name;
(void)sdRef;
@@ -1298,7 +1717,7 @@ dns_sd_printer_callback(
_papplRWLockWrite(printer->system);
_papplRWLockWrite(printer);
- printer->dns_sd_collision = true;
+ printer->dns_sd_collision = true;
printer->system->dns_sd_any_collision = true;
_papplRWUnlock(printer);
@@ -1311,31 +1730,66 @@ dns_sd_printer_callback(
}
}
+/
+ // 'dns_sd_scanner_callback()' - Handle DNS-SD scanner registration events.
+ //
+
+ static void DNSSD_API
+ dns_sd_scanner_callback(
+ DNSServiceRef sdRef, // I - Service reference
+ DNSServiceFlags flags, // I - Status flags
+ DNSServiceErrorType errorCode, // I - Error, if any
+ const char *name, // I - Service name
+ const char *regtype, // I - Service type
+ const char *domain, // I - Domain for service
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ (void)name;
+ (void)sdRef;
+ (void)flags;
+ (void)domain;
+
+ if (errorCode == kDNSServiceErr_NameConflict)
+ {
+ _papplRWLockWrite(scanner->system);
+ _papplRWLockWrite(scanner);
+
+ scanner->dns_sd_collision = true;
+ scanner->system->dns_sd_any_collision = true;
+
+ _papplRWUnlock(scanner);
+ _papplRWUnlock(scanner->system);
+ }
+ else if (errorCode)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "DNSServiceRegister for '%s' failed with error %d (%s).", regtype, (int)errorCode, _papplDNSSDStrError(errorCode));
+ return;
+ }
+}
//
// 'dns_sd_run()' - Handle DNS-SD traffic.
//
-static void * // O - Exit status
-dns_sd_run(void *data) // I - System object
+static void * // O - Exit status
+dns_sd_run(void *data) // I - System object
{
- int err; // Status
+ int err; // Status
pappl_system_t *system = (pappl_system_t *)data;
- // System object
- struct pollfd pfd; // Poll data
-
+ // System object
+ struct pollfd pfd; // Poll data
pfd.events = POLLIN | POLLERR;
- pfd.fd = DNSServiceRefSockFD(pappl_dns_sd_master);
+ pfd.fd = DNSServiceRefSockFD(pappl_dns_sd_master);
for (;;)
{
// Wait up to 1 second for new data...
-# if _WIN32
+#if _WIN32
if (poll(&pfd, 1, 1000) < 0 && WSAGetLastError() == WSAEINTR)
-# else
+#else
if (poll(&pfd, 1, 1000) < 0 && errno != EINTR && errno != EAGAIN)
-# endif // _WIN32
+#endif // _WIN32
{
papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNS-SD poll failed: %s", strerror(errno));
break;
@@ -1346,8 +1800,8 @@ dns_sd_run(void *data) // I - System object
// Read DNS-SD data...
if ((err = DNSServiceProcessResult(pappl_dns_sd_master)) != kDNSServiceErr_NoError)
{
- papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNSServiceProcessResult returned %d (%s).", err, _papplDNSSDStrError(err));
- break;
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "DNSServiceProcessResult returned %d (%s).", err, _papplDNSSDStrError(err));
+ break;
}
}
else if (pfd.revents)
@@ -1360,20 +1814,19 @@ dns_sd_run(void *data) // I - System object
return (NULL);
}
-
//
// 'dns_sd_system_callback()' - Handle DNS-SD system registration events.
//
static void DNSSD_API
dns_sd_system_callback(
- DNSServiceRef sdRef, // I - Service reference
- DNSServiceFlags flags, // I - Status flags
- DNSServiceErrorType errorCode, // I - Error, if any
- const char *name, // I - Service name
- const char *regtype, // I - Service type
- const char *domain, // I - Domain for service
- pappl_system_t *system) // I - System
+ DNSServiceRef sdRef, // I - Service reference
+ DNSServiceFlags flags, // I - Status flags
+ DNSServiceErrorType errorCode, // I - Error, if any
+ const char *name, // I - Service name
+ const char *regtype, // I - Service type
+ const char *domain, // I - Domain for service
+ pappl_system_t *system) // I - System
{
(void)sdRef;
(void)flags;
@@ -1383,7 +1836,7 @@ dns_sd_system_callback(
if (errorCode == kDNSServiceErr_NameConflict)
{
_papplRWLockWrite(system);
- system->dns_sd_collision = true;
+ system->dns_sd_collision = true;
system->dns_sd_any_collision = true;
_papplRWUnlock(system);
}
@@ -1394,7 +1847,6 @@ dns_sd_system_callback(
}
}
-
#elif defined(HAVE_AVAHI)
//
//'dns_sd_client_cb()' - Client callback for Avahi.
@@ -1404,13 +1856,12 @@ dns_sd_system_callback(
static void
dns_sd_client_cb(
- AvahiClient *c, // I - Client
- AvahiClientState state, // I - Current state
- void *data) // I - Callback data (unused)
+ AvahiClient *c, // I - Client
+ AvahiClientState state, // I - Current state
+ void *data) // I - Callback data (unused)
{
(void)data;
-
if (!c)
return;
@@ -1424,21 +1875,20 @@ dns_sd_client_cb(
else if (state == AVAHI_CLIENT_S_RUNNING)
{
pthread_mutex_lock(&pappl_dns_sd_hostname_mutex);
- pappl_dns_sd_hostname_changes ++;
+ pappl_dns_sd_hostname_changes++;
pthread_mutex_unlock(&pappl_dns_sd_hostname_mutex);
}
}
-
//
// 'dns_sd_printer_callback()' - Handle DNS-SD printer registration events.
//
static void
dns_sd_printer_callback(
- AvahiEntryGroup *srv, // I - Service
- AvahiEntryGroupState state, // I - Registration state
- pappl_printer_t *printer) // I - Printer
+ AvahiEntryGroup *srv, // I - Service
+ AvahiEntryGroupState state, // I - Registration state
+ pappl_printer_t *printer) // I - Printer
{
(void)srv;
@@ -1446,13 +1896,35 @@ dns_sd_printer_callback(
{
_papplRWLockWrite(printer->system);
_papplRWLockWrite(printer);
- printer->dns_sd_collision = true;
+ printer->dns_sd_collision = true;
printer->system->dns_sd_any_collision = true;
_papplRWUnlock(printer);
_papplRWUnlock(printer->system);
}
}
+//
+// 'dns_sd_scanner_callback()' - Handle DNS-SD scanner registration events.
+//
+
+static void
+dns_sd_scanner_callback(
+ AvahiEntryGroup *srv, // I - Service
+ AvahiEntryGroupState state, // I - Registration state
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ (void)srv;
+
+ if (state == AVAHI_ENTRY_GROUP_COLLISION)
+ {
+ _papplRWLockWrite(scanner->system);
+ _papplRWLockWrite(scanner);
+ scanner->dns_sd_collision = true;
+ scanner->system->dns_sd_any_collision = true;
+ _papplRWUnlock(scanner);
+ _papplRWUnlock(scanner->system);
+ }
+}
//
// 'dns_sd_system_callback()' - Handle DNS-SD system registration events.
@@ -1460,16 +1932,16 @@ dns_sd_printer_callback(
static void
dns_sd_system_callback(
- AvahiEntryGroup *srv, // I - Service
- AvahiEntryGroupState state, // I - Registration state
- pappl_system_t *system) // I - System
+ AvahiEntryGroup *srv, // I - Service
+ AvahiEntryGroupState state, // I - Registration state
+ pappl_system_t *system) // I - System
{
(void)srv;
if (state == AVAHI_ENTRY_GROUP_COLLISION)
{
_papplRWLockWrite(system);
- system->dns_sd_collision = true;
+ system->dns_sd_collision = true;
system->dns_sd_any_collision = true;
_papplRWUnlock(system);
}
diff --git a/pappl/esclops.c b/pappl/esclops.c
new file mode 100644
index 00000000..aa424b47
--- /dev/null
+++ b/pappl/esclops.c
@@ -0,0 +1,176 @@
+//
+// Scan eSCL functions for the Printer Application Framework
+//
+// Copyright © 2020-2024 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
+
+#include "pappl-private.h"
+#include
+#include
+#include
+#include
+#include
+// #include "esclops.h"
+
+// Function to read XML content from a file
+char *
+readXmlContent(
+ const char *filePath) // I - Path to the XML file
+{
+ char *xmlContent = NULL;
+ long length;
+ FILE *file = fopen(filePath, "rb");
+ if (file)
+ {
+ fseek(file, 0, SEEK_END);
+ length = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ xmlContent = (char *)malloc(length + 1);
+ if (xmlContent)
+ {
+ fread(xmlContent, 1, length, file);
+ xmlContent[length] = '\0';
+ }
+ fclose(file);
+ }
+ return xmlContent;
+}
+
+typedef struct ScanSettingsXml
+{
+ char *xml;
+
+} ScanSettingsXml; // Structure for XML settings of a scan
+
+// Function to initialize scan settings with provided XML string
+void initScanSettingsXml(
+ ScanSettingsXml *settings, // I - Pointer to the ScanSettingsXml structure to be initialized
+ const char *s) // I- XML string used for initialization
+{
+ settings->xml = (char *)malloc(strlen(s) + 1);
+ strcpy(settings->xml, s);
+}
+
+// Function to extract string using regular expression from XML settings
+char *getString(
+ const ScanSettingsXml *settings, // I - Pointer to the ScanSettingsXml structure
+ const char *name, // I - Name of the XML element to be extracted
+ const char *pattern) // I - Regular expression pattern used for extraction
+{
+ const int max_matches = 2;
+ regex_t regex;
+ regmatch_t matches[max_matches];
+
+ if (regcomp(®ex, pattern, REG_EXTENDED) != 0)
+ {
+ fprintf(stderr, "Could not compile regex\n");
+ exit(1);
+ }
+
+ if (regexec(®ex, settings->xml, max_matches, matches, 0) == 0)
+ {
+ size_t match_length = matches[1].rm_eo - matches[1].rm_so;
+ char *result = (char *)malloc(match_length + 1);
+ strncpy(result, settings->xml + matches[1].rm_so, match_length);
+ result[match_length] = '\0';
+
+ regfree(®ex);
+ return result;
+ }
+ regfree(®ex);
+ char *empty = (char *)malloc(1);
+ empty[0] = '\0';
+ return empty;
+}
+
+double getNumber(
+ const ScanSettingsXml *settings, // I - Pointer to the ScanSettingsXml structure
+ const char *name, // I - Name of the XML element to be extracted
+ const char *pattern) // I - Regular expression pattern used for extraction
+{
+ char *string_value = getString(settings, name, pattern);
+ double result = strtod(string_value, NULL);
+ free(string_value);
+ return result;
+}
+
+// Function to check if a client has already initiated air scan
+bool ClientAlreadyAirScan(
+ pappl_client_t *client) // I - Pointer to the pappl_client_t structure
+{
+ const char *airscan_string = "AirScanScanner";
+ size_t airscan_length = strlen(airscan_string);
+
+ const char *user_agent = httpGetField(client->http, HTTP_FIELD_USER_AGENT);
+ const char *result = strstr(user_agent, airscan_string);
+
+ if (result != NULL)
+ {
+ size_t substring_length = strlen(result);
+ if (substring_length > airscan_length)
+ {
+ char next_char = *(result + airscan_length);
+ if (next_char != ' ' && next_char != '\t' && next_char != '\r' && next_char != '\n')
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+// Function to parse XML string and set up scan settings
+void *ScanSettingsFromXML(
+ const char *xmlString, // I - XML string containing scan settings
+ pappl_client_t *client) // I - Pointer to the pappl_client_t structure
+{
+ ScanSettingsXml scanSettings;
+ initScanSettingsXml(&scanSettings, xmlString);
+
+ char *versionPattern = "([^<]*) ";
+ char *version = getString(&scanSettings, "Version", versionPattern);
+
+ char *intentPattern = "([^<]*) ";
+ char *intent = getString(&scanSettings, "Intent", intentPattern);
+
+ char *heightPattern = "([^<]*) ";
+ char *height = getString(&scanSettings, "Height", heightPattern);
+
+ char *contentRegionUnitsPattern = "([^<]*) ";
+ char *contentRegionUnits = getString(&scanSettings, "ContentRegionUnits", contentRegionUnitsPattern);
+
+ char *widthPattern = "([^<]*) ";
+ double width = getNumber(&scanSettings, "Width", widthPattern);
+
+ char *xOffsetPattern = "([^<]*) ";
+ double xOffset = getNumber(&scanSettings, "XOffset", xOffsetPattern);
+
+ char *yOffsetPattern = "([^<]*) ";
+ double yOffset = getNumber(&scanSettings, "YOffset", yOffsetPattern);
+
+ char *inputSourcePattern = "([^<]*) ";
+ char *inputSource = getString(&scanSettings, "InputSource", inputSourcePattern);
+
+ char *colorModePattern = "([^<]*) ";
+ char *colorMode = getString(&scanSettings, "ColorMode", colorModePattern);
+
+ char *blankPageDetectionPattern = "([^<]*) ";
+ char *blankPageDetection = getString(&scanSettings, "BlankPageDetection", blankPageDetectionPattern);
+
+ free(version);
+ free(intent);
+ free(height);
+ free(contentRegionUnits);
+ free(inputSource);
+ free(colorMode);
+ free(blankPageDetection);
+ free(scanSettings.xml);
+}
diff --git a/pappl/job-private.h b/pappl/job-private.h
index 7ad6cb63..08930f41 100644
--- a/pappl/job-private.h
+++ b/pappl/job-private.h
@@ -24,6 +24,7 @@ struct _pappl_job_s // Job data
pthread_rwlock_t rwlock; // Reader/writer lock
pappl_system_t *system; // Containing system
pappl_printer_t *printer; // Containing printer
+ pappl_scanner_t *scanner; // Containing scanner
int job_id; // "job-id" value
const char *name, // "job-name" value
*username, // "job-originating-user-name" value
@@ -61,6 +62,7 @@ extern void _papplJobCopyAttributesNoLock(pappl_job_t *job, pappl_client_t *cli
extern void _papplJobCopyDocumentData(pappl_client_t *client, pappl_job_t *job) _PAPPL_PRIVATE;
extern void _papplJobCopyStateNoLock(pappl_job_t *job, ipp_tag_t group_tag, ipp_t *ipp, cups_array_t *ra) _PAPPL_PRIVATE;
extern pappl_job_t *_papplJobCreate(pappl_printer_t *printer, int job_id, const char *username, const char *format, const char *job_name, ipp_t *attrs) _PAPPL_PRIVATE;
+extern pappl_job_t *_papplScanJobCreate(pappl_scanner_t *scanner, int job_id, const char *username, const char *format, const char *job_name) _PAPPL_PRIVATE; // no extra attributes required
extern void _papplJobDelete(pappl_job_t *job) _PAPPL_PRIVATE;
# ifdef HAVE_LIBJPEG
extern bool _papplJobFilterJPEG(pappl_job_t *job, pappl_device_t *device, void *data);
@@ -71,6 +73,9 @@ extern bool _papplJobFilterPNG(pappl_job_t *job, pappl_device_t *device, void *
extern bool _papplJobHoldNoLock(pappl_job_t *job, const char *username, const char *until, time_t until_time) _PAPPL_PRIVATE;
extern void *_papplJobProcess(pappl_job_t *job) _PAPPL_PRIVATE;
extern void _papplJobProcessIPP(pappl_client_t *client) _PAPPL_PRIVATE;
+// TODO
+extern void _papplJobProcessESCL(pappl_client_t *client) _PAPPL_PRIVATE;
+
extern void _papplJobProcessRaster(pappl_job_t *job, pappl_client_t *client) _PAPPL_PRIVATE;
extern const char *_papplJobReasonString(pappl_jreason_t reason) _PAPPL_PRIVATE;
extern void _papplJobReleaseNoLock(pappl_job_t *job, const char *username) _PAPPL_PRIVATE;
diff --git a/pappl/job-process.c b/pappl/job-process.c
index da23a51f..11f4f63e 100644
--- a/pappl/job-process.c
+++ b/pappl/job-process.c
@@ -1076,7 +1076,7 @@ finish_job(pappl_job_t *job) // I - Job
if (job->state >= IPP_JSTATE_CANCELED && !printer->max_preserved_jobs && !job->retain_until)
_papplJobRemoveFile(job);
- _papplSystemAddEventNoLock(job->system, job->printer, job, PAPPL_EVENT_JOB_COMPLETED, NULL);
+ _papplSystemAddEventNoLock(job->system, job->printer, NULL, job, PAPPL_EVENT_JOB_COMPLETED, NULL);
if (printer->is_stopped)
{
@@ -1102,7 +1102,7 @@ finish_job(pappl_job_t *job) // I - Job
_papplRWUnlock(job);
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, NULL);
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, NULL);
if (printer->max_preserved_jobs > 0)
_papplPrinterCleanJobsNoLock(printer);
@@ -1166,7 +1166,7 @@ start_job(pappl_job_t *job) // I - Job
job->processing = time(NULL);
printer->processing_job = job;
- _papplSystemAddEventNoLock(printer->system, printer, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
_papplRWUnlock(job);
@@ -1216,7 +1216,7 @@ start_job(pappl_job_t *job) // I - Job
job->state = IPP_JSTATE_PENDING;
_papplRWLockRead(job);
- _papplSystemAddEventNoLock(job->system, job->printer, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
+ _papplSystemAddEventNoLock(job->system, job->printer, NULL, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
_papplRWUnlock(job);
if (printer->device)
@@ -1234,7 +1234,7 @@ start_job(pappl_job_t *job) // I - Job
ret = true;
}
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, NULL);
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, NULL);
_papplRWUnlock(printer);
diff --git a/pappl/job-scan.c b/pappl/job-scan.c
new file mode 100644
index 00000000..30b7bcfa
--- /dev/null
+++ b/pappl/job-scan.c
@@ -0,0 +1,603 @@
+//
+// Scan Job functions for the Scanner Application Framework
+//
+// Copyright © 2020-2024 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "pappl-private.h"
+
+// Internal Helper Functions
+//
+// Helper functions for scanner option handling
+//
+
+//
+// '_papplValidateDocumentFormat()' - Validate document format against supported formats.
+//
+static bool // O - TRUE if valid, FALSE if not
+_papplValidateDocumentFormat(
+ const char *format, // I - Format to validate
+ const char **supported, // I - Array of supported formats
+ size_t num_supported) // I - Number of supported formats
+{
+ size_t i;
+
+ if (!format || !supported || num_supported == 0)
+ return (false);
+
+ for (i = 0; i < num_supported && supported[i]; i++)
+ {
+ if (!strcmp(format, supported[i]))
+ return (true);
+ }
+
+ return (false);
+}
+
+//
+// '_papplValidateScanResolution()' - Validate scan resolution against supported values.
+//
+static bool // O - TRUE if valid, FALSE if not
+_papplValidateScanResolution(
+ int resolution, // I - Resolution to validate
+ const int *supported, // I - Array of supported resolutions
+ size_t num_supported) // I - Number of supported resolutions
+{
+ size_t i; // Looping var
+
+ if (resolution <= 0 || !supported || num_supported == 0)
+ return (false);
+
+ for (i = 0; i < num_supported && supported[i] > 0; i++)
+ {
+ if (resolution == supported[i])
+ return (true);
+ }
+
+ return (false);
+}
+
+//
+// '_papplValidateScanRegion()' - Validate scan region against supported dimensions.
+//
+static bool // O - TRUE if valid, FALSE if not
+_papplValidateScanRegion(
+ int x, // I - X offset
+ int y, // I - Y offset
+ int w, // I - Width
+ int h, // I - Height
+ const int supported[4]) // I - Supported region values [x,y,w,h]
+{
+ if (!supported)
+ return (false);
+
+ return (x >= supported[0] && y >= supported[1] &&
+ w <= supported[2] && h <= supported[3] &&
+ w > 0 && h > 0);
+}
+
+// End of helper functions
+
+
+
+
+//
+// 'papplJobGetScanner()' - Get the scanner for the job.
+//
+// This function returns the scanner containing the job.
+//
+
+pappl_scanner_t * // O - Scanner
+papplJobGetScanner(pappl_job_t *job) // I - Job
+{
+ pappl_scanner_t *ret = NULL; // Return value
+
+ if (job)
+ {
+ _papplRWLockRead(job);
+ ret = job->scanner;
+ _papplRWUnlock(job);
+ }
+
+ return (ret);
+}
+
+//
+// 'papplJobDeleteScanOptions()' - Delete a job options structure.
+//
+// This function frees the memory used for a job options structure.
+//
+
+void papplJobDeleteScanOptions(
+ pappl_sc_options_t *options) // I - Scan options
+{
+ if (options)
+ {
+ free(options);
+ }
+}
+
+
+//
+// 'papplJobCreateScanOptions()' - Create the scanner options for a job.
+//
+// This function allocates a scanner options structure and computes the scan
+// options for a job based upon the job configuration and default values
+// set in the scanner driver data.
+//
+
+pappl_sc_options_t * // O - Job options data or `NULL` on error
+papplJobCreateScanOptions(
+ pappl_job_t *job) // I - Job
+{
+ pappl_sc_options_t *options; // New options data
+ pappl_scanner_t *scanner; // Scanner
+ int i; // Looping var
+ bool mode_supported, // Is color mode supported?
+ source_supported, // Is source supported?
+ intent_supported; // Is intent supported?
+
+ if (!job)
+ return (NULL);
+
+ scanner = (pappl_scanner_t *)job->printer;
+ if (!scanner)
+ return (NULL);
+
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Getting scan options for job");
+
+ // Clear all options...
+ if ((options = calloc(1, sizeof(pappl_sc_options_t))) == NULL)
+ return (NULL);
+
+ _papplRWLockRead(scanner);
+
+ // Document format - Validate against supported formats
+ if (_papplValidateDocumentFormat(scanner->driver_data.default_document_format,
+ scanner->driver_data.document_formats_supported,
+ PAPPL_MAX_FORMATS))
+ {
+ papplCopyString(options->document_format,
+ scanner->driver_data.default_document_format,
+ sizeof(options->document_format));
+ }
+ else if (scanner->driver_data.document_formats_supported[0])
+ {
+ // Default to first supported format if default is invalid
+ papplCopyString(options->document_format,
+ scanner->driver_data.document_formats_supported[0],
+ sizeof(options->document_format));
+ }
+ else
+ {
+ // Fallback to a safe default
+ papplCopyString(options->document_format, "application/pdf", sizeof(options->document_format));
+ }
+
+ // Color mode - Validate and set
+ mode_supported = false;
+ for (i = 0; i < PAPPL_MAX_COLOR_MODES; i++)
+ {
+ if (scanner->driver_data.color_modes_supported[i] == scanner->driver_data.default_color_mode)
+ {
+ mode_supported = true;
+ break;
+ }
+ }
+ options->color_mode = mode_supported ? scanner->driver_data.default_color_mode : PAPPL_BLACKANDWHITE1;
+
+ // Resolution - Validate against supported resolutions
+ if (_papplValidateScanResolution(scanner->driver_data.default_resolution,
+ scanner->driver_data.resolutions,
+ MAX_RESOLUTIONS))
+ {
+ options->resolution = scanner->driver_data.default_resolution;
+ }
+ else if (scanner->driver_data.resolutions[0] > 0)
+ {
+ options->resolution = scanner->driver_data.resolutions[0];
+ }
+ else
+ {
+ options->resolution = 300; // Safe default
+ }
+
+ // Input source - Validate against supported sources
+ source_supported = false;
+ for (i = 0; i < PAPPL_MAX_SOURCES; i++)
+ {
+ if (scanner->driver_data.input_sources_supported[i] == scanner->driver_data.default_input_source)
+ {
+ source_supported = true;
+ break;
+ }
+ }
+ options->input_source = source_supported ? scanner->driver_data.default_input_source : PAPPL_FLATBED;
+
+ // Duplex - Only enable if supported
+ options->duplex = scanner->driver_data.duplex_supported ? false : false;
+
+ // Scan intent - Validate against supported intents
+ intent_supported = false;
+ for (i = 0; scanner->driver_data.mandatory_intents[i] && i < 5; i++)
+ {
+ if (!strcmp(scanner->driver_data.default_intent, scanner->driver_data.mandatory_intents[i]))
+ {
+ intent_supported = true;
+ break;
+ }
+ }
+ if (intent_supported)
+ {
+ papplCopyString(options->intent,
+ scanner->driver_data.default_intent,
+ sizeof(options->intent));
+ }
+ else if (scanner->driver_data.mandatory_intents[0])
+ {
+ papplCopyString(options->intent,
+ scanner->driver_data.mandatory_intents[0],
+ sizeof(options->intent));
+ }
+ else
+ {
+ papplCopyString(options->intent, "document", sizeof(options->intent));
+ }
+
+ // Scan area - Validate against supported dimensions
+ if (_papplValidateScanRegion(0, 0,
+ scanner->driver_data.default_scan_area[0],
+ scanner->driver_data.default_scan_area[1],
+ scanner->driver_data.scan_region_supported))
+ {
+ options->scan_area.width = scanner->driver_data.default_scan_area[0];
+ options->scan_area.height = scanner->driver_data.default_scan_area[1];
+ options->scan_area.x_offset = 0;
+ options->scan_area.y_offset = 0;
+ }
+ else
+ {
+ // Use maximum supported dimensions if defaults are invalid
+ options->scan_area.width = scanner->driver_data.scan_region_supported[2];
+ options->scan_area.height = scanner->driver_data.scan_region_supported[3];
+ options->scan_area.x_offset = scanner->driver_data.scan_region_supported[0];
+ options->scan_area.y_offset = scanner->driver_data.scan_region_supported[1];
+ }
+
+ // Image adjustments
+ options->adjustments.brightness = scanner->driver_data.adjustments.brightness;
+ options->adjustments.contrast = scanner->driver_data.adjustments.contrast;
+ options->adjustments.gamma = scanner->driver_data.adjustments.gamma;
+ options->adjustments.threshold = scanner->driver_data.adjustments.threshold;
+ options->adjustments.saturation = scanner->driver_data.adjustments.saturation;
+ options->adjustments.sharpness = scanner->driver_data.adjustments.sharpness;
+
+ // Processing options - Set based on capability
+ options->blank_page_removal = false;
+ options->compression_factor = 0;
+ options->noise_removal = false;
+ options->sharpening = false;
+
+ // Number of pages - Based on input source
+ options->num_pages = (options->input_source == PAPPL_ADF) ? 0 : 1;
+
+ // Log all options
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "document-format='%s'", options->document_format);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "color-mode=%d", options->color_mode);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "resolution=%ddpi", options->resolution);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "input-source=%d", options->input_source);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "duplex=%s", options->duplex ? "true" : "false");
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "intent='%s'", options->intent);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "scan-area=[%d,%d,%d,%d]",
+ options->scan_area.x_offset, options->scan_area.y_offset,
+ options->scan_area.width, options->scan_area.height);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.brightness=%d", options->adjustments.brightness);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.contrast=%d", options->adjustments.contrast);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.gamma=%d", options->adjustments.gamma);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.threshold=%d", options->adjustments.threshold);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.saturation=%d", options->adjustments.saturation);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "adjustments.sharpness=%d", options->adjustments.sharpness);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "blank-page-removal=%s", options->blank_page_removal ? "true" : "false");
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "compression-factor=%d", options->compression_factor);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "noise-removal=%s", options->noise_removal ? "true" : "false");
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "sharpening=%s", options->sharpening ? "true" : "false");
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "num-pages=%u", options->num_pages);
+
+ _papplRWUnlock(scanner);
+
+ return (options);
+}
+
+//
+// 'papplScanJobCreate()' - Create a new/existing scan job object.
+//
+
+pappl_job_t * // O - Job
+papplScanJobCreate(
+ pappl_scanner_t *scanner, // I - Scanner
+ int job_id, // I - Job ID or `0` for new job
+ const char *username, // I - Username
+ const char *format, // I - Document format
+ const char *job_name) // I - Job name
+{
+ pappl_job_t *job; // Job
+ char job_uri[1024]; // job-uri value
+ char job_uuid[64]; // job-uuid value
+
+ if (!scanner || !username || !job_name)
+ return (NULL);
+
+ // Check if scanner is accepting jobs
+ _papplRWLockWrite(scanner);
+
+ if (!scanner->is_accepting)
+ {
+ _papplRWUnlock(scanner);
+ return (NULL);
+ }
+
+ // Allocate and initialize the job object
+ if ((job = calloc(1, sizeof(pappl_job_t))) == NULL)
+ {
+ papplLog(scanner->system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for job: %s", strerror(errno));
+ _papplRWUnlock(scanner);
+ return (NULL);
+ }
+
+ pthread_rwlock_init(&job->rwlock, NULL);
+
+ // Initialize basic job properties
+ job->system = scanner->system;
+ job->format = format ? format : scanner->driver_data.default_document_format;
+ job->name = job_name;
+ job->username = username;
+ job->state = ESCL_SSTATE_IDLE;
+ job->created = time(NULL);
+ job->fd = -1;
+
+ // Set job ID
+ job->job_id = job_id > 0 ? job_id : scanner->next_job_id++;
+
+ // Create HTTPS URI for eSCL
+ httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri),
+ "https", NULL, scanner->system->hostname, scanner->system->port,
+ "%s/ScanJobs/%d", scanner->resource, job->job_id);
+
+ // Create job UUID
+ _papplSystemMakeUUID(scanner->system, scanner->name, job->job_id, job_uuid,
+ sizeof(job_uuid));
+
+ // Set job times
+ job->processing = 0;
+ job->completed = 0;
+
+
+ // Add event and update system configuration
+ papplSystemAddEvent(scanner->system, NULL, job, PAPPL_EVENT_JOB_CREATED, NULL);
+ _papplSystemConfigChanged(scanner->system);
+
+ _papplRWUnlock(scanner);
+
+ papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Created scan job %d.", job->job_id);
+
+ return (job);
+}
+
+static bool // O - `true` on success, `false` otherwise
+start_job(pappl_job_t *job) // I - Job
+{
+ bool ret = false; // Return value
+ pappl_scanner_t *scanner = job->scanner;
+ // Scanner
+ bool first_open = true; // Is this the first time we try to open the device?
+
+ // Move the job to the 'processing' state...
+ _papplRWLockWrite(scanner);
+ _papplRWLockWrite(job);
+
+ papplLogJob(job, PAPPL_LOGLEVEL_INFO, "Starting scan job.");
+
+ job->state = IPP_JSTATE_PROCESSING;
+ job->processing = time(NULL);
+ scanner->processing_job = job;
+
+ // First event call is correct
+ _papplSystemAddEventNoLock(scanner->system, NULL, scanner, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
+
+ _papplRWUnlock(job);
+
+ // Open the output device...
+ if (scanner->device_in_use)
+ {
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Waiting for device to become available.");
+
+ while (scanner->device_in_use && !scanner->is_deleted && !job->is_canceled && papplSystemIsRunning(scanner->system))
+ {
+ _papplRWUnlock(scanner);
+ sleep(1);
+ _papplRWLockWrite(scanner);
+ }
+ }
+
+ while (!scanner->device && !scanner->is_deleted && !job->is_canceled && papplSystemIsRunning(scanner->system))
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_DEBUG, "Opening device for job %d.", job->job_id);
+
+ scanner->device = papplDeviceOpen(scanner->device_uri, job->name, papplLogDevice, job->system);
+
+ if (!scanner->device && !scanner->is_deleted && !job->is_canceled)
+ {
+ // Log that the printer is unavailable then sleep for 5 seconds to retry.
+ if (first_open)
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_ERROR, "Unable to open device '%s', pausing until scanner becomes available.", scanner->device_uri);
+ first_open = false;
+
+ scanner->state = ESCL_SSTATE_STOPPED;
+ scanner->state_time = time(NULL);
+ }
+ else
+ {
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_DEBUG, "Still unable to open device.");
+ }
+
+ _papplRWUnlock(scanner);
+ sleep(5);
+ _papplRWLockWrite(scanner);
+ }
+ }
+
+ if (!papplSystemIsRunning(scanner->system))
+ {
+ job->state = IPP_JSTATE_PENDING;
+
+ _papplRWLockRead(job);
+ // Corrected event call: pass `job` as the fourth argument and event as the fifth
+ _papplSystemAddEventNoLock(job->system, NULL, job->scanner, job, PAPPL_EVENT_JOB_STATE_CHANGED, NULL);
+ _papplRWUnlock(job);
+
+ if (scanner->device)
+ {
+ papplDeviceClose(scanner->device);
+ scanner->device = NULL;
+ }
+ }
+
+ if (scanner->device)
+ {
+ // Move the scanner to the 'processing' state...
+ scanner->state = ESCL_SSTATE_PROCESSING;
+ scanner->state_time = time(NULL);
+ ret = true;
+ }
+
+ _papplSystemAddEventNoLock(scanner->system, NULL, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, NULL);
+
+ _papplRWUnlock(scanner);
+
+ return (ret);
+}
+
+static void
+finish_job(pappl_job_t *job) // I - Job
+{
+ pappl_scanner_t *scanner = job->scanner;
+ // Scanner
+ static const char *const job_states[] =
+ {
+ "Pending",
+ "Held",
+ "Processing",
+ "Canceled",
+ "Aborted",
+ "Completed"};
+
+ _papplRWLockWrite(scanner);
+ _papplRWLockWrite(job);
+
+ if (job->is_canceled)
+ job->state = IPP_JSTATE_CANCELED;
+ else if (job->state == IPP_JSTATE_PROCESSING)
+ job->state = IPP_JSTATE_COMPLETED;
+
+ // Ensure job->state is within the bounds of job_states array
+ if ((job->state - IPP_JSTATE_PENDING) >= 0 &&
+ (job->state - IPP_JSTATE_PENDING) < (sizeof(job_states) / sizeof(job_states[0])))
+ {
+ papplLogJob(job, PAPPL_LOGLEVEL_INFO, "%s, job-impressions-completed=%d.",
+ job_states[job->state - IPP_JSTATE_PENDING],
+ job->impcompleted);
+ }
+ else
+ {
+ papplLogJob(job, PAPPL_LOGLEVEL_WARN, "Unknown job state: %d, job-impressions-completed=%d.",
+ job->state, job->impcompleted);
+ }
+
+ if (job->state >= IPP_JSTATE_CANCELED)
+ job->completed = time(NULL);
+
+ _papplJobSetRetain(job);
+
+ scanner->processing_job = NULL;
+
+
+ // Corrected Call: Ensure fifth argument is the event, and sixth is the message
+ _papplSystemAddEventNoLock(scanner->system, NULL, scanner, job, PAPPL_EVENT_JOB_COMPLETED, NULL);
+
+ if (scanner->is_stopped)
+ {
+ // New scanner-state is 'stopped'...
+ scanner->state = ESCL_SSTATE_STOPPED;
+ scanner->is_stopped = false;
+ }
+ else
+ {
+ // New scanner-state is 'idle'...
+ scanner->state = ESCL_SSTATE_IDLE;
+ }
+
+ scanner->state_time = time(NULL);
+
+ if (!job->system->clean_time)
+ job->system->clean_time = time(NULL) + 60;
+
+ _papplRWUnlock(job);
+
+ // Corrected Call: Ensure fifth argument is the event, and sixth is the message
+ _papplSystemAddEventNoLock(scanner->system, NULL, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, NULL);
+ // ^ Changed event type to PAPPL_EVENT_SCANNER_STATE_CHANGED
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+
+ if (papplScannerIsDeleted(scanner))
+ {
+ papplScannerDelete(scanner);
+ return;
+ }
+ else if (!strncmp(scanner->device_uri, "file:", 5))
+ {
+ pappl_devmetrics_t metrics; // Metrics for device IO
+
+ _papplRWLockWrite(scanner);
+
+ papplDeviceGetMetrics(scanner->device, &metrics);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Device read metrics: %lu requests, %lu bytes, %lu msecs",
+ (unsigned long)metrics.read_requests,
+ (unsigned long)metrics.read_bytes,
+ (unsigned long)metrics.read_msecs);
+ papplLogJob(job, PAPPL_LOGLEVEL_DEBUG, "Device write metrics: %lu requests, %lu bytes, %lu msecs",
+ (unsigned long)metrics.write_requests,
+ (unsigned long)metrics.write_bytes,
+ (unsigned long)metrics.write_msecs);
+
+ papplLogScanner(scanner, PAPPL_LOGLEVEL_DEBUG, "Closing device for job %d.", job->job_id);
+
+ papplDeviceClose(scanner->device);
+ scanner->device = NULL;
+
+ _papplRWUnlock(scanner);
+ }
+}
+
+//
+// '_papplScanJobProcess()' - Process a scan job.
+//
+
+void * // O - Thread exit status
+_papplScanJobProcess(pappl_job_t *job) // I - Job
+{
+
+ // Start processing the job...
+ start_job(job);
+
+ // Move the job to a completed state...
+ finish_job(job);
+
+ return (NULL);
+}
\ No newline at end of file
diff --git a/pappl/job.c b/pappl/job.c
index 463382f6..2dc12e54 100644
--- a/pappl/job.c
+++ b/pappl/job.c
@@ -408,7 +408,7 @@ _papplJobHoldNoLock(
if (username)
{
- _papplSystemAddEventNoLock(job->system, job->printer, job, PAPPL_EVENT_JOB_STATE_CHANGED, "Job held by '%s'.", username);
+ _papplSystemAddEventNoLock(job->system, job->printer, NULL, job, PAPPL_EVENT_JOB_STATE_CHANGED, "Job held by '%s'.", username);
}
return (true);
@@ -583,7 +583,7 @@ _papplJobReleaseNoLock(
ippDeleteAttribute(job->attrs, attr);
if (username)
- _papplSystemAddEventNoLock(job->system, job->printer, job, PAPPL_EVENT_JOB_STATE_CHANGED, "Job released by '%s'.", username);
+ _papplSystemAddEventNoLock(job->system, job->printer, NULL, job, PAPPL_EVENT_JOB_STATE_CHANGED, "Job released by '%s'.", username);
}
@@ -704,7 +704,7 @@ _papplJobRetainNoLock(
if (username)
{
- _papplSystemAddEventNoLock(job->system, job->printer, job, PAPPL_EVENT_JOB_CONFIG_CHANGED, "Job retain set by '%s'.", username);
+ _papplSystemAddEventNoLock(job->system, job->printer, NULL, job, PAPPL_EVENT_JOB_CONFIG_CHANGED, "Job retain set by '%s'.", username);
}
return (true);
diff --git a/pappl/job.h b/pappl/job.h
index a5bd5925..557aecd3 100644
--- a/pappl/job.h
+++ b/pappl/job.h
@@ -59,9 +59,11 @@ typedef unsigned int pappl_jreason_t; // Bitfield for IPP "job-state-reasons" va
extern void papplJobCancel(pappl_job_t *job) _PAPPL_PUBLIC;
extern pappl_pr_options_t *papplJobCreatePrintOptions(pappl_job_t *job, unsigned num_pages, bool color) _PAPPL_PUBLIC;
+extern pappl_sc_options_t *papplJobCreateScanOptions(pappl_job_t *job) _PAPPL_PUBLIC;
extern pappl_job_t *papplJobCreateWithFile(pappl_printer_t *printer, const char *username, const char *format, const char *job_name, int num_options, cups_option_t *options, const char *filename);
extern void papplJobDeletePrintOptions(pappl_pr_options_t *options);
+extern void papplJobDeleteScanOptions(pappl_sc_options_t *options);
extern bool papplJobFilterImage(pappl_job_t *job, pappl_device_t *device, pappl_pr_options_t *options, const unsigned char *pixels, int width, int height, int depth, int ppi, bool smoothing) _PAPPL_PUBLIC;
@@ -77,6 +79,7 @@ extern int papplJobGetImpressionsCompleted(pappl_job_t *job) _PAPPL_PUBLIC;
extern const char *papplJobGetMessage(pappl_job_t *job) _PAPPL_PUBLIC;
extern const char *papplJobGetName(pappl_job_t *job) _PAPPL_PUBLIC;
extern pappl_printer_t *papplJobGetPrinter(pappl_job_t *job) _PAPPL_PUBLIC;
+extern pappl_scanner_t *papplJobGetScanner(pappl_job_t *job) _PAPPL_PUBLIC;
extern pappl_jreason_t papplJobGetReasons(pappl_job_t *job) _PAPPL_PUBLIC;
extern ipp_jstate_t papplJobGetState(pappl_job_t *job) _PAPPL_PUBLIC;
extern time_t papplJobGetTimeCompleted(pappl_job_t *job) _PAPPL_PUBLIC;
diff --git a/pappl/log.c b/pappl/log.c
index 4b4e9a2a..0d1b869d 100644
--- a/pappl/log.c
+++ b/pappl/log.c
@@ -11,6 +11,7 @@
#include "job-private.h"
#include "log-private.h"
#include "printer-private.h"
+#include "scanner-private.h"
#include "system-private.h"
#include
#if !_WIN32
@@ -423,6 +424,75 @@ papplLogPrinter(
va_end(ap);
}
+// 'papplLogScanner()' - Log a message for a scanner.
+//
+// This function sends a scanner message to the system's log file. The "level"
+// argument specifies the urgency of the message:
+//
+// - `PAPPL_LOGLEVEL_DEBUG`: A debugging message.
+// - `PAPPL_LOGLEVEL_ERROR`: An error message.
+// - `PAPPL_LOGLEVEL_FATAL`: A fatal error message.
+// - `PAPPL_LOGLEVEL_INFO`: An informational message.
+// - `PAPPL_LOGLEVEL_WARN`: A warning message.
+//
+// The "message" argument specifies a `printf`-style format string. Values
+// logged using the "%c" and "%s" format specifiers are sanitized to not
+// contain control characters.
+//
+
+void
+papplLogScanner(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_loglevel_t level, // I - Log level
+ const char *message, // I - Printf-style message string
+ ...) // I - Additional arguments as needed
+{
+ char pmessage[1024], // Message with scanner prefix
+ *pptr, // Pointer into prefix
+ *nameptr; // Pointer into scanner name
+ va_list ap; // Pointer to arguments
+ pappl_system_t *system; // System
+
+
+ if (!scanner || !message)
+ return;
+
+ system = scanner->system;
+
+ if (level < papplSystemGetLogLevel(system))
+ return;
+
+ // Prefix the message with "[Printer foo]", making sure to not insert any
+ // printf format specifiers.
+ papplCopyString(pmessage, "[Scanner ", sizeof(pmessage));
+ for (pptr = pmessage + 9, nameptr = scanner->name; *nameptr && pptr < (pmessage + 200); pptr ++)
+ {
+ if (*nameptr == '%')
+ *pptr++ = '%';
+ *pptr = *nameptr++;
+ }
+ *pptr++ = ']';
+ *pptr++ = ' ';
+ papplCopyString(pptr, message, sizeof(pmessage) - (size_t)(pptr - pmessage));
+
+ // Write the log message...
+ va_start(ap, message);
+
+#if !_WIN32
+ if (system->log_is_syslog)
+ {
+ vsyslog(syslevels[level], pmessage, ap);
+ }
+ else
+#endif // !_WIN32
+ {
+ pthread_mutex_lock(&system->log_mutex);
+ write_log_no_lock(system, level, pmessage, ap);
+ pthread_mutex_unlock(&system->log_mutex);
+ }
+
+ va_end(ap);
+}
//
// 'rotate_log_no_lock()' - Rotate the log file...
diff --git a/pappl/log.h b/pappl/log.h
index d73de52b..3542bdb9 100644
--- a/pappl/log.h
+++ b/pappl/log.h
@@ -40,6 +40,7 @@ extern void papplLogClient(pappl_client_t *client, pappl_loglevel_t level, cons
extern void papplLogDevice(const char *message, void *data) _PAPPL_PUBLIC;
extern void papplLogJob(pappl_job_t *job, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4);
extern void papplLogPrinter(pappl_printer_t *printer, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4);
+extern void papplLogScanner(pappl_scanner_t *scanner, pappl_loglevel_t level, const char *message, ...) _PAPPL_PUBLIC _PAPPL_FORMAT(3, 4);
# ifdef __cplusplus
diff --git a/pappl/mainloop-private.h b/pappl/mainloop-private.h
index 414b7933..5f9d3263 100644
--- a/pappl/mainloop-private.h
+++ b/pappl/mainloop-private.h
@@ -28,15 +28,21 @@ extern char *_papplMainloopPath _PAPPL_PRIVATE;
//
extern int _papplMainloopAddPrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
+extern int _papplMainloopAddScanner(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopAutoAddPrinters(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopCancelJob(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopDeletePrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
+extern int _papplMainloopDeleteScanner(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopGetSetDefaultPrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopModifyPrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopPausePrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopResumePrinter(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
+
+// TODO : Introduce functionality for scanner callbacks and scanner drivers.
extern int _papplMainloopRunServer(const char *base_name, const char *version, const char *footer_html, cups_len_t num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, cups_len_t num_options, cups_option_t **options, pappl_ml_system_cb_t system_cb, void *data) _PAPPL_PRIVATE;
extern int _papplMainloopShowDevices(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
+
+// TODO : Introduce functionality for scanner callbacks and scanner drivers.
extern int _papplMainloopShowDrivers(const char *base_name, cups_len_t num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_driver_cb_t driver_cb, cups_len_t num_options, cups_option_t *options, pappl_ml_system_cb_t system_cb, void *data) _PAPPL_PRIVATE;
extern int _papplMainloopShowJobs(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
extern int _papplMainloopShowOptions(const char *base_name, cups_len_t num_options, cups_option_t *options) _PAPPL_PRIVATE;
@@ -47,6 +53,7 @@ extern int _papplMainloopSubmitJob(const char *base_name, cups_len_t num_options
extern void _papplMainloopAddOptions(ipp_t *request, cups_len_t num_options, cups_option_t *options, ipp_t *supported) _PAPPL_PRIVATE;
extern void _papplMainloopAddPrinterURI(ipp_t *request, const char *printer_name, char *resource,size_t rsize) _PAPPL_PRIVATE;
+extern int _papplMainloopAddScannerURI(http_t *request, const char *scanner_name, char *resource, size_t rsize) _PAPPL_PRIVATE; // Request type ipp ?
extern http_t *_papplMainloopConnect(const char *base_name, bool auto_start) _PAPPL_PRIVATE;
extern http_t *_papplMainloopConnectURI(const char *base_name, const char *printer_uri, char *resource, size_t rsize) _PAPPL_PRIVATE;
extern char *_papplMainloopGetDefaultPrinter(http_t *http, char *buffer, size_t bufsize) _PAPPL_PRIVATE;
diff --git a/pappl/mainloop-subcommands.c b/pappl/mainloop-subcommands.c
index 62f8c874..9faae3e6 100644
--- a/pappl/mainloop-subcommands.c
+++ b/pappl/mainloop-subcommands.c
@@ -111,6 +111,95 @@ _papplMainloopAddPrinter(
}
+
+//
+// '_papplMainloopAddScanner()' - Add a scanner using eSCL.
+//
+int
+_papplMainloopAddScanner(
+ const char *base_name, // I - Base name
+ cups_len_t num_options, // I - Number of options
+ cups_option_t *options) // I - Options
+{
+ http_t *http = NULL; // Connection to server
+ const char *device_uri, // Device URI
+ *scanner_name, // Name of scanner
+ *scanner_uri, // Scanner URI
+ *escl_path; // eSCL resource path
+ char resource[1024]; // Resource path for connection
+ bool status = false; // Status of scanner addition
+
+ // Get required values...
+ device_uri = cupsGetOption("device-uri", (int)num_options, options);
+ scanner_name = cupsGetOption("scanner-name", (int)num_options, options);
+ escl_path = cupsGetOption("escl", (int)num_options, options);
+
+ // Rest of the implementation remains the same...
+ if (!device_uri || !scanner_name)
+ {
+ if (!scanner_name)
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Missing '-d SCANNER'."), base_name);
+ if (!device_uri)
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Missing '-v DEVICE-URI'."), base_name);
+ return (1);
+ }
+
+ if ((scanner_uri = cupsGetOption("scanner-uri", (int)num_options, options)) != NULL)
+ {
+ if ((http = _papplMainloopConnectURI(base_name, scanner_uri, resource,
+ sizeof(resource))) == NULL)
+ return (1);
+ }
+
+ // Set up eSCL connection and registration
+ if (!escl_path)
+ escl_path = "/eSCL/"; // Default eSCL path if not specified
+
+ // Create scanner registration request
+ char *post_data = NULL;
+ size_t post_size = 0;
+ FILE *post_file = open_memstream(&post_data, &post_size);
+
+ if (post_file)
+ {
+ // Format eSCL scanner registration XML
+ fprintf(post_file, "\n");
+ fprintf(post_file, "\n");
+ fprintf(post_file, " %s \n", scanner_name);
+ fprintf(post_file, " %s \n", device_uri);
+ fprintf(post_file, " \n");
+ fclose(post_file);
+
+ // Set up HTTP POST request
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/xml");
+ httpSetLength(http, post_size);
+
+ // Send the registration request
+ if (httpPost(http, escl_path) == HTTP_STATUS_OK)
+ {
+ http_status_t response = httpUpdate(http);
+ if (response == HTTP_STATUS_OK || response == HTTP_STATUS_CREATED)
+ status = true;
+ }
+
+ free(post_data);
+ }
+
+ httpClose(http);
+
+ if (!status)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Unable to add scanner: %s"),
+ base_name, cupsGetErrorString());
+ return (1);
+ }
+
+ return (0);
+}
+
+
+
//
// '_papplMainloopAutoAddPrinters()' - Automatically add printers.
//
@@ -303,6 +392,107 @@ _papplMainloopDeletePrinter(
return (0);
}
+//
+// '_papplMainloopDeleteScanner()' - Delete a scanner registration.
+//
+
+int // O - Exit status
+_papplMainloopDeleteScanner(
+ const char *base_name, // I - Base name
+ cups_len_t num_options, // I - Number of options
+ cups_option_t *options) // I - Options
+{
+ http_t *http = NULL; // Connection to server
+ const char *device_uri, // Device URI
+ *scanner_name, // Name of scanner
+ *scanner_uri, // Scanner URI
+ *escl_path; // eSCL resource path
+ char resource[1024]; // Resource path for connection
+ bool status = false; // Status of scanner deletion
+ http_status_t response; // HTTP response status
+
+ // Get required values...
+ device_uri = cupsGetOption("device-uri", (int)num_options, options);
+ scanner_name = cupsGetOption("scanner-name", (int)num_options, options);
+ escl_path = cupsGetOption("escl", (int)num_options, options);
+
+ // Check if we're deleting a remote scanner
+ scanner_uri = cupsGetOption("scanner-uri", (int)num_options, options);
+ if (scanner_uri)
+ {
+ // Connect to the remote scanner...
+ http = _papplMainloopConnectURI(base_name, scanner_uri, resource, sizeof(resource));
+ if (!http)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Unable to connect to remote scanner at '%s'"),
+ base_name, scanner_uri);
+ return (1);
+ }
+ }
+ else
+ {
+ // Validate required parameters for local scanner
+ if (!scanner_name)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Missing '-d SCANNER'."), base_name);
+ return (1);
+ }
+
+ if (!device_uri)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Missing '-v DEVICE-URI'."), base_name);
+ return (1);
+ }
+
+ // Connect to local scanner
+ http = httpConnect2(device_uri, 0, NULL, AF_UNSPEC, HTTP_ENCRYPTION_IF_REQUESTED, 1, 30000, NULL);
+ if (!http)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Unable to connect to scanner at '%s'"),
+ base_name, device_uri);
+ return (1);
+ }
+ }
+
+ // Set up eSCL path
+ if (!escl_path)
+ escl_path = "/eSCL/"; // Default eSCL path if not specified
+
+ // Construct the deletion path
+ char delete_path[1024];
+ snprintf(delete_path, sizeof(delete_path), "%sregistration/%s",
+ escl_path, scanner_name);
+
+ // Send DELETE request to remove scanner registration
+ httpClearFields(http);
+
+ if (httpDelete(http, delete_path) != HTTP_STATUS_OK)
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Unable to send deletion request: %s"),
+ base_name, cupsGetErrorString());
+ httpClose(http);
+ return (1);
+ }
+
+ // Check the response
+ response = httpUpdate(http);
+ if (response == HTTP_STATUS_OK || response == HTTP_STATUS_NO_CONTENT)
+ {
+ status = true;
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Successfully deleted scanner '%s'"),
+ base_name, scanner_name);
+ }
+ else
+ {
+ _papplLocPrintf(stderr, _PAPPL_LOC("%s: Scanner deletion failed with status %d"),
+ base_name, response);
+ }
+
+ // Clean up
+ httpClose(http);
+
+ return (status ? 0 : 1);
+}
//
// '_papplMainloopGetSetDefaultPrinter()' - Get/set the default printer.
diff --git a/pappl/mainloop-support.c b/pappl/mainloop-support.c
index eceaa595..a92c160c 100644
--- a/pappl/mainloop-support.c
+++ b/pappl/mainloop-support.c
@@ -392,6 +392,63 @@ _papplMainloopAddPrinterURI(
}
+// '_papplMainloopAddScannerURI()' - Add the scanner-uri attribute and return a
+// resource path.
+//
+
+int
+_papplMainloopAddScannerURI(
+ http_t *request, // I - HTTP request
+ const char *scanner_name, // I - Scanner name
+ char *resource, // I - Resource path buffer
+ size_t rsize) // I - Size of buffer
+{
+ char uri[1024]; // scanner-uri value
+ char *resptr; // Pointer into resource path
+ int ret;
+
+ // Construct the initial resource path for the scanner
+ ret = snprintf(resource, rsize, "/escl/scan/%s", scanner_name);
+ if (ret < 0 || (size_t)ret >= rsize) {
+ fprintf(stderr, "Error: Resource path buffer too small.\n");
+ return -1;
+ }
+
+ // Sanitize the resource path by replacing invalid characters with '_'
+ for (resptr = resource + strlen("/escl/scan/"); *resptr; resptr++) {
+ if ((*resptr & 255) <= ' ' || strchr("\177/\\\'\"?#", *resptr)) {
+ *resptr = '_';
+ }
+ }
+
+ // Eliminate duplicate and trailing underscores
+ resptr = resource + strlen("/escl/scan/");
+ while (*resptr) {
+ if (resptr[0] == '_' && resptr[1] == '_') {
+ memmove(resptr, resptr + 1, strlen(resptr));
+ // Duplicate underscores removed, do not advance resptr
+ }
+ else if (resptr[0] == '_' && !resptr[1]) {
+ *resptr = '\0'; // Trailing underscore removed
+ break;
+ }
+ else {
+ resptr++;
+ }
+ }
+
+ // Assemble the full URI using HTTP
+ ret = httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "http", NULL, "localhost", 0, resource);
+
+ if (ret < 0) {
+ fprintf(stderr, "Error: Failed to assemble URI.\n");
+ return -1;
+ }
+
+ httpSetField(request, HTTP_FIELD_CONTENT_TYPE, uri);
+ return 0;
+}
+
//
// '_papplMainloopConnect()' - Connect to the local server.
//
diff --git a/pappl/pappl-private.h b/pappl/pappl-private.h
index 1d4f5457..88e20cb1 100644
--- a/pappl/pappl-private.h
+++ b/pappl/pappl-private.h
@@ -17,5 +17,6 @@
# include "log-private.h"
# include "mainloop-private.h"
# include "printer-private.h"
+# include "scanner-private.h"
# include "system-private.h"
#endif // !_PAPPL_PAPPL_PRIVATE_H_
diff --git a/pappl/printer-accessors.c b/pappl/printer-accessors.c
index d8cef8dd..6fc86ebe 100644
--- a/pappl/printer-accessors.c
+++ b/pappl/printer-accessors.c
@@ -649,7 +649,7 @@ papplPrinterHoldNewJobs(
ret = true;
// Notify of the change in state...
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_CONFIG_CHANGED, "Holding new jobs.");
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_CONFIG_CHANGED, "Holding new jobs.");
}
_papplRWUnlock(printer);
@@ -942,7 +942,7 @@ papplPrinterPause(
else
printer->state = IPP_PSTATE_STOPPED;
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED | PAPPL_EVENT_PRINTER_STOPPED, NULL);
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED | PAPPL_EVENT_PRINTER_STOPPED, NULL);
_papplRWUnlock(printer);
}
@@ -979,7 +979,7 @@ papplPrinterReleaseHeldNewJobs(
printer->config_time = time(NULL);
ret = true;
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_CONFIG_CHANGED, "Releasing held new jobs.");
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_CONFIG_CHANGED, "Releasing held new jobs.");
for (job = (pappl_job_t *)cupsArrayGetFirst(printer->active_jobs); job; job = (pappl_job_t *)cupsArrayGetNext(printer->active_jobs))
{
@@ -1022,7 +1022,7 @@ papplPrinterResume(
printer->is_stopped = false;
printer->state = IPP_PSTATE_IDLE;
- _papplSystemAddEventNoLock(printer->system, printer, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, "Resumed printer.");
+ _papplSystemAddEventNoLock(printer->system, printer, NULL, NULL, PAPPL_EVENT_PRINTER_STATE_CHANGED, "Resumed printer.");
_papplPrinterCheckJobsNoLock(printer);
diff --git a/pappl/scanner-accessors.c b/pappl/scanner-accessors.c
new file mode 100644
index 00000000..7a87c790
--- /dev/null
+++ b/pappl/scanner-accessors.c
@@ -0,0 +1,779 @@
+//
+// Scanner accessor functions for the Scanner Application Framework
+//
+// Copyright © 2020-2024 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "scanner-private.h"
+#include "scanner.h"
+#include "job-private.h"
+#include "system-private.h"
+
+//
+// 'papplScannerCloseDevice()' - Close the device associated with the scanner .
+//
+// This function closes the device for a scanner. The device must have been
+// previously opened using the @link papplScannerOpenDevice@ function.
+//
+
+void
+papplScannerCloseDevice(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+ if (scanner->device && scanner->device_in_use)
+ {
+ scanner->device_in_use = false;
+
+ if (scanner->state != ESCL_SSTATE_PROCESSING)
+ {
+ papplDeviceClose(scanner->device);
+ scanner->device = NULL;
+ }
+ }
+
+ _papplRWUnlock(scanner);
+}
+
+//
+// 'papplScannerDisable()' - Stop accepting jobs on a scanner.
+//
+// This function stops accepting jobs on a scanner.
+//
+
+void
+papplScannerDisable(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ if (scanner)
+ {
+ scanner->is_accepting = false;
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, NULL);
+ }
+}
+
+//
+// 'papplScannerEnable()' - Start accepting jobs on a scanner.
+//
+// This function starts accepting jobs on a scanner.
+//
+
+void
+papplScannerEnable(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ if (scanner)
+ {
+ scanner->is_accepting = true;
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, NULL);
+ }
+}
+
+
+//
+// 'papplScannerGetContact()' - Get the "scanner-contact" value.
+//
+// This function copies the current scanner contact information to the buffer
+// pointed to by the "contact" argument.
+//
+
+pappl_contact_t * // O - Contact
+papplScannerGetContact(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_contact_t *contact) // O - Contact
+{
+ if (!scanner || !contact)
+ {
+ if (contact)
+ memset(contact, 0, sizeof(pappl_contact_t));
+
+ return (contact);
+ }
+
+ _papplRWLockRead(scanner);
+
+ *contact = scanner->contact;
+
+ _papplRWUnlock(scanner);
+
+ return (contact);
+}
+
+
+//
+// 'papplScannerGetDeviceID()' - Get the device ID of the scanner.
+//
+// This function returns the device ID of the scanner.
+//
+
+const char * // O - Device ID string
+papplScannerGetDeviceID(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->device_id : NULL);
+}
+
+//
+// 'papplScannerGetDeviceURI()' - Get the URI of the device associated with the
+// scanner.
+//
+// This function returns the device URI for the scanner.
+//
+
+const char * // O - Device URI string
+papplScannerGetDeviceURI(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->device_uri : "file:///dev/null");
+}
+
+
+//
+// 'papplScannerGetDNSSDName()' - Get the current DNS-SD service name.
+//
+// This function copies the current DNS-SD service name to the buffer pointed
+// to by the "buffer" argument.
+//
+
+char * // O - DNS-SD service name or `NULL` for none
+papplScannerGetDNSSDName(
+ pappl_scanner_t *scanner, // I - Scanner
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !scanner->dns_sd_name || !buffer || bufsize == 0)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ _papplRWLockRead(scanner);
+ papplCopyString(buffer, scanner->dns_sd_name, bufsize);
+ _papplRWUnlock(scanner);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetGeoLocation()' - Get the current geo-location as a "geo:"
+// URI.
+//
+// This function copies the currently configured geographic location as a "geo:"
+// URI to the buffer pointed to by the "buffer" argument.
+//
+
+char * // O - "geo:" URI or `NULL` for unknown
+papplScannerGetGeoLocation(
+ pappl_scanner_t *scanner, // I - Scanner
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !scanner->geo_location || !buffer || bufsize == 0)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ _papplRWLockRead(scanner);
+ papplCopyString(buffer, scanner->geo_location, bufsize);
+ _papplRWUnlock(scanner);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetID()' - Get the scanner ID.
+//
+// This function returns the scanner's unique positive integer identifier.
+//
+
+int // O - "scanner-id" value or `0` for none
+papplScannerGetID(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->scanner_id : 0);
+}
+
+//
+// 'papplScannerGetLocation()' - Get the location string.
+//
+// This function copies the scanner's human-readable location to the buffer
+// pointed to by the "buffer" argument.
+//
+
+char * // O - Location or `NULL` for none
+papplScannerGetLocation(
+ pappl_scanner_t *scanner, // I - Scanner
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !scanner->location || !buffer || bufsize == 0)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ _papplRWLockRead(scanner);
+ papplCopyString(buffer, scanner->location, bufsize);
+ _papplRWUnlock(scanner);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetName()' - Get the scanner name.
+//
+// This function returns the scanner's human-readable name.
+//
+
+
+const char * // O - Scanner name
+papplScannerGetName(
+ pappl_scanner_t *scanner) // I - scanner
+{
+ return (scanner ? scanner->name : NULL);
+}
+
+//
+// 'papplScannerGetNextJobID()' - Get the next job ID.
+//
+// This function returns the positive integer identifier that will be used for
+// the next job that is created.
+//
+
+int // O - Next job ID or `0` for none
+papplScannerGetNextJobID(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->next_job_id : 0);
+}
+
+//
+// 'papplScannerGetOrganization()' - Get the organization name.
+//
+// This function copies the scanner's organization name to the buffer pointed
+// to by the "buffer" argument.
+//
+
+char * // O - Organization name or `NULL` for none
+papplScannerGetOrganization(
+ pappl_scanner_t *scanner, // I - Scanner
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !scanner->organization || !buffer || bufsize == 0)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ _papplRWLockRead(scanner);
+ papplCopyString(buffer, scanner->organization, bufsize);
+ _papplRWUnlock(scanner);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetOrganizationalUnit()' - Get the organizational unit name.
+//
+// This function copies the scanner's organizational unit name to the buffer
+// pointed to by the "buffer" argument.
+//
+
+char * // O - Organizational unit name or `NULL` for none
+papplScannerGetOrganizationalUnit(
+ pappl_scanner_t *scanner, // I - Scanner
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !scanner->org_unit || !buffer || bufsize == 0)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ _papplRWLockRead(scanner);
+ papplCopyString(buffer, scanner->org_unit, bufsize);
+ _papplRWUnlock(scanner);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetPath()' - Get the URL path for a scanner web page.
+//
+// This function generates and returns the URL path for the scanner's web page.
+// The "subpath" argument specifies an optional sub-path for a specific printer
+// web page.
+//
+
+char * // O - URI path or `NULL` on error
+papplScannerGetPath(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *subpath, // I - Sub-path or `NULL` for none
+ char *buffer, // I - String buffer
+ size_t bufsize) // I - Size of string buffer
+{
+ if (!scanner || !buffer || bufsize < 32)
+ {
+ if (buffer)
+ *buffer = '\0';
+
+ return (NULL);
+ }
+
+ if (subpath)
+ snprintf(buffer, bufsize, "%s/%s", scanner->uriname, subpath);
+ else
+ papplCopyString(buffer, scanner->uriname, bufsize);
+
+ return (buffer);
+}
+
+//
+// 'papplScannerGetReasons()' - Get the current "scanner-state-reasons" bit values.
+//
+// This function returns the current scanner state reasons bitfield, which can
+// be updated by the scanner driver and/or by the @link papplScannerSetReasons@
+// function.
+//
+
+pappl_sreason_t // O - "scanner-state-reasons" bit values
+papplScannerGetReasons(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ pappl_sreason_t ret = PAPPL_SREASON_NONE;
+ // Return value
+
+ if (scanner)
+ {
+ _papplRWLockRead(scanner);
+
+ if (!scanner->device_in_use && !scanner->processing_job && (time(NULL) - scanner->status_time) > 1 && scanner->driver_data.status_cb)
+ {
+ // Update scanner status...
+ _papplRWUnlock(scanner);
+ (scanner->driver_data.status_cb)(scanner);
+
+ _papplRWLockRead(scanner);
+ scanner->status_time = time(NULL);
+ }
+
+ ret = scanner->state_reasons;
+
+ _papplRWUnlock(scanner);
+ }
+
+ return (ret);
+}
+
+//
+// 'papplScannerGetState()' - Get the current "scanner-state" value.
+//
+// This function returns the current scanner state as an enumeration:
+//
+
+escl_sstate_t // O - "scanner-state" value
+papplScannerGetState(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->state : ESCL_SSTATE_STOPPED);
+}
+
+
+//
+// 'papplScannerGetSystem()' - Get the system associated with the scanner.
+//
+// This function returns a pointer to the system object that contains the
+// scanner.
+//
+
+pappl_system_t * // O - System
+papplScannerGetSystem(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->system : NULL);
+}
+
+//
+// 'papplScannerIsAcceptingJobs()' - Return whether the scanner is accepting jobs.
+//
+// This function returns a boolean value indicating whether a scanner is
+// accepting jobs.
+//
+
+bool // O - `true` if the scanner is accepting jobs, `false` otherwise
+papplScannerIsAcceptingJobs(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ bool is_accepting; // Return value
+
+
+ // Range check input...
+ if (!scanner)
+ return (false);
+
+ // Lock and grab value...
+ _papplRWLockRead(scanner);
+ is_accepting = scanner->is_accepting;
+ _papplRWUnlock(scanner);
+
+ return (is_accepting);
+}
+
+//
+// 'papplScannerIsDeleted()' - Return whether a scanner is in the process of being deleted.
+//
+// This function returns a boolean value indicating whether a scanner is being
+// deleted.
+//
+
+bool // O - `true` is scanner is being deleted, `false` otherwise
+papplScannerIsDeleted(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ bool is_deleted; // Return value
+
+
+ // Range check input...
+ if (!scanner)
+ return (false);
+
+ // Lock and grab value...
+ _papplRWLockRead(scanner);
+ is_deleted = scanner->is_deleted;
+ _papplRWUnlock(scanner);
+
+ return (is_deleted);
+}
+
+
+//
+// 'papplScannerOpenDevice()' - Open the device associated with a scanner.
+//
+// This function opens the scanners's device. `NULL` is returned if the device
+// is already in use, for example while a job is being scanned.
+//
+// The returned device must be closed using the @link papplScannerCloseDevice@
+// function.
+//
+
+pappl_device_t * // O - Device or `NULL` if not possible
+papplScannerOpenDevice(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ pappl_device_t *device = NULL; // Open device
+
+
+ if (!scanner)
+ return (NULL);
+
+ _papplRWLockWrite(scanner);
+
+ if (!scanner->device_in_use && !scanner->processing_job && scanner->device_uri)
+ {
+ scanner->device = device = papplDeviceOpen(scanner->device_uri, "scanner", papplLogDevice, scanner->system);
+ scanner->device_in_use = device != NULL;
+ }
+
+ _papplRWUnlock(scanner);
+
+ return (device);
+}
+
+//
+// 'papplScannerPause()' - Pause (stop) a scanner.
+//
+// This function pauses a scanner. If the scanner is currently processing
+// (scanning) a job, it will be completed before the scanner is stopped.
+//
+
+void
+papplScannerPause(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ if (scanner->processing_job)
+ scanner->is_stopped = true;
+ else
+ scanner->state = ESCL_SSTATE_STOPPED;
+
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED | PAPPL_EVENT_SCANNER_STOPPED, NULL);
+
+ _papplRWUnlock(scanner);
+}
+
+//
+// 'papplScannerResume()' - Resume (start) a scanner.
+//
+// This function resumes a scanner.
+//
+
+void
+papplScannerResume(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ scanner->is_stopped = false;
+ scanner->state = ESCL_SSTATE_IDLE;
+
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, "Resumed scanner.");
+
+ _papplRWUnlock(scanner);
+}
+
+
+//
+// 'papplScannerSetContact()' - Set the "scanner-contact" value.
+//
+// This function sets the scanner's contact information.
+//
+
+void
+papplScannerSetContact(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_contact_t *contact) // I - Contact
+{
+ if (!scanner || !contact)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ scanner->contact = *contact;
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetDNSSDName()' - Set the DNS-SD service name.
+//
+// This function sets the scanner's DNS-SD service name. If `NULL`, the scanner
+// will stop advertising the scanner.
+//
+
+void
+papplScannerSetDNSSDName(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *value) // I - DNS-SD service name or `NULL` for none
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ free(scanner->dns_sd_name);
+ scanner->dns_sd_name = value ? strdup(value) : NULL;
+ scanner->dns_sd_collision = false;
+ scanner->dns_sd_serial = 0;
+ scanner->config_time = time(NULL);
+
+ // TODO
+ if (!value)
+ _papplScannerUnregisterDNSSDNoLock(scanner);
+ else
+ _papplScannerRegisterDNSSDNoLock(scanner);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetGeoLocation()' - Set the geo-location value as a "geo:" URI.
+//
+// This function sets the scanner's geographic location as a "geo:" URI. If
+// `NULL`, the location is cleared to the 'unknown' value.
+//
+
+void
+papplScannerSetGeoLocation(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *value) // I - "geo:" URI or `NULL` for unknown
+{
+ float lat, lon; // Latitude and longitude from geo: URI
+
+
+ if (!scanner)
+ return;
+
+ // Validate geo-location - must be NULL or a "geo:" URI...
+ if (value && *value && sscanf(value, "geo:%f,%f", &lat, &lon) != 2)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ free(scanner->geo_location);
+ scanner->geo_location = value && *value ? strdup(value) : NULL;
+ scanner->config_time = time(NULL);
+
+ // TODO
+ _papplScannerRegisterDNSSDNoLock(scanner);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetLocation()' - Set the location string.
+//
+// This function sets the scanner's human-readable location string. If `NULL`,
+// the location is cleared.
+//
+
+void
+papplScannerSetLocation(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *value) // I - Location ("Bob's Office", etc.) or `NULL` for none
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ free(scanner->location);
+ scanner->location = value ? strdup(value) : NULL;
+ scanner->config_time = time(NULL);
+
+ //TODO
+ _papplScannerRegisterDNSSDNoLock(scanner);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetNextJobID()' - Set the next "job-id" value.
+//
+// This function sets the next unique positive integer identifier that will be
+// used for a job.
+//
+// > Note: This function is normally only called once to restore the previous
+// > state of the scanner.
+//
+void
+papplScannerSetNextJobID(
+ pappl_scanner_t *scanner, // I - Scanner
+ int next_job_id) // I - Next "job-id" value
+{
+ if (!scanner || next_job_id < 1)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ scanner->next_job_id = next_job_id;
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetOrganization()' - Set the organization name.
+//
+// This function sets the scanner's organization name. If `NULL` the value is
+// cleared.
+//
+
+void
+papplScannerSetOrganization(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *value) // I - Organization name or `NULL` for none
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ free(scanner->organization);
+ scanner->organization = value ? strdup(value) : NULL;
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+//
+// 'papplScannerSetOrganizationalUnit()' - Set the organizational unit name.
+//
+// This function sets the scanner's organizational unit name. If `NULL` the
+// value is cleared.
+//
+
+void
+papplScannerSetOrganizationalUnit(
+ pappl_scanner_t *scanner, // I - Scanner
+ const char *value) // I - Organizational unit name or `NULL` for none
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ free(scanner->org_unit);
+ scanner->org_unit = value ? strdup(value) : NULL;
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ _papplSystemConfigChanged(scanner->system);
+}
+
+
+//
+// 'papplScannerSetReasons()' - Add or remove values from
+// "scanner-state-reasons".
+//
+// This function updates the scanner state reasons bitfield by clearing any bit
+// values in the "remove" argument and setting any bit values in the "add"
+// argument.
+//
+
+void
+papplScannerSetReasons(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_sreason_t add, // I - "scanner-state-reasons" bit values to add or `PAPPL_SREASON_NONE` for none
+ pappl_sreason_t remove) // I - "scanner-state-reasons" bit values to remove or `PAPPL_SREASON_NONE` for none
+{
+ if (!scanner)
+ return;
+
+ _papplRWLockWrite(scanner);
+
+ scanner->state_reasons &= ~remove;
+ scanner->state_reasons |= add;
+ scanner->state_time = scanner->status_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+}
diff --git a/pappl/scanner-driver.c b/pappl/scanner-driver.c
new file mode 100644
index 00000000..52f180ac
--- /dev/null
+++ b/pappl/scanner-driver.c
@@ -0,0 +1,293 @@
+//
+// Printer driver functions for the Printer Application Framework
+//
+// Copyright © 2020-2024 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "scanner-private.h"
+#include "system-private.h"
+#include
+#include
+
+//
+// Local functions...
+//
+extern xmlDocPtr make_escl_attr(pappl_scanner_t *scanner);
+extern const char *ScannerInputSourceString(pappl_sc_input_source_t value) _PAPPL_PRIVATE;
+extern const char *ScannerResolutionString(int resolution) _PAPPL_PRIVATE;
+extern const char *_papplScannerColorModeString(pappl_sc_color_mode_t value) _PAPPL_PRIVATE;
+
+//
+// 'papplScannerGetDriverData()' - Get the current scanner driver data.
+//
+// This function copies the current scanner driver data into the specified buffer.
+//
+
+pappl_sc_driver_data_t * // O - Driver data or `NULL` if none
+papplScannerGetDriverData(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_sc_driver_data_t *data) // I - Pointer to driver data structure to fill
+{
+ if (!scanner || !scanner->driver_name || !data)
+ {
+ if (data)
+ _papplScannerInitDriverData(scanner, data);
+
+ return (NULL);
+ }
+
+ memcpy(data, &scanner->driver_data, sizeof(pappl_sc_driver_data_t));
+
+ return (data);
+}
+
+
+//
+// 'papplScannerGetDriverName()' - Get the driver name for a scanner.
+//
+// This function returns the driver name for the scanner.
+//
+
+const char * // O - Driver name or `NULL` for none
+papplScannerGetDriverName(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ return (scanner ? scanner->driver_name : NULL);
+}
+
+//
+// 'papplScannerSetDriverData()' - Set the driver data.
+//
+// This function sets the driver data.
+//
+
+bool // O - `true` on success, `false` on failure
+papplScannerSetDriverData(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_sc_driver_data_t *data) // I - Driver data
+{
+ if (!scanner || !data)
+ return (false);
+
+ // Note: For now, we assume the data is valid. We will add validation in later versions.
+
+ _papplRWLockWrite(scanner);
+
+ memcpy(&scanner->driver_data, data, sizeof(scanner->driver_data));
+
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ return (true);
+}
+
+//
+// 'papplScannerSetDriverDefaults()' - Set the default scan option values.
+//
+// This function validates and sets the scanner's default scan options.
+//
+
+bool // O - `true` on success, `false` on failure
+papplScannerSetDriverDefaults(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_sc_driver_data_t *data) // I - Driver data
+{
+ if (!scanner || !data)
+ return (false);
+
+ // Note: For now, we assume the data is valid. We will add validation in later versions.
+
+ _papplRWLockWrite(scanner);
+
+ scanner->driver_data.default_color_mode = data->default_color_mode;
+ scanner->driver_data.default_resolution = data->default_resolution;
+ scanner->driver_data.default_input_source = data->default_input_source;
+ scanner->driver_data.default_media_type = data->default_media_type;
+ scanner->driver_data.default_document_format = data->default_document_format;
+ scanner->driver_data.default_intent = data->default_intent;
+ scanner->driver_data.default_color_space = data->default_color_space;
+
+ scanner->config_time = time(NULL);
+
+ _papplRWUnlock(scanner);
+
+ return (true);
+}
+
+//
+// _paplScannerInitDriverData() - Initialize the driver data.
+//
+// This function initializes the driver data through the callback function.
+
+void
+_papplScannerInitDriverData(
+ pappl_scanner_t *scanner, // I - Scanner
+ pappl_sc_driver_data_t *d) // I - Driver data
+{
+ memset(d, 0, sizeof(pappl_sc_driver_data_t));
+ if (scanner->driver_data.capabilities_cb)
+ {
+ // Get the driver data from the callback function
+ pappl_sc_driver_data_t callback_data = (scanner->driver_data.capabilities_cb)(scanner);
+ *d = callback_data;
+ }
+}
+
+//
+// 'make_escl_attr()' - Generate the scanner attributes in eSCL format.
+//
+
+xmlDocPtr // O - XML document pointer
+make_escl_attr(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root_node = NULL;
+
+ doc = xmlNewDoc(BAD_CAST "1.0");
+ if (!doc)
+ return (NULL);
+
+ root_node = xmlNewNode(NULL, BAD_CAST "scan:ScannerCapabilities");
+ if (!root_node)
+ {
+ xmlFreeDoc(doc);
+ return (NULL);
+ }
+ xmlDocSetRootElement(doc, root_node);
+
+ xmlNsPtr ns = xmlNewNs(root_node, BAD_CAST "http://schemas.hp.com/imaging/escl/2011/05/03", BAD_CAST "scan");
+ xmlSetNs(root_node, ns);
+
+ xmlNewChild(root_node, NULL, BAD_CAST "pwg:Version", BAD_CAST "2.0"); // Example value
+ xmlNewChild(root_node, NULL, BAD_CAST "pwg:MakeAndModel", BAD_CAST scanner->driver_data.make_and_model);
+
+ xmlNodePtr resolutions_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:SupportedResolutions", NULL);
+ for (int i = 0; i < MAX_RESOLUTIONS && scanner->driver_data.resolutions[i]; i++)
+ {
+ char res_str[16];
+ snprintf(res_str, sizeof(res_str), "%d", scanner->driver_data.resolutions[i]);
+ xmlNewChild(resolutions_node, NULL, BAD_CAST "scan:Resolution", BAD_CAST res_str);
+ }
+
+ xmlNodePtr formats_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:DocumentFormatsSupported", NULL);
+ for (int i = 0; i < PAPPL_MAX_FORMATS && scanner->driver_data.document_formats_supported[i]; i++)
+ {
+ xmlNewChild(formats_node, NULL, BAD_CAST "scan:DocumentFormat", BAD_CAST scanner->driver_data.document_formats_supported[i]);
+ }
+
+ xmlNodePtr color_modes_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:ColorModesSupported", NULL);
+ for (int i = 0; i < PAPPL_MAX_COLOR_MODES && scanner->driver_data.color_modes_supported[i]; i++)
+ {
+ xmlNewChild(color_modes_node, NULL, BAD_CAST "scan:ColorMode", BAD_CAST _papplScannerColorModeString(scanner->driver_data.color_modes_supported[i]));
+ }
+
+ xmlNodePtr input_sources_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:InputSourcesSupported", NULL);
+ for (int i = 0; i < PAPPL_MAX_SOURCES && scanner->driver_data.input_sources_supported[i]; i++)
+ {
+ xmlNewChild(input_sources_node, NULL, BAD_CAST "scan:InputSource", BAD_CAST ScannerInputSourceString(scanner->driver_data.input_sources_supported[i]));
+ }
+
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:DuplexSupported", BAD_CAST (scanner->driver_data.duplex_supported ? "true" : "false"));
+
+ xmlNodePtr color_spaces_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:ColorSpacesSupported", NULL);
+ for (int i = 0; i < PAPPL_MAX_COLOR_SPACES && scanner->driver_data.color_spaces_supported[i]; i++)
+ {
+ xmlNewChild(color_spaces_node, NULL, BAD_CAST "scan:ColorSpace", BAD_CAST scanner->driver_data.color_spaces_supported[i]);
+ }
+
+ char max_scan_area_str[64];
+ snprintf(max_scan_area_str, sizeof(max_scan_area_str), "width=%d,height=%d",
+ scanner->driver_data.max_scan_area[0],
+ scanner->driver_data.max_scan_area[1]);
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:MaxScanArea", BAD_CAST max_scan_area_str);
+
+ xmlNodePtr media_types_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:MediaTypesSupported", NULL);
+ for (int i = 0; i < PAPPL_MAX_MEDIA_TYPES && scanner->driver_data.media_type_supported[i]; i++)
+ {
+ xmlNewChild(media_types_node, NULL, BAD_CAST "scan:MediaType", BAD_CAST scanner->driver_data.media_type_supported[i]);
+ }
+
+ xmlNodePtr defaults_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:Defaults", NULL);
+ xmlNewChild(defaults_node, NULL, BAD_CAST "scan:DefaultResolution", BAD_CAST ScannerResolutionString(scanner->driver_data.default_resolution));
+ xmlNewChild(defaults_node, NULL, BAD_CAST "scan:DefaultColorMode", BAD_CAST _papplScannerColorModeString(scanner->driver_data.default_color_mode));
+ xmlNewChild(defaults_node, NULL, BAD_CAST "scan:DefaultInputSource", BAD_CAST ScannerInputSourceString(scanner->driver_data.default_input_source));
+
+ char scan_region_str[64];
+ snprintf(scan_region_str, sizeof(scan_region_str), "top=%d,left=%d,width=%d,height=%d",
+ scanner->driver_data.scan_region_supported[0],
+ scanner->driver_data.scan_region_supported[1],
+ scanner->driver_data.scan_region_supported[2],
+ scanner->driver_data.scan_region_supported[3]);
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:ScanRegionsSupported", BAD_CAST scan_region_str);
+
+ xmlNodePtr mandatory_intents_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:MandatoryIntents", NULL);
+ for (int i = 0; i < 5 && scanner->driver_data.mandatory_intents[i]; i++)
+ {
+ xmlNewChild(mandatory_intents_node, NULL, BAD_CAST "scan:Intent", BAD_CAST scanner->driver_data.mandatory_intents[i]);
+ }
+
+ xmlNodePtr optional_intents_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:OptionalIntents", NULL);
+ for (int i = 0; i < 5 && scanner->driver_data.optional_intents[i]; i++)
+ {
+ xmlNewChild(optional_intents_node, NULL, BAD_CAST "scan:Intent", BAD_CAST scanner->driver_data.optional_intents[i]);
+ }
+
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:CompressionSupported", BAD_CAST (scanner->driver_data.compression_supported ? "true" : "false"));
+
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:NoiseRemovalSupported", BAD_CAST (scanner->driver_data.noise_removal_supported ? "true" : "false"));
+
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:SharpeningSupported", BAD_CAST (scanner->driver_data.sharpening_supported ? "true" : "false"));
+
+ xmlNewChild(root_node, NULL, BAD_CAST "scan:BinaryRenderingSupported", BAD_CAST (scanner->driver_data.binary_rendering_supported ? "true" : "false"));
+
+ xmlNodePtr feed_directions_node = xmlNewChild(root_node, NULL, BAD_CAST "scan:FeedDirectionsSupported", NULL);
+ for (int i = 0; i < 2 && scanner->driver_data.feed_direction_supported[i]; i++)
+ {
+ xmlNewChild(feed_directions_node, NULL, BAD_CAST "scan:FeedDirection", BAD_CAST scanner->driver_data.feed_direction_supported[i]);
+ }
+
+ return (doc);
+}
+
+// Converts input source to string
+const char *ScannerInputSourceString(pappl_sc_input_source_t value)
+{
+ switch (value)
+ {
+ case PAPPL_FLATBED:
+ return "Flatbed";
+ case PAPPL_ADF:
+ return "ADF";
+ default:
+ return "Unknown";
+ }
+}
+
+// Converts resolution to string
+const char *ScannerResolutionString(int resolution)
+{
+ static char res_str[32];
+ snprintf(res_str, sizeof(res_str), "%d DPI", resolution);
+ return res_str;
+}
+
+// Converts color mode to string
+const char *_papplScannerColorModeString(pappl_sc_color_mode_t value)
+{
+ switch (value)
+ {
+ case PAPPL_BLACKANDWHITE1:
+ return "BlackAndWhite1";
+ case PAPPL_GRAYSCALE8:
+ return "Grayscale8";
+ case PAPPL_RGB24:
+ return "RGB24";
+ default:
+ return "Unknown";
+ }
+}
\ No newline at end of file
diff --git a/pappl/scanner-escl.c b/pappl/scanner-escl.c
new file mode 100644
index 00000000..c18178b2
--- /dev/null
+++ b/pappl/scanner-escl.c
@@ -0,0 +1,93 @@
+#include "scanner-private.h"
+#include "client-private.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// _papplScannerReasonString() - Convert a scanner state reason value to a string.
+const char *
+_papplScannerReasonString(pappl_sreason_t reason)
+{
+ switch (reason)
+ {
+ case PAPPL_SREASON_NONE:
+ return "none";
+ case PAPPL_SREASON_IDLE:
+ return "idle";
+ case PAPPL_SREASON_PROCESSING:
+ return "processing";
+ case PAPPL_SREASON_TESTING:
+ return "testing";
+ case PAPPL_SREASON_STOPPED:
+ return "stopped";
+ case PAPPL_SREASON_DOWN:
+ return "down";
+ default:
+ return "unknown";
+ }
+}
+
+void
+_papplScannerCopyStateNoLock(
+ pappl_scanner_t *scanner, // I - Scanner
+ ipp_tag_t group_tag, // I - Group tag
+ ipp_t *ipp, // I - IPP message
+ pappl_client_t *client, // I - Client connection
+ cups_array_t *ra) // I - Requested attributes
+{
+ if (!ra || cupsArrayFind(ra, "scanner-is-accepting-jobs"))
+ ippAddBoolean(ipp, group_tag, "scanner-is-accepting-jobs", scanner->is_accepting);
+
+ if (!ra || cupsArrayFind(ra, "scanner-state"))
+ ippAddInteger(ipp, group_tag, IPP_TAG_ENUM, "scanner-state", (int)scanner->state);
+
+ if (!ra || cupsArrayFind(ra, "scanner-state-message"))
+ {
+ static const char * const messages[] = { "Idle.", "Scanning.", "Stopped." };
+
+ ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_TEXT), "scanner-state-message", NULL, messages[scanner->state - ESCL_SSTATE_IDLE]);
+ }
+
+ if (!ra || cupsArrayFind(ra, "scanner-state-reasons"))
+ {
+ ipp_attribute_t *attr = NULL; // scanner-state-reasons
+
+ if (scanner->state_reasons == PAPPL_SREASON_NONE)
+ {
+ if (scanner->is_stopped)
+ attr = ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "scanner-state-reasons", NULL, "moving-to-paused");
+ else if (scanner->state == ESCL_SSTATE_STOPPED)
+ attr = ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "scanner-state-reasons", NULL, "paused");
+
+ if (!attr)
+ ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "scanner-state-reasons", NULL, "none");
+ }
+ else
+ {
+ pappl_sreason_t bit; // Reason bit
+
+ for (bit = PAPPL_SREASON_IDLE; bit <= PAPPL_SREASON_DOWN; bit *= 2)
+ {
+ if (scanner->state_reasons & bit)
+ {
+ if (attr)
+ ippSetString(ipp, &attr, ippGetCount(attr), _papplScannerReasonString(bit));
+ else
+ attr = ippAddString(ipp, group_tag, IPP_CONST_TAG(IPP_TAG_KEYWORD), "scanner-state-reasons", NULL, _papplScannerReasonString(bit));
+ }
+ }
+
+ if (scanner->is_stopped)
+ ippSetString(ipp, &attr, ippGetCount(attr), "moving-to-paused");
+ else if (scanner->state == ESCL_SSTATE_STOPPED)
+ ippSetString(ipp, &attr, ippGetCount(attr), "paused");
+ }
+ }
+
+ ippAddInteger(ipp, group_tag, IPP_TAG_INTEGER, "scanner-state-change-time", (int)(scanner->state_time));
+ ippAddInteger(ipp, group_tag, IPP_TAG_INTEGER, "scanner-up-time", (int)(time(NULL) - scanner->start_time));
+}
diff --git a/pappl/scanner-private.h b/pappl/scanner-private.h
new file mode 100644
index 00000000..2c8ae3e5
--- /dev/null
+++ b/pappl/scanner-private.h
@@ -0,0 +1,111 @@
+//
+// Private scanner header file for the Scanner Application Framework
+//
+// Copyright © 2019-2024 by Michael R Sweet.
+// Copyright © 2010-2019 by Apple Inc.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#ifndef _PAPPL_SCANNER_PRIVATE_H_
+# define _PAPPL_SCANNER_PRIVATE_H_
+# include "dnssd-private.h"
+# include "scanner.h" // Changed to Scanner.h
+# include "log.h"
+# ifdef __APPLE__
+# include
+# include
+# elif !_WIN32
+# include
+# endif // __APPLE__
+# ifdef HAVE_SYS_RANDOM_H
+# include
+# endif // HAVE_SYS_RANDOM_H
+# ifdef HAVE_GNUTLS_RND
+# include
+# include
+# endif // HAVE_GNUTLS_RND
+# include "base-private.h"
+# include "device.h"
+
+//
+// Types and structures...
+//
+
+struct _pappl_scanner_s // Scanner data that get configured when a scanning job is configured
+{
+ pthread_rwlock_t rwlock; // Reader/writer lock
+ pappl_system_t *system; // Containing system
+ int scanner_id; // "scanner-id" value
+ char *name, // "scanner-name" value
+ *dns_sd_name, // "scanner-dns-sd-name" value
+ *location, // "scanner-location" value
+ *geo_location, // "scanner-geo-location" value (geo: URI)
+ *organization, // "scanner-organization" value
+ *org_unit; // "scanner-organizational-unit" value
+ pappl_contact_t contact; // "scanner-contact" value
+ char *resource; // Resource path of scanner
+ size_t resourcelen; // Length of resource path
+ char *uriname; // Name for URLs
+ char *uuid; // UUID for the scanner
+ escl_sstate_t state; // "scanner-state" value --> Replacement for ipp_pstate_t
+ pappl_sreason_t state_reasons; // "scanner-state-reasons" values --> Replacement for pappl_preason_t
+ time_t state_time; // "scanner-state-change-time" value
+ bool is_accepting, // Are we accepting scan jobs?
+ is_stopped, // Are we stopping this scanner?
+ is_deleted; // Has this scanner been deleted?
+ char *device_id, // "scanner-device-id" value
+ *device_uri; // Device URI
+ pappl_device_t *device; // Current connection to device (if any)
+ bool device_in_use; // Is the device in use?
+ char *driver_name; // Driver name
+ pappl_sc_driver_data_t driver_data; // Driver data
+ time_t start_time; // Startup time
+ time_t config_time; // "scanner-config-change-time" value
+ time_t status_time; // Last time status was updated
+ pappl_job_t *processing_job; // Current scanning job, if any
+ int next_job_id; // Next "job-id" value
+ cups_array_t *links; // Web navigation links
+ # ifdef HAVE_MDNSRESPONDER
+ _pappl_srv_t dns_sd_http_ref, // DNS-SD HTTP service
+ _pappl_srv_t dns_sd_escl_ref, // DNS-SD eSCL service
+ DNSRecordRef dns_sd_escl_loc_ref, // DNS-SD LOC record for ESCL service
+ # elif defined(HAVE_AVAHI)
+ _pappl_srv_t dns_sd_ref; // DNS-SD services
+ # endif // HAVE_MDNSRESPONDER
+ unsigned char dns_sd_loc[16]; // DNS-SD LOC record data
+ bool dns_sd_collision; // Was there a name collision?
+ int dns_sd_serial; // DNS-SD serial number (for collisions)
+};
+
+//
+// Functions...
+//
+extern void _papplScannerCopyStateNoLock(pappl_scanner_t *scanner, ipp_tag_t group_tag, ipp_t *ipp, pappl_client_t *client, cups_array_t *ra) _PAPPL_PRIVATE;
+extern const char *_papplScannerReasonString(pappl_sreason_t reason) _PAPPL_PRIVATE;
+
+extern void _papplScannerDelete(pappl_scanner_t *scanner) _PAPPL_PRIVATE;
+extern void _papplScannerInitDriverData(pappl_scanner_t *scanner, pappl_sc_driver_data_t *d) _PAPPL_PRIVATE;
+extern bool _papplScannerIsAuthorized(pappl_client_t *client) _PAPPL_PRIVATE; // Implement in scan-escl.c
+extern void _papplScannerProcessESCL(pappl_client_t *client) _PAPPL_PRIVATE;// Implement in scan-escl.c
+extern bool _papplScannerRegisterDNSSDNoLock(pappl_scanner_t *scanner) _PAPPL_PRIVATE; // Implement with reference to _papplPrinterRegisterDNSSDNoLock
+extern void _papplScannerUnregisterDNSSDNoLock(pappl_scanner_t *scanner) _PAPPL_PRIVATE; // Implement with reference to _papplPrinterUnregisterDNSSDNoLock
+
+extern const char *_papplScannerColorModeString(pappl_sc_color_mode_t value) _PAPPL_PRIVATE;
+extern pappl_sc_color_mode_t _papplScannerColorModeValue(const char *value) _PAPPL_PRIVATE;
+
+extern const char *_papplScannerReasonString(pappl_sreason_t value) _PAPPL_PRIVATE;
+extern pappl_sreason_t _papplScannerReasonValue(const char *value) _PAPPL_PRIVATE;
+
+extern void _papplScannerWebConfig(pappl_client_t *client, pappl_scanner_t *scanner) _PAPPL_PRIVATE;
+extern void _papplScannerWebConfigFinalize(pappl_scanner_t *scanner, cups_len_t num_form, cups_option_t *form) _PAPPL_PRIVATE;
+extern void _papplScannerWebDefaults(pappl_client_t *client, pappl_scanner_t *scanner) _PAPPL_PRIVATE;
+extern void _papplScannerWebDelete(pappl_client_t *client, pappl_scanner_t *scanner) _PAPPL_PRIVATE;
+extern void _papplScannerWebHome(pappl_client_t *client, pappl_scanner_t *scanner) _PAPPL_PRIVATE;
+extern void _papplSannerWebIteratorCallback(pappl_scanner_t *scanner, pappl_client_t *client) _PAPPL_PRIVATE; // Check
+extern void _papplScannerWebJobs(pappl_client_t *client, pappl_scanner_t *scanner) _PAPPL_PRIVATE; // Check
+extern void _papplScannerWebMedia(pappl_client_t *client, pappl_scanner_t *printer) _PAPPL_PRIVATE; //Check
+
+
+#endif // !_PAPPL_SCANNER_PRIVATE_H_
diff --git a/pappl/scanner-webif.c b/pappl/scanner-webif.c
new file mode 100644
index 00000000..d4111f3f
--- /dev/null
+++ b/pappl/scanner-webif.c
@@ -0,0 +1,636 @@
+//
+// Scanner web interface functions for the Scanner Application Framework
+//
+// Copyright © 2019-2024 by Michael R Sweet.
+// Copyright © 2010-2019 by Apple Inc.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "pappl-private.h"
+
+//
+// Local functions...
+//
+
+// Duplicate of the local functions in scanner-driver.c. TODO:Remove Later
+extern const char *InputSourceString(pappl_sc_input_source_t value) _PAPPL_PRIVATE;
+extern const char *ResolutionString(int resolution) _PAPPL_PRIVATE;
+
+// Converts resolution to string
+const char *ResolutionString(int resolution)
+{
+ static char res_str[32];
+ snprintf(res_str, sizeof(res_str), "%d DPI", resolution);
+ return res_str;
+}
+
+// Converts input source to string
+const char *InputSourceString(pappl_sc_input_source_t value)
+{
+ switch (value)
+ {
+ case PAPPL_FLATBED:
+ return "Flatbed";
+ case PAPPL_ADF:
+ return "ADF";
+ default:
+ return "Unknown";
+ }
+}
+
+//
+// '_papplScannerWebConfig()' - Show the scanner configuration web page.
+//
+
+void
+_papplScannerWebConfig(
+ pappl_client_t *client, // I - Client
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ const char *status = NULL; // Status message, if any
+ char dns_sd_name[64], // DNS-SD name
+ location[128], // Location
+ geo_location[128], // Geo-location latitude
+ organization[128]; // Organization
+ pappl_contact_t contact; // Contact info
+
+
+ if (!papplClientHTMLAuthorize(client))
+ return;
+
+ if (client->operation == HTTP_STATE_POST)
+ {
+ cups_len_t num_form = 0; // Number of form variable
+ cups_option_t *form = NULL; // Form variables
+
+ if ((num_form = (cups_len_t)papplClientGetForm(client, &form)) == 0)
+ {
+ status = _PAPPL_LOC("Invalid form data.");
+ }
+ else if (!papplClientIsValidForm(client, (int)num_form, form))
+ {
+ status = _PAPPL_LOC("Invalid form submission.");
+ }
+ else
+ {
+ _papplScannerWebConfigFinalize(scanner, num_form, form);
+ status = _PAPPL_LOC("Changes saved.");
+ }
+
+ cupsFreeOptions(num_form, form);
+ }
+
+ papplClientHTMLScannerHeader(client, scanner, _PAPPL_LOC("Configuration"), 0, NULL, NULL);
+ if (status)
+ papplClientHTMLPrintf(client, "%s
\n", papplClientGetLocString(client, status));
+
+ _papplClientHTMLInfo(client, true, papplScannerGetDNSSDName(scanner, dns_sd_name, sizeof(dns_sd_name)), papplScannerGetLocation(scanner, location, sizeof(location)), papplScannerGetGeoLocation(scanner, geo_location, sizeof(geo_location)), papplScannerGetOrganization(scanner, organization, sizeof(organization)), NULL , papplScannerGetContact(scanner, &contact));
+
+ papplClientHTMLScannerFooter(client);
+}
+
+
+//
+// '_papplScannerWebConfigFinalize()' - Save the changes to the scanner configuration.
+//
+
+void
+_papplScannerWebConfigFinalize(
+ pappl_scanner_t *scanner, // I - Scanner
+ cups_len_t num_form, // I - Number of form variables
+ cups_option_t *form) // I - Form variables
+{
+ const char *value, // Form value
+ *geo_lat, // Geo-location latitude
+ *geo_lon, // Geo-location longitude
+ *contact_name, // Contact name
+ *contact_email, // Contact email
+ *contact_tel; // Contact telephone number
+
+
+ if ((value = cupsGetOption("dns_sd_name", num_form, form)) != NULL)
+ papplScannerSetDNSSDName(scanner, *value ? value : NULL);
+
+ if ((value = cupsGetOption("location", num_form, form)) != NULL)
+ papplScannerSetLocation(scanner, *value ? value : NULL);
+
+ geo_lat = cupsGetOption("geo_location_lat", num_form, form);
+ geo_lon = cupsGetOption("geo_location_lon", num_form, form);
+ if (geo_lat && geo_lon)
+ {
+ char uri[1024]; // "geo:" URI
+
+ if (*geo_lat && *geo_lon)
+ {
+ snprintf(uri, sizeof(uri), "geo:%g,%g", strtod(geo_lat, NULL), strtod(geo_lon, NULL));
+ papplScannerSetGeoLocation(scanner, uri);
+ }
+ else
+ papplScannerSetGeoLocation(scanner, NULL);
+ }
+
+ if ((value = cupsGetOption("organization", num_form, form)) != NULL)
+ papplScannerSetOrganization(scanner, *value ? value : NULL);
+
+ contact_name = cupsGetOption("contact_name", num_form, form);
+ contact_email = cupsGetOption("contact_email", num_form, form);
+ contact_tel = cupsGetOption("contact_telephone", num_form, form);
+ if (contact_name || contact_email || contact_tel)
+ {
+ pappl_contact_t contact; // Contact info
+
+ memset(&contact, 0, sizeof(contact));
+
+ if (contact_name)
+ papplCopyString(contact.name, contact_name, sizeof(contact.name));
+ if (contact_email)
+ papplCopyString(contact.email, contact_email, sizeof(contact.email));
+ if (contact_tel)
+ papplCopyString(contact.telephone, contact_tel, sizeof(contact.telephone));
+
+ papplScannerSetContact(scanner, &contact);
+ }
+}
+
+//
+// '_papplScannerWebDefaults()' - Show the scanner defaults web page.
+//
+
+void
+_papplScannerWebDefaults(
+ pappl_client_t *client, // I - Client
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ pappl_sc_driver_data_t data;
+ const char *status = NULL;
+
+ if (!papplClientHTMLAuthorize(client))
+ return;
+
+ papplScannerGetDriverData(scanner, &data);
+
+ if (client->operation == HTTP_STATE_POST)
+ {
+ cups_len_t num_form = 0;
+ cups_option_t *form = NULL;
+ int num_vendor = 0;
+ cups_option_t *vendor = NULL;
+
+ if ((num_form = (cups_len_t)papplClientGetForm(client, &form)) == 0)
+ {
+ status = _PAPPL_LOC("Invalid form data.");
+ }
+ else if (!papplClientIsValidForm(client, (int)num_form, form))
+ {
+ status = _PAPPL_LOC("Invalid form submission.");
+ }
+ else
+ {
+ const char *value;
+ char *end;
+
+ if ((value = cupsGetOption("document-format", num_form, form)) != NULL)
+ {
+ strncpy((char *)data.default_document_format, value, sizeof(data.default_document_format) - 1);
+ }
+
+ if ((value = cupsGetOption("color-mode", num_form, form)) != NULL)
+ {
+ data.default_color_mode = (pappl_sc_color_mode_t)_papplColorModeValue(value);
+ }
+
+ if ((value = cupsGetOption("resolution", num_form, form)) != NULL)
+ {
+ data.default_resolution = (int)strtol(value, &end, 10);
+
+ if (errno == ERANGE || *end || data.default_resolution < 0)
+ data.default_resolution = data.default_resolution;
+ }
+
+ if ((value = cupsGetOption("input-source", num_form, form)) != NULL)
+ {
+ for (int i = 0; i < PAPPL_MAX_SOURCES; i++)
+ {
+ if (!strcmp(InputSourceString(data.input_sources_supported[i]), value))
+ {
+ data.default_input_source = data.input_sources_supported[i];
+ break;
+ }
+ }
+ }
+
+ if ((value = cupsGetOption("duplex", num_form, form)) != NULL)
+ {
+ data.duplex_supported = !strcmp(value, "true");
+ }
+
+ if ((value = cupsGetOption("intent", num_form, form)) != NULL)
+ {
+ strncpy((char *)data.default_intent, value, sizeof(data.default_intent) - 1);
+ }
+
+ if ((value = cupsGetOption("scan-area-width", num_form, form)) != NULL)
+ {
+ data.default_scan_area[0] = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("scan-area-height", num_form, form)) != NULL)
+ {
+ data.default_scan_area[1] = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("brightness", num_form, form)) != NULL)
+ {
+ data.adjustments.brightness = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("contrast", num_form, form)) != NULL)
+ {
+ data.adjustments.contrast = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("gamma", num_form, form)) != NULL)
+ {
+ data.adjustments.gamma = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("threshold", num_form, form)) != NULL)
+ {
+ data.adjustments.threshold = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("saturation", num_form, form)) != NULL)
+ {
+ data.adjustments.saturation = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("sharpness", num_form, form)) != NULL)
+ {
+ data.adjustments.sharpness = (int)strtol(value, &end, 10);
+ }
+
+ if ((value = cupsGetOption("compression-supported", num_form, form)) != NULL)
+ {
+ data.compression_supported = !strcmp(value, "true");
+ }
+
+ if ((value = cupsGetOption("noise-removal-supported", num_form, form)) != NULL)
+ {
+ data.noise_removal_supported = !strcmp(value, "true");
+ }
+
+ if ((value = cupsGetOption("sharpening-supported", num_form, form)) != NULL)
+ {
+ data.sharpening_supported = !strcmp(value, "true");
+ }
+
+ if ((value = cupsGetOption("binary-rendering-supported", num_form, form)) != NULL)
+ {
+ data.binary_rendering_supported = !strcmp(value, "true");
+ }
+
+ if ((value = cupsGetOption("blank-page-removal-supported", num_form, form)) != NULL)
+ {
+ data.blank_page_removal_supported = !strcmp(value, "true");
+ }
+
+ if (papplScannerSetDriverDefaults(scanner, &data))
+ status = _PAPPL_LOC("Changes saved.");
+ else
+ status = _PAPPL_LOC("Bad scanner defaults.");
+
+ cupsFreeOptions((cups_len_t)num_vendor, vendor);
+ }
+
+ cupsFreeOptions(num_form, form);
+ }
+
+ papplClientHTMLScannerHeader(client, scanner, _PAPPL_LOC("Scanning Defaults"), 0, NULL, NULL);
+ if (status)
+ papplClientHTMLPrintf(client, "%s
\n", papplClientGetLocString(client, status));
+
+ papplClientHTMLStartForm(client, client->uri, false);
+
+ papplClientHTMLPuts(client,
+ " \n"
+ " \n");
+
+ papplClientHTMLScannerFooter(client);
+}
+
+
+//
+// '_papplScannerWebDelete()' - Show the scanner delete confirmation web page.
+//
+
+void
+_papplScannerWebDelete(
+ pappl_client_t *client, // I - Client
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ const char *status = NULL; // Status message, if any
+
+
+ if (!papplClientHTMLAuthorize(client))
+ return;
+
+ if (client->operation == HTTP_STATE_POST)
+ {
+ cups_len_t num_form = 0; // Number of form variables
+ cups_option_t *form = NULL; // Form variables
+
+ if ((num_form = (cups_len_t)papplClientGetForm(client, &form)) == 0)
+ {
+ status = _PAPPL_LOC("Invalid form data.");
+ }
+ else if (!papplClientIsValidForm(client, (int)num_form, form))
+ {
+ status = _PAPPL_LOC("Invalid form submission.");
+ }
+ else if (scanner->processing_job)
+ {
+ // Scanner is processing a job...
+ status = _PAPPL_LOC("Scanner is currently active.");
+ }
+ else
+ {
+ if (!papplScannerIsDeleted(scanner))
+ {
+ papplScannerDelete(scanner);
+ scanner = NULL;
+ }
+
+ papplClientRespondRedirect(client, HTTP_STATUS_FOUND, "/");
+ cupsFreeOptions(num_form, form);
+ return;
+ }
+
+ cupsFreeOptions(num_form, form);
+ }
+
+ papplClientHTMLScannerHeader(client, scanner, _PAPPL_LOC("Delete Scanner"), 0, NULL, NULL);
+
+ if (status)
+ papplClientHTMLPrintf(client, " %s
\n", papplClientGetLocString(client, status));
+
+ papplClientHTMLStartForm(client, client->uri, false);
+ papplClientHTMLPrintf(client," ", papplClientGetLocString(client, _PAPPL_LOC("Confirm Delete Scanner")));
+
+ papplClientHTMLFooter(client);
+}
+
+//
+// '_papplScannerWebHome()' - Show the scanner home page.
+//
+
+void
+_papplScannerWebHome(
+ pappl_client_t *client, // I - Client
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ const char *status = NULL; // Status text, if any
+ escl_sstate_t scanner_state; // Scanner state
+ char edit_path[1024]; // Edit configuration URL
+ // Job index and limit not required as of now
+ char dns_sd_name[64], // Scanner DNS-SD name
+ location[128], // Scanner location
+ geo_location[128], // Scanner geo-location
+ organization[256]; // Scanner organization
+ pappl_contact_t contact; // Scanner contact
+
+ // Save current scanner state...
+ scanner_state = scanner->state;
+
+ // Handle POSTs to perform scanner actions...
+ if (client->operation == HTTP_STATE_POST)
+ {
+ cups_len_t num_form = 0; // Number of form variables
+ cups_option_t *form = NULL; // Form variables
+ const char *action; // Form action
+
+ if ((num_form = (cups_len_t)papplClientGetForm(client, &form)) == 0)
+ {
+ status = _PAPPL_LOC("Invalid form data.");
+ }
+ else if (!papplClientIsValidForm(client, (int)num_form, form))
+ {
+ status = _PAPPL_LOC("Invalid form submission.");
+ }
+ else if ((action = cupsGetOption("action", num_form, form)) == NULL)
+ {
+ status = _PAPPL_LOC("Missing action.");
+ }
+ else
+ {
+ // Handle different actions (e.g., pause, resume, identify, etc.)
+ if (!strcmp(action, "identify-scanner"))
+ {
+ if (scanner->driver_data.identify_cb)
+ {
+ (scanner->driver_data.identify_cb)(scanner, scanner->driver_data.identify_supported, "Hello.");
+ status = _PAPPL_LOC("Scanner identified.");
+ }
+ else
+ {
+ status = _PAPPL_LOC("Unable to identify scanner.");
+ }
+ }
+ else if (!strcmp(action, "resume-scanner"))
+ {
+ papplScannerResume(scanner);
+ scanner->state = ESCL_SSTATE_IDLE;
+ status = _PAPPL_LOC("Scanner resuming.");
+ }
+ else if (!strcmp(action, "set-as-default"))
+ {
+ papplSystemSetDefaultScannerID(scanner->system, scanner->scanner_id);
+ status = _PAPPL_LOC("Default scanner set.");
+ }
+ else
+ {
+ status = _PAPPL_LOC("Unknown action.");
+ }
+ }
+ cupsFreeOptions(num_form, form);
+ }
+
+
+ // Show status...
+ papplClientHTMLScannerHeader(client, scanner, NULL, scanner_state == ESCL_SSTATE_PROCESSING ? 10 : 0, NULL, NULL);
+
+ papplClientHTMLPuts(client,
+ " \n"
+ "
\n");
+
+
+ if (status)
+ papplClientHTMLPrintf(client, "
%s
\n", papplClientGetLocString(client, status));
+
+ snprintf(edit_path, sizeof(edit_path), "%s/config", scanner->uriname);
+ papplClientHTMLPrintf(client, "
\n", papplClientGetLocString(client, _PAPPL_LOC("Configuration")), _papplClientGetAuthWebScheme(client), client->host_field, client->host_port, edit_path, papplClientGetLocString(client, _PAPPL_LOC("Change")));
+
+ // Display scanner information and links
+ _papplClientHTMLInfo(client, false,
+ papplScannerGetDNSSDName(scanner, dns_sd_name, sizeof(dns_sd_name)),
+ papplScannerGetLocation(scanner, location, sizeof(location)),
+ papplScannerGetGeoLocation(scanner, geo_location, sizeof(geo_location)),
+ papplScannerGetOrganization(scanner, organization, sizeof(organization)),
+ NULL,
+ papplScannerGetContact(scanner, &contact));
+
+ _papplClientHTMLPutLinks(client, scanner->links, PAPPL_LOPTIONS_CONFIGURATION);
+
+ // Display scanner state
+ papplClientHTMLPrintf(client,
+ "
\n"
+ "
\n"
+ "
%s \n", papplClientGetLocString(client, _PAPPL_LOC("Scanner Status")));
+
+ if (scanner->state == ESCL_SSTATE_PROCESSING)
+ {
+ papplClientHTMLPrintf(client, "
%s
\n", papplClientGetLocString(client, _PAPPL_LOC("Processing")));
+ }
+ else
+ {
+ papplClientHTMLPrintf(client, "
%s
\n", papplClientGetLocString(client, _PAPPL_LOC("Idle")));
+ }
+
+ _papplClientHTMLPutLinks(client, scanner->links, PAPPL_LOPTIONS_JOB);
+
+ papplClientHTMLScannerFooter(client);
+
+ // Optional TODO: Add functions to show all completed jobs, right now we only show scanner status and configuration
+
+}
diff --git a/pappl/scanner.c b/pappl/scanner.c
new file mode 100644
index 00000000..d15c9062
--- /dev/null
+++ b/pappl/scanner.c
@@ -0,0 +1,335 @@
+//
+// scanner object for the Scanner Application Framework
+//
+// Copyright © 2019-2024 by Michael R Sweet.
+// Copyright © 2010-2019 by Apple Inc.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#include "pappl-private.h"
+#include "scanner-private.h"
+#include "scanner.h"
+#include "system-private.h"
+
+//
+// 'papplScannerCreate()' - Create a new scanner.
+//
+// This function creates a new scanner (service) on the specified system. The
+// "scanner_id" argument specifies a positive integer identifier that is
+// unique to the system. If you specify a value of `0`, a new identifier will
+// be assigned.
+//
+// The "scanner_name" argument specifies a human-readable name for the scanner.
+//
+// The "driver_name" argument specifies a named driver for the scanner.
+//
+// The "device_id" and "device_uri" arguments specify the device ID
+// and device URI strings for the scanner.
+//
+// On error, this function sets the `errno` variable to one of the following
+// values:
+//
+// - `EEXIST`: A scanner with the specified name already exists.
+// - `EINVAL`: Bad values for the arguments were specified.
+// - `EIO`: The driver callback failed.
+// - `ENOENT`: No driver callback has been set.
+// - `ENOMEM`: Ran out of memory.
+//
+
+
+pappl_scanner_t * // O - Scanner or `NULL` on error
+papplScannerCreate(
+ pappl_system_t *system, // I - System
+ int scanner_id, // I - scanner-id value or `0` for new
+ const char *scanner_name, // I - Human-readable scanner name
+ const char *driver_name, // I - Driver name
+ const char *device_id, // I - IEEE-1284 device ID
+ const char *device_uri) // I - Device URI
+{
+ pappl_scanner_t *scanner; // Scanner
+ pappl_sc_driver_data_t driver_data; // Driver data
+ char resource[1024], // Resource path
+ *resptr, // Pointer into resource path
+ uuid[128]; // scanner-uuid
+ char path[256]; // Path to resource
+
+ // Range check input...
+ if (!system || !scanner_name || !driver_name || !device_uri)
+ {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (!system->driver_cb)
+ {
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "No driver callback set, unable to add scanner.");
+ errno = ENOENT;
+ return (NULL);
+ }
+
+ // Prepare URI values for the scanner attributes...
+ if (system->options & PAPPL_SOPTIONS_MULTI_QUEUE)
+ {
+ // Make sure scanner names that start with a digit have a resource path
+ // containing an underscore...
+ if (isdigit(*scanner_name & 255))
+ snprintf(resource, sizeof(resource), "/escl/scan/_%s", scanner_name);
+ else
+ snprintf(resource, sizeof(resource), "/escl/scan/%s", scanner_name);
+
+ // Convert URL reserved characters to underscore...
+ for (resptr = resource + 11; *resptr; resptr ++)
+ {
+ if ((*resptr & 255) <= ' ' || strchr("\177/\\\'\"?#", *resptr))
+ *resptr = '_';
+ }
+
+ // Eliminate duplicate and trailing underscores...
+ resptr = resource + 11;
+ while (*resptr)
+ {
+ if (resptr[0] == '_' && resptr[1] == '_')
+ memmove(resptr, resptr + 1, strlen(resptr)); // Duplicate underscores
+ else if (resptr[0] == '_' && !resptr[1])
+ *resptr = '\0'; // Trailing underscore
+ else
+ resptr ++;
+ }
+ }
+ else
+ {
+ papplCopyString(resource, "/escl/scan", sizeof(resource));
+ }
+
+ // Make sure the scanner doesn't already exist...
+ if ((scanner = papplSystemFindScanner(system, resource, 0, NULL)) != NULL)
+ {
+ int n; // Current instance number
+ char temp[1024]; // Temporary resource path
+
+ if (!strcmp(scanner_name, scanner->name))
+ {
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Scanner '%s' already exists.", scanner_name);
+ errno = EEXIST;
+ return (NULL);
+ }
+
+ for (n = 2; n < 10; n ++)
+ {
+ snprintf(temp, sizeof(temp), "%s_%d", resource, n);
+ if (!papplSystemFindScanner(system, temp, 0, NULL))
+ break;
+ }
+
+ if (n >= 10)
+ {
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Scanner '%s' name conflicts with existing scanner.", scanner_name);
+ errno = EEXIST;
+ return (NULL);
+ }
+
+ papplCopyString(resource, temp, sizeof(resource));
+ }
+
+ // Allocate memory for the scanner...
+ if ((scanner = calloc(1, sizeof(pappl_scanner_t))) == NULL)
+ {
+ papplLog(system, PAPPL_LOGLEVEL_ERROR, "Unable to allocate memory for scanner: %s", strerror(errno));
+ return (NULL);
+ }
+
+ papplLog(system, PAPPL_LOGLEVEL_INFO, "Scanner '%s' at resource path '%s'.", scanner_name, resource);
+
+ _papplSystemMakeUUID(system, scanner_name, 0, uuid, sizeof(uuid));
+
+ // Initialize scanner structure and attributes...
+ pthread_rwlock_init(&scanner->rwlock, NULL);
+
+ scanner->system = system;
+ scanner->name = strdup(scanner_name);
+ scanner->dns_sd_name = strdup(scanner_name);
+ scanner->resource = strdup(resource);
+ scanner->resourcelen = strlen(resource);
+ scanner->uriname = scanner->resource + 10; // Skip "/escl/scan" in resource
+ scanner->device_id = device_id ? strdup(device_id) : NULL;
+ scanner->device_uri = strdup(device_uri);
+ scanner->driver_name = strdup(driver_name);
+ scanner->uuid = strdup(uuid);
+ scanner->start_time = time(NULL);
+ scanner->config_time = scanner->start_time;
+ scanner->state = ESCL_SSTATE_IDLE;
+ scanner->state_reasons = PAPPL_SREASON_NONE;
+ scanner->state_time = scanner->start_time;
+ scanner->is_accepting = true;
+ scanner->next_job_id = 1;
+ scanner->processing_job = NULL; // Initialize to NULL
+ scanner->device = NULL; // Initialize to NULL
+ scanner->device_in_use = false; // Initialize to false
+
+ // Check for memory allocation failures
+ if (!scanner->name || !scanner->dns_sd_name || !scanner->resource || (device_id && !scanner->device_id) || !scanner->device_uri || !scanner->driver_name || !scanner->uuid)
+ {
+ // Failed to allocate one of the required members...
+ _papplScannerDelete(scanner);
+ return (NULL);
+ }
+
+ // If the driver is "auto", figure out the proper driver name...
+ if (!strcmp(driver_name, "auto") && system->autoadd_sc_cb)
+ {
+ // If device_id is NULL, try to look it up...
+ if (!scanner->device_id && strncmp(device_uri, "file://", 7))
+ {
+ pappl_device_t *device; // Connection to scanner
+
+ if ((device = papplDeviceOpen(device_uri, "auto", papplLogDevice, system)) != NULL)
+ {
+ char new_id[1024]; // New device ID
+
+ if (papplDeviceGetID(device, new_id, sizeof(new_id)))
+ scanner->device_id = strdup(new_id);
+
+ papplDeviceClose(device);
+ }
+ }
+
+ if ((driver_name = (system->autoadd_sc_cb)(scanner_name, device_uri, scanner->device_id, system->sc_driver_cbdata)) == NULL)
+ {
+ errno = EIO;
+ _papplScannerDelete(scanner);
+ return (NULL);
+ }
+ }
+
+
+ _papplScannerInitDriverData(scanner,&driver_data);
+
+ if (!(system->driver_sc_cb)(system, driver_name, device_uri, device_id, &driver_data, system->sc_driver_cbdata))
+ {
+ errno = EIO;
+ _papplScannerDelete(scanner);
+ return (NULL);
+ }
+
+ papplScannerSetDriverData(scanner, &driver_data);
+
+ // Add the scanner to the system...
+ _papplSystemAddScanner(system, scanner, scanner_id);
+
+ // Do any post-creation work...
+ if (system->create_sc_cb)
+ (system->create_sc_cb)(scanner, system->sc_driver_cbdata);
+
+ // Add icons...
+ _papplSystemAddScannerIcons(system, scanner);
+
+ // Add web pages, if any...
+ if (system->options & PAPPL_SOPTIONS_WEB_INTERFACE)
+ {
+ snprintf(path, sizeof(path), "%s/", scanner->uriname);
+ papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplScannerWebHome, scanner);
+
+ snprintf(path, sizeof(path), "%s/delete", scanner->uriname);
+ papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplScannerWebDelete, scanner);
+
+ snprintf(path, sizeof(path), "%s/config", scanner->uriname);
+ papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplScannerWebConfig, scanner);
+
+ snprintf(path, sizeof(path), "%s/printing", scanner->uriname);
+ papplSystemAddResourceCallback(system, path, "text/html", (pappl_resource_cb_t)_papplScannerWebDefaults, scanner);
+
+ }
+
+ _papplSystemConfigChanged(system);
+
+ // Return it!
+ return (scanner);
+}
+
+//
+// '_papplScannerDelete()' - Free memory associated with a scanner.
+//
+
+void
+_papplScannerDelete(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ _pappl_resource_t *r; // Current resource
+ char prefix[1024]; // Prefix for scanner resources
+ size_t prefixlen; // Length of prefix
+
+
+ _papplRWLockWrite(scanner);
+ scanner->is_deleted = true;
+ _papplRWUnlock(scanner);
+
+ // Remove DNS-SD registrations...
+ _papplScannerUnregisterDNSSDNoLock(scanner);
+
+ // Remove scanner-specific resources...
+ snprintf(prefix, sizeof(prefix), "%s/", scanner->uriname);
+ prefixlen = strlen(prefix);
+
+ // Note: System writer lock is already held when calling cupsArrayRemove
+ // for the system's scanner object, so we don't need a separate lock here
+ // and can safely use cupsArrayGetFirst/Next...
+ _papplRWLockWrite(scanner->system);
+ for (r = (_pappl_resource_t *)cupsArrayGetFirst(scanner->system->resources); r; r = (_pappl_resource_t *)cupsArrayGetNext(scanner->system->resources))
+ {
+ if (r->cbdata == scanner || !strncmp(r->path, prefix, prefixlen))
+ cupsArrayRemove(scanner->system->resources, r);
+ }
+ _papplRWUnlock(scanner->system);
+
+ // If applicable, call the delete function...
+ if (scanner->driver_data.sc_delete_cb)
+ (scanner->driver_data.sc_delete_cb)(scanner, &scanner->driver_data);
+
+ // Free memory...
+ free(scanner->name);
+ free(scanner->dns_sd_name);
+ free(scanner->location);
+ free(scanner->geo_location);
+ free(scanner->organization);
+ free(scanner->resource);
+ free(scanner->device_id);
+ free(scanner->device_uri);
+ free(scanner->driver_name);
+ free(scanner->uuid);
+
+ cupsArrayDelete(scanner->links);
+
+ pthread_rwlock_destroy(&scanner->rwlock);
+
+ free(scanner);
+}
+
+
+//
+// 'papplScannerDelete()' - Delete a scanner.
+//
+// This function deletes a scanner from a system, freeing all memory and
+// canceling all jobs as needed.
+//
+
+void
+papplScannerDelete(
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ pappl_system_t *system = scanner->system;
+ // System
+
+ // Deliver delete event...
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED | PAPPL_EVENT_SYSTEM_CONFIG_CHANGED, NULL);
+
+ // Remove the scanner from the system object...
+ _papplRWLockWrite(system);
+ cupsArrayRemove(system->scanners, scanner);
+ _papplRWUnlock(system);
+
+ _papplScannerDelete(scanner);
+
+ _papplSystemConfigChanged(system);
+}
diff --git a/pappl/scanner.h b/pappl/scanner.h
new file mode 100644
index 00000000..b39c99cb
--- /dev/null
+++ b/pappl/scanner.h
@@ -0,0 +1,236 @@
+//
+// Public scanner header file for the Scanner Application Framework
+//
+// Copyright © 2019-2024 by Michael R Sweet.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+#ifndef _PAPPL_SCANNER_H_
+# define _PAPPL_SCANNER_H_
+# include "base.h"
+#include "system.h"
+# ifdef __cplusplus
+extern "C" {
+# endif // __cplusplus
+
+//
+// Limits...
+//
+#define PAPPL_MAX_FORMATS 5 // Most scanners support a variety of document formats such as JPEG, PDF, TIFF, PNG, and BMP.
+#define PAPPL_MAX_COLOR_MODES 3 // Most scanners support a few color modes: Black and White, Grayscale, Color
+#define PAPPL_MAX_SOURCES 2 // MOST scanners offer two input sources: Flatbed and ADF
+#define PAPPL_MAX_COLOR_SPACES 2 // Common color spaces like sRGB and AdobeRGB
+#define PAPPL_MAX_MEDIA_TYPES 5 // Various media types like Plain, Photo, Card, etc.
+#define MAX_RESOLUTIONS 5 // The number of resolutions supported by the scanner
+
+//
+// Constants...
+//
+
+// This defines the overall state of the scanner. It describes the scanner's operational state,
+// focusing on what the scanner is currently doing or its readiness to perform tasks.
+// The states are mutually exclusive, meaning the scanner can be in only one of these states at a time.
+typedef enum
+{
+ ESCL_SSTATE_IDLE, // Scanner is idle
+ ESCL_SSTATE_PROCESSING, // Scanner is busy with some job or activity
+ ESCL_SSTATE_TESTING, // Scanner is calibrating, preparing the unit
+ ESCL_SSTATE_STOPPED, // Scanner stopped due to an error condition
+ ESCL_SSTATE_DOWN // Scanner is unavailable
+} escl_sstate_t;
+
+// This defines specific reasons for the scanner's state. These reasons can provide more detailed
+// information about why the scanner is in its current state. Multiple reasons can be combined using
+// bitwise operations, which means the scanner can have multiple reasons for being in a particular state at the same time.
+typedef enum
+{
+ PAPPL_SREASON_NONE = 0x0000, // 'none', no error, scanner is ready
+ PAPPL_SREASON_IDLE = 0x0001, // 'idle', scanner is idle
+ PAPPL_SREASON_PROCESSING = 0x0002, // 'processing', scanner is currently processing a job
+ PAPPL_SREASON_TESTING = 0x0004, // 'testing', scanner is in calibration or preparation mode
+ PAPPL_SREASON_STOPPED = 0x0008, // 'stopped', an error has occurred and the scanner has stopped
+ PAPPL_SREASON_DOWN = 0x0010, // 'down', the scanner is unavailable
+} pappl_sreason_t;
+
+// Define color modes supported by the scanner
+typedef enum {
+ PAPPL_BLACKANDWHITE1, // For black and white scans
+ PAPPL_GRAYSCALE8, // For grayscale scans
+ PAPPL_RGB24 // For full color scans
+} pappl_sc_color_mode_t;
+
+// Define input sources for the scanner
+typedef enum {
+ PAPPL_FLATBED, // For flatbed scanners
+ PAPPL_ADF, // For automatic dopappl_sc_input_source_t;cument feeder
+} pappl_sc_input_source_t;
+
+typedef unsigned pappl_identify_sc_actions_t; // eSCL actions for identifying the scanner
+
+//
+// Structures
+//
+
+typedef struct pappl_icon_sc_s // Scanner PNG icon structure
+{
+ char filename[256]; // External filename, if any
+ const void *data; // PNG icon data, if any
+ size_t datalen; // Size of PNG icon data
+} pappl_icon_sc_t;
+
+typedef struct pappl_sc_options_s // Provides scan job options for the user after we have fetched the scanner driver data
+{
+ char document_format[64]; // Desired output format (JPEG, PDF, TIFF, PNG, BMP)
+ pappl_sc_color_mode_t color_mode; // Color mode for the scan
+ int resolution; // Scanning resolution in DPI
+ pappl_sc_input_source_t input_source; // Selected input source
+ bool duplex; // Duplex scanning option
+ char intent[64]; // Scan intent (e.g., Document, Photo, Preview, etc.)
+ struct {
+ int width; // Width of the scan area
+ int height; // Height of the scan area
+ int x_offset; // X offset for the scan area
+ int y_offset; // Y offset for the scan area
+ } scan_area;
+ struct {
+ int brightness; // Brightness adjustment
+ int contrast; // Contrast adjustment
+ int gamma; // Gamma adjustment
+ int threshold; // Threshold for black/white scans
+ int saturation; // Saturation adjustment
+ int sharpness; // Sharpness adjustment
+ } adjustments;
+ bool blank_page_removal; // Automatically detect and remove blank pages
+ unsigned num_pages; // Number of pages to scan (for ADF)
+ int compression_factor; // Compression factor for the scan
+ bool noise_removal; // Noise removal option
+ bool sharpening; // Sharpening option
+} pappl_sc_options_t;
+
+typedef struct pappl_sc_driver_data_s // Initially polling the scanner driver for capabilities and settings
+{
+ pappl_sc_identify_cb_t identify_cb; // Callback for identifying the scanner
+ pappl_sc_delete_cb_t sc_delete_cb; // Scanner deletion callback
+ pappl_sc_capabilities_cb_t capabilities_cb; // Callback for getting scanner capabilities
+ pappl_sc_job_create_cb_t job_create_cb; // Callback for creating a scan job
+ pappl_sc_job_delete_cb_t job_delete_cb; // Callback for deleting a scan job
+ pappl_sc_data_cb_t data_cb; // Callback for getting scan data
+ pappl_sc_status_cb_t status_cb; // Callback for getting scanner status
+ pappl_sc_job_complete_cb_t job_complete_cb; // Callback for completing a scan job
+ pappl_sc_job_cancel_cb_t job_cancel_cb; // Callback for cancelling a scan job
+ pappl_sc_buffer_info_cb_t buffer_info_cb; // Callback for getting buffer information
+ pappl_sc_image_info_cb_t image_info_cb; // Callback for getting image information
+
+ pappl_identify_sc_actions_t identify_default; // "identify-actions-default" values
+ pappl_identify_sc_actions_t identify_supported; // "identify-actions-supported" values
+ pappl_icon_sc_t icons[3]; // "scanner-icons" values
+
+ char make_and_model[128]; // Make and model of the scanner
+ const char *document_formats_supported[PAPPL_MAX_FORMATS]; // Supported document formats (JPEG, PDF, TIFF, PNG, BMP)
+ pappl_sc_color_mode_t color_modes_supported[PAPPL_MAX_COLOR_MODES]; // Supported color modes (BlackAndWhite1, Grayscale8, RGB24)
+ int resolutions[MAX_RESOLUTIONS]; // All optical resolutions in DPI
+ pappl_sc_input_source_t input_sources_supported[PAPPL_MAX_SOURCES]; // Supported input sources (Flatbed, ADF)
+ bool duplex_supported; // Duplex (double-sided) scanning support
+ const char *color_spaces_supported[PAPPL_MAX_COLOR_SPACES]; // Supported color spaces (sRGB, AdobeRGB)
+ int max_scan_area[2]; // Maximum scan area size (width, height)
+ const char *media_type_supported[PAPPL_MAX_MEDIA_TYPES]; // Types of media that can be scanned (Plain, Photo, Card)
+ int default_resolution; // Default scanning resolution
+ pappl_sc_color_mode_t default_color_mode; // Default color mode
+ pappl_sc_input_source_t default_input_source; // Default input source
+ int scan_region_supported[4]; // Supported scan regions (top, left, width, height)
+ const char *mandatory_intents[5]; // Mandatory intents supported by the scanner (e.g., Document, Photo, TextAndGraphic, Preview, BusinessCard)
+ const char *optional_intents[5]; // Optional intents supported by the scanner (e.g., Object, CustomIntent)
+
+ struct {
+ int brightness; // Brightness adjustment
+ int contrast; // Contrast adjustment
+ int gamma; // Gamma adjustment
+ int threshold; // Threshold for black/white scans
+ int saturation; // Saturation adjustment
+ int sharpness; // Sharpness adjustment
+ } adjustments;
+
+ bool compression_supported; // Whether compression is supported
+ bool noise_removal_supported; // Whether noise removal is supported
+ bool sharpening_supported; // Whether sharpness adjustment is supported
+ bool binary_rendering_supported; // Whether binary rendering is supported
+ bool blank_page_removal_supported; // Whether blank page removal is supported
+
+ const char *feed_direction_supported[2]; // Supported feed directions (e.g., LeftToRight, RightToLeft)
+ const char *default_document_format; // Default document format
+ const char *default_color_space; // Default color space
+ int default_scan_area[2]; // Default scan area (width, height)
+ const char *default_media_type; // Default media type
+ const char *default_intent; // Default intent
+} pappl_sc_driver_data_t;
+
+//
+// Functions...
+//
+
+extern void papplScannerAddLink(pappl_scanner_t *scanner, const char *label, const char *path_or_url, pappl_loptions_t options) _PAPPL_PUBLIC;
+
+extern void papplScannerCloseDevice(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern pappl_scanner_t *papplScannerCreate(pappl_system_t *system, int scanner_id, const char *scanner_name, const char *driver_name, const char *device_id, const char *device_uri) _PAPPL_PUBLIC;
+extern void papplScannerDelete(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern void papplScannerDisable(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern void papplScannerEnable(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern pappl_contact_t *papplScannerGetContact(pappl_scanner_t *scanner, pappl_contact_t *contact) _PAPPL_PUBLIC;
+extern const char *papplScannerGetDeviceID(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern const char *papplScannerGetDeviceURI(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern char *papplScannerGetDNSSDName(pappl_scanner_t *scanner, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+
+extern pappl_sc_driver_data_t *papplScannerGetDriverData(pappl_scanner_t *scanner, pappl_sc_driver_data_t *data) _PAPPL_PUBLIC;
+extern const char *papplScannerGetDriverName(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern char *papplScannerGetGeoLocation(pappl_scanner_t *scanner, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+extern int papplScannerGetID(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern char *papplScannerGetLocation(pappl_scanner_t *scanner, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+
+extern const char *papplScannerGetName(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern int papplScannerGetNextJobID(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern char *papplScannerGetOrganization(pappl_scanner_t *scanner, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+extern char *papplScannerGetOrganizationalUnit(pappl_scanner_t *scanner, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+
+extern char *papplScannerGetPath(pappl_scanner_t *scanner, const char *subpath, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
+
+extern pappl_sreason_t papplScannerGetReasons(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern escl_sstate_t papplScannerGetState(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern pappl_system_t *papplScannerGetSystem(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern void papplScannerHTMLFooter(pappl_client_t *client) _PAPPL_PUBLIC;
+extern void papplScannerHTMLHeader(pappl_client_t *client, const char *title, int refresh) _PAPPL_PUBLIC;
+
+extern bool papplScannerIsAcceptingJobs(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern bool papplScannerIsDeleted(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern pappl_device_t *papplScannerOpenDevice(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+extern int papplScannerOpenFile(pappl_scanner_t *scanner, char *fname, size_t fnamesize, const char *directory, const char *resname, const char *ext, const char *mode) _PAPPL_PUBLIC;
+
+extern void papplScannerPause(pappl_scanner_t *printer) _PAPPL_PUBLIC;
+extern void papplScannerRemoveLink(pappl_scanner_t *scanner, const char *label) _PAPPL_PUBLIC;
+extern void papplScannerResume(pappl_scanner_t *scanner) _PAPPL_PUBLIC;
+
+extern void papplScannerSetContact(pappl_scanner_t *scanner, pappl_contact_t *contact) _PAPPL_PUBLIC;
+extern void papplScannerSetDNSSDName(pappl_scanner_t *scanner, const char *value) _PAPPL_PUBLIC;
+
+extern bool papplScannerSetDriverData(pappl_scanner_t *scanner, pappl_sc_driver_data_t *data) _PAPPL_PUBLIC;
+extern bool papplScannerSetDriverDefaults(pappl_scanner_t *scanner, pappl_sc_driver_data_t *data) _PAPPL_PUBLIC;
+
+extern void papplScannerSetGeoLocation(pappl_scanner_t *scanner, const char *value) _PAPPL_PUBLIC;
+extern void papplScannerSetLocation(pappl_scanner_t *scanner, const char *value) _PAPPL_PUBLIC;
+extern void papplScannerSetNextJobID(pappl_scanner_t *scanner, int next_job_id) _PAPPL_PUBLIC;
+
+extern void papplScannerSetOrganization(pappl_scanner_t *scanner, const char *value) _PAPPL_PUBLIC;
+extern void papplScannerSetOrganizationalUnit(pappl_scanner_t *scanner, const char *value) _PAPPL_PUBLIC;
+extern void papplScannerSetReasons(pappl_scanner_t *scanner, pappl_sreason_t add, pappl_sreason_t remove) _PAPPL_PUBLIC;
+
+# ifdef __cplusplus
+}
+# endif // __cplusplus
+#endif // !_PAPPL_SCANNER_H_
diff --git a/pappl/subscription-private.h b/pappl/subscription-private.h
index a76b9382..855ad415 100644
--- a/pappl/subscription-private.h
+++ b/pappl/subscription-private.h
@@ -38,6 +38,7 @@ struct _pappl_subscription_s // Subscription data
# endif // DEBUG
pappl_event_t mask; // IPP "notify-events" bit field
pappl_printer_t *printer; // Printer, if any
+ pappl_scanner_t *scanner; // Scanner, if any
pappl_job_t *job; // Job, if any
ipp_t *attrs; // Attributes
char *language, // Language for notifications
diff --git a/pappl/subscription.h b/pappl/subscription.h
index 1182dd27..1312c32a 100644
--- a/pappl/subscription.h
+++ b/pappl/subscription.h
@@ -103,11 +103,20 @@ enum pappl_event_e // IPP "notify-events" bit values
// All 'printer-xxx' configuration events
PAPPL_EVENT_PRINTER_STATE_ALL = 0x001e0000,
// All 'printer-xxx' state events
- PAPPL_EVENT_ALL = 0x7fffffff // All events
+ PAPPL_EVENT_ALL = 0x7fffffff, // All events
+
+ // New scanner-specific events
+ PAPPL_EVENT_SCANNER_CONFIG_CHANGED = 0x80000000, // 'scanner-config-changed'
+ PAPPL_EVENT_SCANNER_STATE_CHANGED = 0x100000000, // 'scanner-state-changed'
+ PAPPL_EVENT_SCANNER_STOPPED = 0x600000000, // 'scanner-stopped',
+ PAPPL_EVENT_SCANNER_ALL = 0x180000000 // All 'scanner' events
+
};
-typedef unsigned int pappl_event_t; // Bitfield for IPP "notify-events" attribute
+typedef unsigned long long int pappl_event_t; // Bitfield for IPP/ESCL "notify-events" attribute
typedef void (*pappl_event_cb_t)(pappl_system_t *system, pappl_printer_t *printer, pappl_job_t *job, pappl_event_t event, void *data);
// System event callback
+typedef void (*pappl_scanner_event_cb_t)(pappl_system_t *system, pappl_scanner_t *scanner, pappl_job_t *job, pappl_event_t event, void *data);
+ // System scanner event callback
//
diff --git a/pappl/system-accessors.c b/pappl/system-accessors.c
index ed52aa61..2821169b 100644
--- a/pappl/system-accessors.c
+++ b/pappl/system-accessors.c
@@ -547,6 +547,28 @@ papplSystemGetDefaultPrinterID(
return (ret);
}
+//
+// 'papplSystemGetDefaultScannerID()' - Get the current "default-scanner-id" value.
+//
+// This function returns the positive integer identifier for the current
+// default scanner or `0` if there is no default scanner.
+//
+
+int // O - "default-scanner-id" value
+papplSystemGetDefaultScannerID(
+ pappl_system_t *system) // I - System
+{
+ int ret = 0; // Return value
+
+ if (system)
+ {
+ _papplRWLockRead(system);
+ ret = system->default_scanner_id;
+ _papplRWUnlock(system);
+ }
+ return (ret);
+}
+
//
// 'papplSystemGetDefaultPrintGroup()' - Get the default print group, if any.
@@ -1650,6 +1672,30 @@ papplSystemSetDefaultPrinterID(
}
}
+//
+// 'papplSystemSetDefaultScannerID()' - Set the "default-scanner-id" value.
+//
+// This function sets the default scanner using its unique positive integer
+// identifier.
+//
+
+void
+papplSystemSetDefaultScannerID(
+ pappl_system_t *system, // I - System
+ int default_scanner_id) // I - "default-scanner-id" value
+{
+ if (system)
+ {
+ _papplRWLockWrite(system);
+
+ system->default_scanner_id = default_scanner_id;
+
+ _papplSystemConfigChanged(system);
+
+ _papplRWUnlock(system);
+ }
+}
+
//
// 'papplSystemSetDefaultPrintGroup()' - Set the default print group.
@@ -1736,6 +1782,22 @@ papplSystemSetEventCallback(
}
}
+void
+papplSystemSetScanEventCallback(
+ pappl_system_t *system, // I - System
+ pappl_scanner_event_cb_t scan_event_cb, // I - Event callback function
+ void *scan_event_data) // I - Event callback data
+{
+ if (system && scan_event_cb)
+ {
+ _papplRWLockWrite(system);
+
+ system->scan_event_cb = scan_event_cb;
+ system->scan_event_data = scan_event_data;
+
+ _papplRWUnlock(system);
+ }
+}
//
// 'papplSystemSetFooterHTML()' - Set the footer HTML for the web interface.
@@ -2254,6 +2316,33 @@ papplSystemSetNextPrinterID(
}
}
+//
+// 'papplSystemSetNextScannerID()' - Set the next "scanner-id" value.
+//
+// This function sets the unique positive integer identifier that will be used
+// for the next scanner that is created. It is typically only called as part
+// of restoring the state of a system.
+//
+// > Note: The next scanner ID can only be set prior to calling
+// > @link papplSystemRun@.
+//
+
+void
+papplSystemSetNextScannerID(
+ pappl_system_t *system, // I - System
+ int next_scanner_id) // I - Next "scanner-id" value
+{
+ if (system && !system->is_running)
+ {
+ _papplRWLockWrite(system);
+
+ system->next_scanner_id = next_scanner_id;
+
+ _papplSystemConfigChanged(system);
+
+ _papplRWUnlock(system);
+ }
+}
//
// 'papplSystemSetOperationCallback()' - Set the IPP operation callback.
@@ -2410,6 +2499,54 @@ papplSystemSetPrinterDrivers(
}
}
+//
+// 'papplSystemSetScannerDrivers()' - Set the list of drivers and the driver
+// callbacks for scanners.
+//
+
+void
+papplSystemSetScannerDrivers(
+ pappl_system_t *system, // I - System
+ int num_drivers, // I - Number of drivers
+ pappl_sc_driver_t *drivers, // I - Scanner drivers
+ pappl_sc_identify_cb_t identify_cb, // I - Identify scanner callback or `NULL` if none
+ pappl_sc_create_cb_t create_cb, // I - Scanner creation callback or `NULL` if none
+ pappl_sc_driver_cb_t driver_cb, // I - Driver initialization callback
+ pappl_sc_delete_cb_t sc_delete_cb, // I - Scanner delete callback or `NULL` if none
+ pappl_sc_capabilities_cb_t capabilities_cb, // I - Scanner capabilities callback
+ pappl_sc_job_create_cb_t job_create_cb, // I - Job creation callback
+ pappl_sc_job_delete_cb_t job_delete_cb, // I - Job delete callback
+ pappl_sc_data_cb_t data_cb, // I - Data processing callback
+ pappl_sc_status_cb_t status_cb, // I - Status callback
+ pappl_sc_job_complete_cb_t job_complete_cb, // I - Job completion callback
+ pappl_sc_job_cancel_cb_t job_cancel_cb, // I - Job cancel callback
+ pappl_sc_buffer_info_cb_t buffer_info_cb, // I - Buffer information callback
+ pappl_sc_image_info_cb_t image_info_cb, // I - Image information callback
+ void *data) // I - Callback data
+{
+ if (system)
+ {
+ _papplRWLockWrite(system);
+
+ // Set the system's scanner-related fields
+ system->num_scanner_drivers = (cups_len_t)num_drivers;
+ system->scanner_drivers = drivers;
+ system->identify_cb = identify_cb;
+ system->sc_delete_cb = sc_delete_cb;
+ system->capabilities_cb = capabilities_cb;
+ system->job_create_cb = job_create_cb;
+ system->job_delete_cb = job_delete_cb;
+ system->data_cb = data_cb;
+ system->status_cb = status_cb;
+ system->job_complete_cb = job_complete_cb;
+ system->job_cancel_cb = job_cancel_cb;
+ system->buffer_info_cb = buffer_info_cb;
+ system->image_info_cb = image_info_cb;
+ system->sc_driver_cbdata = data;
+
+ _papplRWUnlock(system);
+ }
+}
//
// 'papplSystemSetSaveCallback()' - Set the save callback.
diff --git a/pappl/system-printer.c b/pappl/system-printer.c
index eba7d1bd..f8c80c6f 100644
--- a/pappl/system-printer.c
+++ b/pappl/system-printer.c
@@ -19,8 +19,42 @@
//
static int compare_printers(pappl_printer_t *a, pappl_printer_t *b);
+static int compare_scanners(pappl_scanner_t *a, pappl_scanner_t *b);
+//
+// '_papplSystemAddScanner()' - Add a scanner to the system object, creating the scanners array as needed.
+//
+
+void
+_papplSystemAddScanner(
+ pappl_system_t *system, // I - System
+ pappl_scanner_t *scanner, // I - Scanner
+ int scanner_id) // I - Scanner ID or `0` for new
+{
+ // Add the scanner to the system...
+ _papplRWLockWrite(system);
+
+ if (scanner_id)
+ scanner->scanner_id = scanner_id;
+ else
+ scanner->scanner_id = system->next_scanner_id ++;
+
+ if (!system->scanners)
+ system->scanners = cupsArrayNew((cups_array_cb_t)compare_scanners, /*cb_data*/NULL, /*hash_cb*/NULL, /*hash_size*/0, /*copy_cb*/NULL, /*free_cb*/NULL);
+
+ cupsArrayAdd(system->scanners, scanner);
+
+ if (!system->default_scanner_id)
+ system->default_scanner_id = scanner->scanner_id;
+
+ _papplRWUnlock(system);
+
+ _papplSystemConfigChanged(system);
+
+ papplSystemAddScannerEvent(scanner->system, scanner, NULL, PAPPL_EVENT_SCANNER_STATE_CHANGED, NULL);
+}
+
//
// '_papplSystemAddPrinter()' - Add a printer to the system object, creating the printers array as needed.
//
@@ -112,6 +146,62 @@ papplSystemCreatePrinters(
}
+//
+// 'papplSystemCreateScanners()' - Create newly discovered scanners.
+//
+// This function lists all devices specified by "types" and attempts to add any
+// new scanners that are found. The callback function "cb" is invoked for each
+// scanner that is added.
+//
+
+bool // O - `true` if printers were added, `false` otherwise
+papplSystemCreateScanners(
+ pappl_system_t *system, // I - System
+ pappl_devtype_t types, // I - Device types
+ pappl_sc_create_cb_t cb, // I - Callback function
+ void *cb_data) // I - Callback data
+{
+ bool ret = false; // Return value
+ cups_array_t *devices; // Device array
+ _pappl_dinfo_t *d; // Current device information
+
+ // List the devices...
+ devices = _papplDeviceInfoCreateArray();
+
+ papplDeviceList(types, (pappl_device_cb_t)_papplDeviceInfoCallback, devices, papplLogDevice, system);
+
+ // Loop through the devices to find new stuff...
+ for (d = (_pappl_dinfo_t *)cupsArrayGetFirst(devices); d; d = (_pappl_dinfo_t *)cupsArrayGetNext(devices))
+ {
+ pappl_scanner_t *scanner = NULL;// New scanner
+
+ // See if there is already a scanner for this device URI...
+ if (papplSystemFindScanner(system, NULL, 0, d->device_uri))
+ continue; // Scanner with this device URI exists
+
+ // Then try creating the scanner...
+ if ((scanner = papplScannerCreate(system, 0, d->device_info, "auto", d->device_id, d->device_uri)) == NULL)
+ continue; // Scanner with this name exists
+
+ // Register the DNS-SD service...
+ _papplRWLockRead(scanner->system);
+ _papplRWLockRead(scanner);
+ _papplScannerRegisterDNSSDNoLock(scanner);
+ _papplRWUnlock(scanner);
+ _papplRWUnlock(scanner->system);
+
+ // Created, return true and invoke the callback if provided...
+ ret = true;
+
+ if (cb)
+ (cb)(scanner, cb_data);
+ }
+
+ cupsArrayDelete(devices);
+
+ return (ret);
+}
+
//
// 'papplSystemFindPrinter()' - Find a printer by resource, ID, or device URI.
//
@@ -169,6 +259,63 @@ papplSystemFindPrinter(
return (printer);
}
+//
+// 'papplSystemFindScanner()' - Find a scanner by resource, ID, or device URI.
+//
+// This function finds a scanner contained in the system using its resource
+// path, unique integer identifier, or device URI. If none of these is
+// specified, the current default scanner is returned.
+//
+
+pappl_scanner_t * // O - Scanner or `NULL` if none
+papplSystemFindScanner(
+ pappl_system_t *system, // I - System
+ const char *resource, // I - Resource path or `NULL`
+ int scanner_id, // I - Printer ID or `0`
+ const char *device_uri) // I - Device URI or `NULL`
+{
+ cups_len_t i, // Current scanner index
+ count; // scanner count
+ pappl_scanner_t *scanner = NULL;// Matching scanner
+
+
+ // Range check input...
+ if (!system)
+ return (NULL);
+
+ _papplRWLockRead(system);
+
+ if (resource && (!strcmp(resource, "/") || !strcmp(resource, "/escl/scan") || (!strncmp(resource, "/escl/scan/", 11) && isdigit(resource[11] & 255))))
+ {
+ scanner_id = system->default_scanner_id;
+ resource = NULL;
+ }
+
+ // Loop through the scanners to find the one we want...
+ //
+ // Note: Cannot use cupsArrayGetFirst/Last since other threads might be
+ // enumerating the scanners array.
+
+ for (i = 0, count = cupsArrayGetCount(system->scanners); i < count; i ++)
+ {
+ scanner = (pappl_scanner_t *)cupsArrayGetElement(system->scanners, i);
+
+ if (resource && !strncasecmp(scanner->resource, resource, scanner->resourcelen) && (!resource[scanner->resourcelen] || resource[scanner->resourcelen] == '/'))
+ break;
+ else if (scanner->scanner_id == scanner_id)
+ break;
+ else if (device_uri && !strcmp(scanner->device_uri, device_uri))
+ break;
+ }
+
+ if (i >= count)
+ scanner = NULL;
+
+ _papplRWUnlock(system);
+
+ return (scanner);
+}
+
//
// 'compare_printers()' - Compare two printers.
@@ -180,3 +327,14 @@ compare_printers(pappl_printer_t *a, // I - First printer
{
return (strcmp(a->name, b->name));
}
+
+
+//
+// 'compare_scanners())' - Compare two scanners.
+//
+static int // O - Result of comparison
+compare_scanners(pappl_scanner_t *a, // I - First scanner
+ pappl_scanner_t *b) // I - Second scanner
+{
+ return (strcmp(a->name, b->name));
+}
diff --git a/pappl/system-private.h b/pappl/system-private.h
index 50336397..17df61cb 100644
--- a/pappl/system-private.h
+++ b/pappl/system-private.h
@@ -101,15 +101,41 @@ struct _pappl_system_s // System data
cups_array_t *filters; // Array of filters
int next_client; // Next client number
cups_array_t *printers; // Array of printers
+ cups_array_t *scanners; // Array of scanners
int default_printer_id, // Default printer-id
next_printer_id; // Next printer-id
+
+ int default_scanner_id, // Default scanner-id
+ next_scanner_id; // Next scanner-id
char password_hash[100]; // Access password hash
cups_len_t num_drivers; // Number of printer drivers
pappl_pr_driver_t *drivers; // Printer drivers
+
+ pappl_sc_driver_t *scanner_drivers; // Scanner drivers
+
+ cups_len_t num_scanner_drivers; // Number of scanner drivers
+ pappl_sc_identify_cb_t identify_cb; // Callback for identifying the scanner
+ pappl_sc_delete_cb_t sc_delete_cb; // Scanner deletion callback
+ pappl_sc_capabilities_cb_t capabilities_cb; // Callback for getting scanner capabilities
+ pappl_sc_job_create_cb_t job_create_cb; // Callback for creating a scan job
+ pappl_sc_job_delete_cb_t job_delete_cb; // Callback for deleting a scan job
+ pappl_sc_data_cb_t data_cb; // Callback for getting scan data
+ pappl_sc_status_cb_t status_cb; // Callback for getting scanner status
+ pappl_sc_job_complete_cb_t job_complete_cb; // Callback for completing a scan job
+ pappl_sc_job_cancel_cb_t job_cancel_cb; // Callback for cancelling a scan job
+ pappl_sc_buffer_info_cb_t buffer_info_cb; // Callback for getting buffer information
+ pappl_sc_image_info_cb_t image_info_cb; // Callback for getting image information
+ pappl_identify_sc_actions_t identify_default; // "identify-actions-default" values
+ pappl_identify_sc_actions_t identify_supported; // "identify-actions-supported" values
+ pappl_sc_autoadd_cb_t autoadd_sc_cb; // Scanner driver auto-add callback
+ pappl_sc_create_cb_t create_sc_cb; // Scanner driver creation callback
+ pappl_sc_driver_cb_t driver_sc_cb; // Scanner driver initialization callback
+
pappl_pr_autoadd_cb_t autoadd_cb; // Printer driver auto-add callback
pappl_pr_create_cb_t create_cb; // Printer driver creation callback
pappl_pr_driver_cb_t driver_cb; // Printer driver initialization callback
void *driver_cbdata; // Printer driver callback data
+ void *sc_driver_cbdata; // Scanner driver callback data
ipp_t *attrs; // Static attributes for system
char *auth_scheme; // Authentication scheme
pappl_auth_cb_t auth_cb; // Authentication callback
@@ -142,6 +168,12 @@ struct _pappl_system_s // System data
pappl_event_cb_t event_cb; // Event callback
void *event_data; // Event callback data
+
+ pappl_scanner_event_cb_t scan_event_cb; // Scanner event callback
+ void *scan_event_data; // Scanner event callback data
+ pappl_scanner_event_cb_t systemui_scan_cb; // System UI scanner event callback
+ void *systemui_scan_data; // System UI scanner event callback data
+
pappl_event_cb_t systemui_cb; // System UI event callback
void *systemui_data; // System UI event callback data
size_t max_subscriptions; // Maximum number of subscriptions
@@ -168,11 +200,13 @@ typedef struct _pappl_timer_s // Timer callback data
// Functions...
//
-extern void _papplSystemAddEventNoLock(pappl_system_t *system, pappl_printer_t *printer, pappl_job_t *job, pappl_event_t event, const char *message, ...) _PAPPL_FORMAT(5, 6) _PAPPL_PRIVATE;
-extern void _papplSystemAddEventNoLockv(pappl_system_t *system, pappl_printer_t *printer, pappl_job_t *job, pappl_event_t event, const char *message, va_list ap) _PAPPL_PRIVATE;
+extern void _papplSystemAddEventNoLock(pappl_system_t *system, pappl_printer_t *printer, pappl_scanner_t *scanner, pappl_job_t *job, pappl_event_t event, const char *message, ...) _PAPPL_FORMAT(6, 7) _PAPPL_PRIVATE;
+extern void _papplSystemAddEventNoLockv(pappl_system_t *system, pappl_printer_t *printer, pappl_scanner_t *scanner, pappl_job_t *job, pappl_event_t event, const char *message, va_list ap) _PAPPL_PRIVATE;
extern void _papplSystemAddLoc(pappl_system_t *system, pappl_loc_t *loc) _PAPPL_PRIVATE;
extern void _papplSystemAddPrinter(pappl_system_t *system, pappl_printer_t *printer, int printer_id) _PAPPL_PRIVATE;
+extern void _papplSystemAddScanner(pappl_system_t *system, pappl_scanner_t *scanner, int scanner_id) _PAPPL_PRIVATE;
extern void _papplSystemAddPrinterIcons(pappl_system_t *system, pappl_printer_t *printer) _PAPPL_PRIVATE;
+extern void _papplSystemAddScannerIcons(pappl_system_t *system, pappl_scanner_t *scanner) _PAPPL_PRIVATE;
extern bool _papplSystemAddSubscription(pappl_system_t *system, pappl_subscription_t *sub, int sub_id) _PAPPL_PRIVATE;
extern void _papplSystemCleanSubscriptions(pappl_system_t *system, bool clean_all) _PAPPL_PRIVATE;
extern void _papplSystemConfigChanged(pappl_system_t *system) _PAPPL_PRIVATE;
diff --git a/pappl/system-subscription.c b/pappl/system-subscription.c
index 87b455b3..69b08ce5 100644
--- a/pappl/system-subscription.c
+++ b/pappl/system-subscription.c
@@ -12,6 +12,8 @@
//
#include "pappl-private.h"
+#include "subscription.h"
+#include "scanner-private.h"
//
@@ -27,15 +29,14 @@ static int compare_subscriptions(pappl_subscription_t *a, pappl_subscription_t *
void
papplSystemAddEvent(
- pappl_system_t *system, // I - System
- pappl_printer_t *printer, // I - Associated printer, if any
- pappl_job_t *job, // I - Associated job, if any
- pappl_event_t event, // I - IPP "notify-events" bit value
- const char *message, // I - printf-style message string
- ...) // I - Additional arguments as needed
+ pappl_system_t *system, // I - System
+ pappl_printer_t *printer, // I - Associated printer, if any
+ pappl_job_t *job, // I - Associated job, if any
+ pappl_event_t event, // I - IPP "notify-events" bit value
+ const char *message, // I - printf-style message string
+ ...) // I - Additional arguments as needed
{
- va_list ap; // Argument pointer
-
+ va_list ap; // Argument pointer
if (!system)
return;
@@ -46,7 +47,7 @@ papplSystemAddEvent(
_papplRWLockRead(job);
va_start(ap, message);
- _papplSystemAddEventNoLockv(system, printer, job, event, message, ap);
+ _papplSystemAddEventNoLockv(system, printer, NULL, job, event, message, ap);
va_end(ap);
if (job)
@@ -56,47 +57,75 @@ papplSystemAddEvent(
}
+void
+papplSystemAddScannerEvent(
+ pappl_system_t *system, // I - System
+ pappl_scanner_t *scanner, // I - Associated scanner, if any
+ pappl_job_t *job, // I - Associated job, if any
+ pappl_event_t event, // I - IPP "notify-events" bit value
+ const char *message, // I - printf-style message string
+ ...) // I - Additional arguments as needed
+{
+ va_list ap; // Argument pointer
+
+ if (!system)
+ return;
+
+ if (scanner)
+ _papplRWLockRead(scanner);
+ if (job)
+ _papplRWLockRead(job);
+
+ va_start(ap, message);
+ _papplSystemAddEventNoLockv(system, NULL, scanner, job, event, message, ap);
+ va_end(ap);
+
+ if (job)
+ _papplRWUnlock(job);
+ if (scanner)
+ _papplRWUnlock(scanner);
+}
+
//
// '_papplSystemAddEventNoLock()' - Add a notification event (no lock).
//
void
_papplSystemAddEventNoLock(
- pappl_system_t *system, // I - System
- pappl_printer_t *printer, // I - Associated printer, if any
- pappl_job_t *job, // I - Associated job, if any
- pappl_event_t event, // I - IPP "notify-events" bit value
- const char *message, // I - printf-style message string
- ...) // I - Additional arguments as needed
+ pappl_system_t *system, // I - System
+ pappl_printer_t *printer, // I - Associated printer, if any
+ pappl_scanner_t *scanner, // I - Associated scanner, if any
+ pappl_job_t *job, // I - Associated job, if any
+ pappl_event_t event, // I - IPP "notify-events" bit value
+ const char *message, // I - printf-style message string
+ ...) // I - Additional arguments as needed
{
- va_list ap; // Argument pointer
-
+ va_list ap; // Argument pointer
va_start(ap, message);
- _papplSystemAddEventNoLockv(system, printer, job, event, message, ap);
+ _papplSystemAddEventNoLockv(system, printer, scanner, job, event, message, ap);
va_end(ap);
}
-
//
// '_papplSystemAddEventNoLockv()' - Add a notification event (no lock).
//
void
_papplSystemAddEventNoLockv(
- pappl_system_t *system, // I - System
- pappl_printer_t *printer, // I - Associated printer, if any
- pappl_job_t *job, // I - Associated job, if any
- pappl_event_t event, // I - IPP "notify-events" bit value
- const char *message, // I - printf-style message string
- va_list ap) // I - Pointer to additional arguments
+ pappl_system_t *system, // I - System
+ pappl_printer_t *printer, // I - Associated printer, if any
+ pappl_scanner_t *scanner, // I - Associated scanner, if any
+ pappl_job_t *job, // I - Associated job, if any
+ pappl_event_t event, // I - IPP "notify-events" bit value
+ const char *message, // I - printf-style message string
+ va_list ap) // I - Pointer to additional arguments
{
- pappl_subscription_t *sub; // Current subscription
- ipp_t *n; // Notify event attributes
- char uri[1024] = "", // "notify-printer/system-uri" value
- text[1024]; // "notify-text" value
- va_list cap; // Copy of additional arguments
-
+ pappl_subscription_t *sub; // Current subscription
+ ipp_t *n; // Notify event attributes
+ char uri[1024] = "", // "notify-printer/scanner/system-uri" value
+ text[1024]; // "notify-text" value
+ va_list cap; // Copy of additional arguments
// Loop through all of the subscriptions and deliver any events...
_papplRWLockRead(system);
@@ -107,15 +136,23 @@ _papplSystemAddEventNoLockv(
if (system->event_cb)
(system->event_cb)(system, printer, job, event, system->event_data);
+ // Check if scanner-specific callbacks are defined
+ if (system->systemui_scan_cb && system->systemui_scan_data)
+ (system->systemui_scan_cb)(system, scanner, job, event, system->systemui_scan_data);
+
+ if (system->scan_event_cb && system->scan_event_data)
+ (system->scan_event_cb)(system, scanner, job, event, system->scan_event_data);
+
for (sub = (pappl_subscription_t *)cupsArrayGetFirst(system->subscriptions); sub; sub = (pappl_subscription_t *)cupsArrayGetNext(system->subscriptions))
{
- if ((sub->mask & event) && (!sub->job || job == sub->job) && (!sub->printer || printer == sub->printer))
+ if ((sub->mask & event) && (!sub->job || job == sub->job) && (!sub->printer || printer == sub->printer) && (!sub->scanner || scanner == sub->scanner))
{
_papplRWLockWrite(sub);
n = ippNew();
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_CONST_TAG(IPP_TAG_CHARSET), "notify-charset", NULL, "utf-8");
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_CONST_TAG(IPP_TAG_LANGUAGE), "notify-natural-language", NULL, sub->language);
+
if (printer)
{
if (!uri[0])
@@ -123,6 +160,13 @@ _papplSystemAddEventNoLockv(
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-printer-uri", NULL, uri);
}
+ else if (scanner)
+ {
+ if (!uri[0])
+ httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipps", NULL, system->hostname, system->port, scanner->resource);
+
+ ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-scanner-uri", NULL, uri);
+ }
else
{
if (!uri[0])
@@ -130,12 +174,15 @@ _papplSystemAddEventNoLockv(
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_URI, "notify-system-uri", NULL, uri);
}
+
if (job)
ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-job-id", job->job_id);
+
ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-subscription-id", sub->subscription_id);
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_CONST_TAG(IPP_TAG_URI), "notify-subscription-uuid", NULL, sub->uuid);
- ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-sequence-number", ++ sub->last_sequence);
+ ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "notify-sequence-number", ++sub->last_sequence);
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_CONST_TAG(IPP_TAG_KEYWORD), "notify-subscribed-event", NULL, _papplSubscriptionEventString(event));
+
if (message)
{
va_copy(cap, ap);
@@ -143,29 +190,36 @@ _papplSystemAddEventNoLockv(
va_end(cap);
ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_TEXT, "notify-text", NULL, text);
}
+
if (job && (event & PAPPL_EVENT_JOB_ALL))
{
_papplJobCopyStateNoLock(job, IPP_TAG_EVENT_NOTIFICATION, n, NULL);
- if (event == PAPPL_EVENT_JOB_CREATED)
- {
- ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-name", NULL, job->name);
- ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
- }
+ if (event == PAPPL_EVENT_JOB_CREATED)
+ {
+ ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-name", NULL, job->name);
+ ippAddString(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_NAME, "job-originating-user-name", NULL, job->username);
+ }
}
+
if (!sub->job && printer && (event & PAPPL_EVENT_PRINTER_ALL))
_papplPrinterCopyStateNoLock(printer, IPP_TAG_EVENT_NOTIFICATION, n, NULL, NULL);
- // TODO: add system event notifications
+
+ if (!sub->job && scanner && (event & PAPPL_EVENT_SCANNER_ALL))
+ _papplScannerCopyStateNoLock(scanner, IPP_TAG_EVENT_NOTIFICATION, n, NULL, NULL);
+
if (printer)
- ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
+ ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "printer-up-time", (int)(time(NULL) - printer->start_time));
+ else if (scanner)
+ ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "scanner-up-time", (int)(time(NULL) - scanner->start_time));
else
- ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "system-up-time", (int)(time(NULL) - system->start_time));
+ ippAddInteger(n, IPP_TAG_EVENT_NOTIFICATION, IPP_TAG_INTEGER, "system-up-time", (int)(time(NULL) - system->start_time));
cupsArrayAdd(sub->events, n);
if (cupsArrayGetCount(sub->events) > PAPPL_MAX_EVENTS)
{
- cupsArrayRemove(sub->events, cupsArrayGetFirst(sub->events));
- sub->first_sequence ++;
+ cupsArrayRemove(sub->events, cupsArrayGetFirst(sub->events));
+ sub->first_sequence++;
}
_papplRWUnlock(sub);
diff --git a/pappl/system.c b/pappl/system.c
index bbbdf42e..911abf06 100644
--- a/pappl/system.c
+++ b/pappl/system.c
@@ -73,6 +73,49 @@ _papplSystemAddPrinterIcons(
}
+//
+// '_papplSystemAddScannerIcons()' - (Re)add scanner icon resources.
+//
+
+void
+_papplSystemAddScannerIcons(
+ pappl_system_t *system, // I - System
+ pappl_scanner_t *scanner) // I - Scanner
+{
+ char path[256]; // Resource path
+ pappl_icon_sc_t *icons = scanner->driver_data.icons;
+ // Scanner icons
+
+
+ snprintf(path, sizeof(path), "%s/icon-sm.png", scanner->uriname);
+ papplSystemRemoveResource(system, path);
+ if (icons[0].filename[0])
+ papplSystemAddResourceFile(system, path, "image/png", icons[0].filename);
+ else if (icons[0].data && icons[0].datalen)
+ papplSystemAddResourceData(system, path, "image/png", icons[0].data, icons[0].datalen);
+ else
+ papplSystemAddResourceData(system, path, "image/png", icon_sm_png, sizeof(icon_sm_png));
+
+ snprintf(path, sizeof(path), "%s/icon-md.png", scanner->uriname);
+ papplSystemRemoveResource(system, path);
+ if (icons[1].filename[0])
+ papplSystemAddResourceFile(system, path, "image/png", icons[1].filename);
+ else if (icons[1].data && icons[1].datalen)
+ papplSystemAddResourceData(system, path, "image/png", icons[1].data, icons[1].datalen);
+ else
+ papplSystemAddResourceData(system, path, "image/png", icon_md_png, sizeof(icon_md_png));
+
+ snprintf(path, sizeof(path), "%s/icon-lg.png", scanner->uriname);
+ papplSystemRemoveResource(system, path);
+ if (icons[2].filename[0])
+ papplSystemAddResourceFile(system, path, "image/png", icons[2].filename);
+ else if (icons[2].data && icons[2].datalen)
+ papplSystemAddResourceData(system, path, "image/png", icons[2].data, icons[2].datalen);
+ else
+ papplSystemAddResourceData(system, path, "image/png", icon_lg_png, sizeof(icon_lg_png));
+}
+
+
//
// '_papplSystemConfigChanged()' - Mark the system configuration as changed.
//
diff --git a/pappl/system.h b/pappl/system.h
index ce83464c..62b69fca 100644
--- a/pappl/system.h
+++ b/pappl/system.h
@@ -55,6 +55,14 @@ typedef struct pappl_pr_driver_s // Printer driver information
void *extension; // Extension data pointer
} pappl_pr_driver_t;
+typedef struct pappl_sc_driver_s // Scanner driver information
+{
+ const char *name; // Driver name
+ const char *description; // Driver description (usually the make and model)
+ const char *device_id; // Device ID
+ void *extension; // Extension data pointer
+} pappl_sc_driver_t;
+
enum pappl_soptions_e // System option bits
{
PAPPL_SOPTIONS_NONE = 0x0000, // No options
@@ -109,6 +117,14 @@ typedef void (*pappl_pr_create_cb_t)(pappl_printer_t *printer, void *data);
// Printer creation callback
typedef bool (*pappl_pr_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_pr_driver_data_t *driver_data, ipp_t **driver_attrs, void *data);
// Driver callback function
+
+typedef const char *(*pappl_sc_autoadd_cb_t)(const char *device_info, const char *device_uri, const char *device_id, void *data);
+ // Scanner Auto-add callback
+typedef void (*pappl_sc_create_cb_t)(pappl_scanner_t *scanner, void *data);
+ // Scanner creation callback
+typedef bool (*pappl_sc_driver_cb_t)(pappl_system_t *system, const char *driver_name, const char *device_uri, const char *device_id, pappl_sc_driver_data_t *driver_data, void *data);
+ // Scanner Driver callback function
+
typedef bool (*pappl_mime_filter_cb_t)(pappl_job_t *job, pappl_device_t *device, void *data);
// Filter callback function
typedef bool (*pappl_ipp_op_cb_t)(pappl_client_t *client, void *data);
@@ -134,12 +150,25 @@ typedef int (*pappl_wifi_list_cb_t)(pappl_system_t *system, void *data, cups_des
typedef pappl_wifi_t *(*pappl_wifi_status_cb_t)(pappl_system_t *system, void *data, pappl_wifi_t *wifi_data);
// Wi-Fi status callback
+typedef unsigned pappl_identify_sc_actions_t; // eSCL actions for identifying the scanner
+typedef pappl_sc_driver_data_t (*pappl_sc_capabilities_cb_t)(pappl_scanner_t *scanner); // Callback for getting scanner capabilities
+typedef void (*pappl_sc_identify_cb_t)(pappl_scanner_t *scanner, pappl_identify_sc_actions_t actions, const char *message); // Callback for identifying the scanner
+typedef void (*pappl_sc_delete_cb_t)(pappl_scanner_t *scanner, pappl_sc_driver_data_t *data);
+typedef void (*pappl_sc_job_create_cb_t)(pappl_job_t *job, pappl_sc_options_t *options, pappl_device_t *device); // Callback for creating a scan job
+typedef void (*pappl_sc_job_delete_cb_t)(pappl_job_t *job); // Callback for deleting a scan job
+typedef bool (*pappl_sc_data_cb_t)(pappl_job_t *job, pappl_device_t *device, void *buffer, size_t bufsize); // Callback for getting scan data
+typedef bool (*pappl_sc_status_cb_t)(pappl_scanner_t *scanner); // Callback for getting scanner status
+typedef void (*pappl_sc_job_complete_cb_t)(pappl_job_t *job); // Callback for completing a scan job
+typedef bool (*pappl_sc_job_cancel_cb_t)(pappl_job_t *job); // Callback for cancelling a scan job
+typedef void (*pappl_sc_buffer_info_cb_t)(pappl_job_t *job, pappl_sc_options_t *options, pappl_device_t *device); // Callback for getting buffer information
+typedef void (*pappl_sc_image_info_cb_t)(pappl_job_t *job, pappl_device_t *device, void *data); // Callback for getting scan image information
//
// Functions...
//
extern void papplSystemAddEvent(pappl_system_t *system, pappl_printer_t *printer, pappl_job_t *job, pappl_event_t event, const char *message, ...) _PAPPL_FORMAT(5, 6) _PAPPL_PUBLIC;
+extern void papplSystemAddScannerEvent(pappl_system_t *system, pappl_scanner_t *scanner, pappl_job_t *job, pappl_event_t event, const char *message, ...) _PAPPL_PUBLIC;
extern void papplSystemAddLink(pappl_system_t *system, const char *label, const char *path_or_url, pappl_loptions_t options) _PAPPL_PUBLIC;
extern bool papplSystemAddListeners(pappl_system_t *system, const char *name) _PAPPL_PUBLIC;
extern void papplSystemAddMIMEFilter(pappl_system_t *system, const char *srctype, const char *dsttype, pappl_mime_filter_cb_t cb, void *data) _PAPPL_PUBLIC;
@@ -154,14 +183,17 @@ extern bool papplSystemAddTimerCallback(pappl_system_t *system, time_t start, i
extern void papplSystemCleanJobs(pappl_system_t *system) _PAPPL_PUBLIC;
extern pappl_system_t *papplSystemCreate(pappl_soptions_t options, const char *name, int port, const char *subtypes, const char *spooldir, const char *logfile, pappl_loglevel_t loglevel, const char *auth_service, bool tls_only) _PAPPL_PUBLIC;
extern bool papplSystemCreatePrinters(pappl_system_t *system, pappl_devtype_t types, pappl_pr_create_cb_t cb, void *cb_data) _PAPPL_PUBLIC;
+extern bool papplSystemCreateScanners(pappl_system_t *system, pappl_devtype_t types, pappl_sc_create_cb_t cb, void *cb_data) _PAPPL_PUBLIC;
extern void papplSystemDelete(pappl_system_t *system) _PAPPL_PUBLIC;
extern pappl_loc_t *papplSystemFindLoc(pappl_system_t *system, const char *language) _PAPPL_PUBLIC;
extern pappl_printer_t *papplSystemFindPrinter(pappl_system_t *system, const char *resource, int printer_id, const char *device_uri) _PAPPL_PUBLIC;
+extern pappl_scanner_t *papplSystemFindScanner(pappl_system_t *system, const char *resource, int scanner_id, const char *device_uri) _PAPPL_PUBLIC;
extern pappl_subscription_t *papplSystemFindSubscription(pappl_system_t *system, int sub_id) _PAPPL_PUBLIC;
extern char *papplSystemGetAdminGroup(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
extern const char *papplSystemGetAuthService(pappl_system_t *system) _PAPPL_PUBLIC;
extern pappl_contact_t *papplSystemGetContact(pappl_system_t *system, pappl_contact_t *contact) _PAPPL_PUBLIC;
extern int papplSystemGetDefaultPrinterID(pappl_system_t *system) _PAPPL_PUBLIC;
+extern int papplSystemGetDefaultScannerID(pappl_system_t *system) _PAPPL_PUBLIC;
extern char *papplSystemGetDefaultPrintGroup(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
extern char *papplSystemGetDNSSDName(pappl_system_t *system, char *buffer, size_t bufsize) _PAPPL_PUBLIC;
extern const char *papplSystemGetFooterHTML(pappl_system_t *system) _PAPPL_PUBLIC;
@@ -203,6 +235,7 @@ extern void papplSystemSetAdminGroup(pappl_system_t *system, const char *value)
extern void papplSystemSetAuthCallback(pappl_system_t *system, const char *auth_scheme, pappl_auth_cb_t auth_cb, void *auth_cbdata) _PAPPL_PUBLIC;
extern void papplSystemSetContact(pappl_system_t *system, pappl_contact_t *contact) _PAPPL_PUBLIC;
extern void papplSystemSetDefaultPrinterID(pappl_system_t *system, int default_printer_id) _PAPPL_PUBLIC;
+extern void papplSystemSetDefaultScannerID(pappl_system_t *system, int default_scanner_id) _PAPPL_PUBLIC;
extern void papplSystemSetDefaultPrintGroup(pappl_system_t *system, const char *value) _PAPPL_PUBLIC;
extern void papplSystemSetDNSSDName(pappl_system_t *system, const char *value) _PAPPL_PUBLIC;
extern void papplSystemSetEventCallback(pappl_system_t *system, pappl_event_cb_t event_cb, void *event_data) _PAPPL_PUBLIC;
@@ -219,11 +252,13 @@ extern void papplSystemSetMaxSubscriptions(pappl_system_t *system, size_t max_s
extern void papplSystemSetMIMECallback(pappl_system_t *system, pappl_mime_cb_t cb, void *data) _PAPPL_PUBLIC;
extern void papplSystemSetNetworkCallbacks(pappl_system_t *system, pappl_network_get_cb_t get_cb, pappl_network_set_cb_t set_cb, void *cb_data) _PAPPL_PUBLIC;
extern void papplSystemSetNextPrinterID(pappl_system_t *system, int next_printer_id) _PAPPL_PUBLIC;
+extern void papplSystemSetNextScannerID(pappl_system_t *system, int next_scanner_id) _PAPPL_PUBLIC;
extern void papplSystemSetOperationCallback(pappl_system_t *system, pappl_ipp_op_cb_t cb, void *data) _PAPPL_PUBLIC;
extern void papplSystemSetOrganization(pappl_system_t *system, const char *value) _PAPPL_PUBLIC;
extern void papplSystemSetOrganizationalUnit(pappl_system_t *system, const char *value) _PAPPL_PUBLIC;
extern void papplSystemSetPassword(pappl_system_t *system, const char *hash) _PAPPL_PUBLIC;
extern void papplSystemSetPrinterDrivers(pappl_system_t *system, int num_drivers, pappl_pr_driver_t *drivers, pappl_pr_autoadd_cb_t autoadd_cb, pappl_pr_create_cb_t create_cb, pappl_pr_driver_cb_t driver_cb, void *data) _PAPPL_PUBLIC;
+extern void papplSystemSetScannerDrivers(pappl_system_t *system, int num_drivers, pappl_sc_driver_t *drivers, pappl_sc_identify_cb_t identify_cb, pappl_sc_create_cb_t create_cb, pappl_sc_driver_cb_t driver_cb, pappl_sc_delete_cb_t sc_delete_cb, pappl_sc_capabilities_cb_t capabilities_cb, pappl_sc_job_create_cb_t job_create_cb, pappl_sc_job_delete_cb_t job_delete_cb, pappl_sc_data_cb_t data_cb, pappl_sc_status_cb_t status_cb, pappl_sc_job_complete_cb_t job_complete_cb, pappl_sc_job_cancel_cb_t job_cancel_cb, pappl_sc_buffer_info_cb_t buffer_info_cb, pappl_sc_image_info_cb_t image_info_cb, void *data) _PAPPL_PUBLIC;
extern void papplSystemSetSaveCallback(pappl_system_t *system, pappl_save_cb_t cb, void *data) _PAPPL_PUBLIC;
extern void papplSystemSetUUID(pappl_system_t *system, const char *value) _PAPPL_PUBLIC;
extern void papplSystemSetVersions(pappl_system_t *system, int num_versions, pappl_version_t *versions) _PAPPL_PUBLIC;
diff --git a/testsuite/Makefile b/testsuite/Makefile
index 1acd42bb..c58e9ffb 100644
--- a/testsuite/Makefile
+++ b/testsuite/Makefile
@@ -82,3 +82,5 @@ resheader:
# Dependencies
include Dependencies
+
+LIBS=-ldl -lpthread -lcups -lavahi-common -lavahi-client -lssl -lcrypto -ljpeg -lpng16 -lz -lusb-1.0 -lpam -lxml2