/* sane - Scanner Access Now Easy. Copyright (C) 2019 Touboul Nathane Copyright (C) 2019 Thierry HUCHARD This file is part of the SANE package. SANE is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. SANE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sane; see the file COPYING. If not, see . This file implements a SANE backend for eSCL scanners. */ #define DEBUG_DECLARE_ONLY #include "../include/sane/config.h" #include "escl.h" #include #include #include #include #include #include #include #include #include "../include/sane/sanei.h" static AvahiSimplePoll *simple_poll = NULL; static int count_finish = 0; /** * \fn static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED * AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, * AvahiResolverEvent event, const char *name, * const char *type, const char *domain, const char *host_name, * const AvahiAddress *address, uint16_t port, * AvahiStringList *txt, AvahiLookupResultFlags flags, * void *userdata) * \brief Callback function that will check if the selected scanner follows the escl * protocol or not. */ static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char __sane_unused__ *type, const char __sane_unused__ *domain, const char __sane_unused__ *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags __sane_unused__ flags, void __sane_unused__ *userdata) { char a[(AVAHI_ADDRESS_STR_MAX + 10)] = { 0 }; char *t; const char *is; const char *uuid; AvahiStringList *s; assert(r); switch (event) { case AVAHI_RESOLVER_FAILURE: break; case AVAHI_RESOLVER_FOUND: { char *psz_addr = ((void*)0); char b[128] = { 0 }; avahi_address_snprint(b, (sizeof(b)/sizeof(b[0]))-1, address); #ifdef ENABLE_IPV6 if (protocol == AVAHI_PROTO_INET6 && strchr(b, ':')) { if ( asprintf( &psz_addr, "[%s]", b ) == -1 ) break; } else #endif { if ( asprintf( &psz_addr, "%s", b ) == -1 ) break; } t = avahi_string_list_to_string(txt); if (strstr(t, "\"rs=eSCL\"") || strstr(t, "\"rs=/eSCL\"")) { s = avahi_string_list_find(txt, "is"); if (s && s->size > 3) is = (const char*)s->text + 3; else is = (const char*)NULL; s = avahi_string_list_find(txt, "uuid"); if (s && s->size > 5) uuid = (const char*)s->text + 5; else uuid = (const char*)NULL; DBG (10, "resolve_callback [%s]\n", a); if (strstr(psz_addr, "127.0.0.1") != NULL) { escl_device_add(port, name, "localhost", is, uuid, (char*)type); DBG (10,"resolve_callback fix redirect [localhost]\n"); } else escl_device_add(port, name, psz_addr, is, uuid, (char*)type); } } } } /** * \fn static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, * AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, * const char *type, const char *domain, * AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) * \brief Callback function that will browse tanks to 'avahi' the scanners * connected in network. */ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { AvahiClient *c = userdata; assert(b); switch (event) { case AVAHI_BROWSER_FAILURE: avahi_simple_poll_quit(simple_poll); return; case AVAHI_BROWSER_NEW: if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) break; case AVAHI_BROWSER_REMOVE: break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: if (event != AVAHI_BROWSER_CACHE_EXHAUSTED) { count_finish++; if (count_finish == 2) avahi_simple_poll_quit(simple_poll); } break; } } /** * \fn static void client_callback(AvahiClient *c, AvahiClientState state, * AVAHI_GCC_UNUSED void *userdata) * \brief Callback Function that quit if it doesn't find a connected scanner, * possible thanks the "Hello Protocol". * --> Waiting for a answer by the scanner to continue the avahi process. */ static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) { assert(c); if (state == AVAHI_CLIENT_FAILURE) avahi_simple_poll_quit(simple_poll); } /** * \fn ESCL_Device *escl_devices(SANE_Status *status) * \brief Function that calls all the avahi functions and then, recovers the * connected eSCL devices. * This function is called in the 'sane_get_devices' function. * * \return NULL (the eSCL devices found) */ ESCL_Device * escl_devices(SANE_Status *status) { AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; count_finish = 0; *status = SANE_STATUS_GOOD; if (!(simple_poll = avahi_simple_poll_new())) { DBG( 1, "Failed to create simple poll object.\n"); *status = SANE_STATUS_INVAL; goto fail; } client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); if (!client) { DBG( 1, "Failed to create client: %s\n", avahi_strerror(error)); *status = SANE_STATUS_INVAL; goto fail; } if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscan._tcp", NULL, 0, browse_callback, client))) { DBG( 1, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); *status = SANE_STATUS_INVAL; goto fail; } if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_uscans._tcp", NULL, 0, browse_callback, client))) { DBG( 1, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); *status = SANE_STATUS_INVAL; goto fail; } avahi_simple_poll_loop(simple_poll); fail: if (sb) avahi_service_browser_free(sb); if (client) avahi_client_free(client); if (simple_poll) avahi_simple_poll_free(simple_poll); return (NULL); }