1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy. 2141cc406Sopenharmony_ci 3141cc406Sopenharmony_ci Copyright (C) 2019 Touboul Nathane 4141cc406Sopenharmony_ci Copyright (C) 2019 Thierry HUCHARD <thierry@ordissimo.com> 5141cc406Sopenharmony_ci 6141cc406Sopenharmony_ci This file is part of the SANE package. 7141cc406Sopenharmony_ci 8141cc406Sopenharmony_ci SANE is free software; you can redistribute it and/or modify it under 9141cc406Sopenharmony_ci the terms of the GNU General Public License as published by the Free 10141cc406Sopenharmony_ci Software Foundation; either version 3 of the License, or (at your 11141cc406Sopenharmony_ci option) any later version. 12141cc406Sopenharmony_ci 13141cc406Sopenharmony_ci SANE is distributed in the hope that it will be useful, but WITHOUT 14141cc406Sopenharmony_ci ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15141cc406Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16141cc406Sopenharmony_ci for more details. 17141cc406Sopenharmony_ci 18141cc406Sopenharmony_ci You should have received a copy of the GNU General Public License 19141cc406Sopenharmony_ci along with sane; see the file COPYING. 20141cc406Sopenharmony_ci If not, see <https://www.gnu.org/licenses/>. 21141cc406Sopenharmony_ci 22141cc406Sopenharmony_ci This file implements a SANE backend for eSCL scanners. */ 23141cc406Sopenharmony_ci 24141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY 25141cc406Sopenharmony_ci#include "../include/sane/config.h" 26141cc406Sopenharmony_ci 27141cc406Sopenharmony_ci#include "escl.h" 28141cc406Sopenharmony_ci 29141cc406Sopenharmony_ci#include <assert.h> 30141cc406Sopenharmony_ci#include <stdio.h> 31141cc406Sopenharmony_ci#include <stdlib.h> 32141cc406Sopenharmony_ci#include <string.h> 33141cc406Sopenharmony_ci#include <arpa/inet.h> 34141cc406Sopenharmony_ci 35141cc406Sopenharmony_ci#include <avahi-client/lookup.h> 36141cc406Sopenharmony_ci#include <avahi-common/error.h> 37141cc406Sopenharmony_ci#include <avahi-common/simple-watch.h> 38141cc406Sopenharmony_ci 39141cc406Sopenharmony_ci#include "../include/sane/sanei.h" 40141cc406Sopenharmony_ci 41141cc406Sopenharmony_cistatic AvahiSimplePoll *simple_poll = NULL; 42141cc406Sopenharmony_cistatic int count_finish = 0; 43141cc406Sopenharmony_ci 44141cc406Sopenharmony_ci/** 45141cc406Sopenharmony_ci * \fn static void resolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED 46141cc406Sopenharmony_ci * AvahiIfIndex interface, AVAHI_GCC_UNUSED AvahiProtocol protocol, 47141cc406Sopenharmony_ci * AvahiResolverEvent event, const char *name, 48141cc406Sopenharmony_ci * const char *type, const char *domain, const char *host_name, 49141cc406Sopenharmony_ci * const AvahiAddress *address, uint16_t port, 50141cc406Sopenharmony_ci * AvahiStringList *txt, AvahiLookupResultFlags flags, 51141cc406Sopenharmony_ci * void *userdata) 52141cc406Sopenharmony_ci * \brief Callback function that will check if the selected scanner follows the escl 53141cc406Sopenharmony_ci * protocol or not. 54141cc406Sopenharmony_ci */ 55141cc406Sopenharmony_cistatic void 56141cc406Sopenharmony_ciresolve_callback(AvahiServiceResolver *r, AVAHI_GCC_UNUSED AvahiIfIndex interface, 57141cc406Sopenharmony_ci AvahiProtocol protocol, 58141cc406Sopenharmony_ci AvahiResolverEvent event, 59141cc406Sopenharmony_ci const char *name, 60141cc406Sopenharmony_ci const char __sane_unused__ *type, 61141cc406Sopenharmony_ci const char __sane_unused__ *domain, 62141cc406Sopenharmony_ci const char __sane_unused__ *host_name, 63141cc406Sopenharmony_ci const AvahiAddress *address, 64141cc406Sopenharmony_ci uint16_t port, 65141cc406Sopenharmony_ci AvahiStringList *txt, 66141cc406Sopenharmony_ci AvahiLookupResultFlags __sane_unused__ flags, 67141cc406Sopenharmony_ci void __sane_unused__ *userdata) 68141cc406Sopenharmony_ci{ 69141cc406Sopenharmony_ci char a[(AVAHI_ADDRESS_STR_MAX + 10)] = { 0 }; 70141cc406Sopenharmony_ci char *t; 71141cc406Sopenharmony_ci const char *is; 72141cc406Sopenharmony_ci const char *uuid; 73141cc406Sopenharmony_ci AvahiStringList *s; 74141cc406Sopenharmony_ci assert(r); 75141cc406Sopenharmony_ci switch (event) { 76141cc406Sopenharmony_ci case AVAHI_RESOLVER_FAILURE: 77141cc406Sopenharmony_ci break; 78141cc406Sopenharmony_ci case AVAHI_RESOLVER_FOUND: 79141cc406Sopenharmony_ci { 80141cc406Sopenharmony_ci char *psz_addr = ((void*)0); 81141cc406Sopenharmony_ci char b[128] = { 0 }; 82141cc406Sopenharmony_ci avahi_address_snprint(b, (sizeof(b)/sizeof(b[0]))-1, address); 83141cc406Sopenharmony_ci#ifdef ENABLE_IPV6 84141cc406Sopenharmony_ci if (protocol == AVAHI_PROTO_INET6 && strchr(b, ':')) 85141cc406Sopenharmony_ci { 86141cc406Sopenharmony_ci if ( asprintf( &psz_addr, "[%s]", b ) == -1 ) 87141cc406Sopenharmony_ci break; 88141cc406Sopenharmony_ci } 89141cc406Sopenharmony_ci else 90141cc406Sopenharmony_ci#endif 91141cc406Sopenharmony_ci { 92141cc406Sopenharmony_ci if ( asprintf( &psz_addr, "%s", b ) == -1 ) 93141cc406Sopenharmony_ci break; 94141cc406Sopenharmony_ci } 95141cc406Sopenharmony_ci t = avahi_string_list_to_string(txt); 96141cc406Sopenharmony_ci if (strstr(t, "\"rs=eSCL\"") || strstr(t, "\"rs=/eSCL\"")) { 97141cc406Sopenharmony_ci s = avahi_string_list_find(txt, "is"); 98141cc406Sopenharmony_ci if (s && s->size > 3) 99141cc406Sopenharmony_ci is = (const char*)s->text + 3; 100141cc406Sopenharmony_ci else 101141cc406Sopenharmony_ci is = (const char*)NULL; 102141cc406Sopenharmony_ci s = avahi_string_list_find(txt, "uuid"); 103141cc406Sopenharmony_ci if (s && s->size > 5) 104141cc406Sopenharmony_ci uuid = (const char*)s->text + 5; 105141cc406Sopenharmony_ci else 106141cc406Sopenharmony_ci uuid = (const char*)NULL; 107141cc406Sopenharmony_ci DBG (10, "resolve_callback [%s]\n", a); 108141cc406Sopenharmony_ci if (strstr(psz_addr, "127.0.0.1") != NULL) { 109141cc406Sopenharmony_ci escl_device_add(port, name, "localhost", is, uuid, (char*)type); 110141cc406Sopenharmony_ci DBG (10,"resolve_callback fix redirect [localhost]\n"); 111141cc406Sopenharmony_ci } 112141cc406Sopenharmony_ci else 113141cc406Sopenharmony_ci escl_device_add(port, name, psz_addr, is, uuid, (char*)type); 114141cc406Sopenharmony_ci } 115141cc406Sopenharmony_ci } 116141cc406Sopenharmony_ci } 117141cc406Sopenharmony_ci} 118141cc406Sopenharmony_ci 119141cc406Sopenharmony_ci/** 120141cc406Sopenharmony_ci * \fn static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, 121141cc406Sopenharmony_ci * AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, 122141cc406Sopenharmony_ci * const char *type, const char *domain, 123141cc406Sopenharmony_ci * AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) 124141cc406Sopenharmony_ci * \brief Callback function that will browse tanks to 'avahi' the scanners 125141cc406Sopenharmony_ci * connected in network. 126141cc406Sopenharmony_ci */ 127141cc406Sopenharmony_cistatic void 128141cc406Sopenharmony_cibrowse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, 129141cc406Sopenharmony_ci AvahiProtocol protocol, AvahiBrowserEvent event, 130141cc406Sopenharmony_ci const char *name, const char *type, 131141cc406Sopenharmony_ci const char *domain, 132141cc406Sopenharmony_ci AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, 133141cc406Sopenharmony_ci void* userdata) 134141cc406Sopenharmony_ci{ 135141cc406Sopenharmony_ci AvahiClient *c = userdata; 136141cc406Sopenharmony_ci assert(b); 137141cc406Sopenharmony_ci switch (event) { 138141cc406Sopenharmony_ci case AVAHI_BROWSER_FAILURE: 139141cc406Sopenharmony_ci avahi_simple_poll_quit(simple_poll); 140141cc406Sopenharmony_ci return; 141141cc406Sopenharmony_ci case AVAHI_BROWSER_NEW: 142141cc406Sopenharmony_ci if (!(avahi_service_resolver_new(c, interface, protocol, name, 143141cc406Sopenharmony_ci type, domain, 144141cc406Sopenharmony_ci AVAHI_PROTO_UNSPEC, 0, 145141cc406Sopenharmony_ci resolve_callback, c))) 146141cc406Sopenharmony_ci break; 147141cc406Sopenharmony_ci case AVAHI_BROWSER_REMOVE: 148141cc406Sopenharmony_ci break; 149141cc406Sopenharmony_ci case AVAHI_BROWSER_ALL_FOR_NOW: 150141cc406Sopenharmony_ci case AVAHI_BROWSER_CACHE_EXHAUSTED: 151141cc406Sopenharmony_ci if (event != AVAHI_BROWSER_CACHE_EXHAUSTED) 152141cc406Sopenharmony_ci { 153141cc406Sopenharmony_ci count_finish++; 154141cc406Sopenharmony_ci if (count_finish == 2) 155141cc406Sopenharmony_ci avahi_simple_poll_quit(simple_poll); 156141cc406Sopenharmony_ci } 157141cc406Sopenharmony_ci break; 158141cc406Sopenharmony_ci } 159141cc406Sopenharmony_ci} 160141cc406Sopenharmony_ci 161141cc406Sopenharmony_ci/** 162141cc406Sopenharmony_ci * \fn static void client_callback(AvahiClient *c, AvahiClientState state, 163141cc406Sopenharmony_ci * AVAHI_GCC_UNUSED void *userdata) 164141cc406Sopenharmony_ci * \brief Callback Function that quit if it doesn't find a connected scanner, 165141cc406Sopenharmony_ci * possible thanks the "Hello Protocol". 166141cc406Sopenharmony_ci * --> Waiting for a answer by the scanner to continue the avahi process. 167141cc406Sopenharmony_ci */ 168141cc406Sopenharmony_cistatic void 169141cc406Sopenharmony_ciclient_callback(AvahiClient *c, AvahiClientState state, 170141cc406Sopenharmony_ci AVAHI_GCC_UNUSED void *userdata) 171141cc406Sopenharmony_ci{ 172141cc406Sopenharmony_ci assert(c); 173141cc406Sopenharmony_ci if (state == AVAHI_CLIENT_FAILURE) 174141cc406Sopenharmony_ci avahi_simple_poll_quit(simple_poll); 175141cc406Sopenharmony_ci} 176141cc406Sopenharmony_ci 177141cc406Sopenharmony_ci/** 178141cc406Sopenharmony_ci * \fn ESCL_Device *escl_devices(SANE_Status *status) 179141cc406Sopenharmony_ci * \brief Function that calls all the avahi functions and then, recovers the 180141cc406Sopenharmony_ci * connected eSCL devices. 181141cc406Sopenharmony_ci * This function is called in the 'sane_get_devices' function. 182141cc406Sopenharmony_ci * 183141cc406Sopenharmony_ci * \return NULL (the eSCL devices found) 184141cc406Sopenharmony_ci */ 185141cc406Sopenharmony_ciESCL_Device * 186141cc406Sopenharmony_ciescl_devices(SANE_Status *status) 187141cc406Sopenharmony_ci{ 188141cc406Sopenharmony_ci AvahiClient *client = NULL; 189141cc406Sopenharmony_ci AvahiServiceBrowser *sb = NULL; 190141cc406Sopenharmony_ci int error; 191141cc406Sopenharmony_ci 192141cc406Sopenharmony_ci count_finish = 0; 193141cc406Sopenharmony_ci 194141cc406Sopenharmony_ci *status = SANE_STATUS_GOOD; 195141cc406Sopenharmony_ci if (!(simple_poll = avahi_simple_poll_new())) { 196141cc406Sopenharmony_ci DBG( 1, "Failed to create simple poll object.\n"); 197141cc406Sopenharmony_ci *status = SANE_STATUS_INVAL; 198141cc406Sopenharmony_ci goto fail; 199141cc406Sopenharmony_ci } 200141cc406Sopenharmony_ci client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, 201141cc406Sopenharmony_ci client_callback, NULL, &error); 202141cc406Sopenharmony_ci if (!client) { 203141cc406Sopenharmony_ci DBG( 1, "Failed to create client: %s\n", avahi_strerror(error)); 204141cc406Sopenharmony_ci *status = SANE_STATUS_INVAL; 205141cc406Sopenharmony_ci goto fail; 206141cc406Sopenharmony_ci } 207141cc406Sopenharmony_ci if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, 208141cc406Sopenharmony_ci AVAHI_PROTO_UNSPEC, "_uscan._tcp", 209141cc406Sopenharmony_ci NULL, 0, browse_callback, client))) { 210141cc406Sopenharmony_ci DBG( 1, "Failed to create service browser: %s\n", 211141cc406Sopenharmony_ci avahi_strerror(avahi_client_errno(client))); 212141cc406Sopenharmony_ci *status = SANE_STATUS_INVAL; 213141cc406Sopenharmony_ci goto fail; 214141cc406Sopenharmony_ci } 215141cc406Sopenharmony_ci if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, 216141cc406Sopenharmony_ci AVAHI_PROTO_UNSPEC, 217141cc406Sopenharmony_ci "_uscans._tcp", NULL, 0, 218141cc406Sopenharmony_ci browse_callback, client))) { 219141cc406Sopenharmony_ci DBG( 1, "Failed to create service browser: %s\n", 220141cc406Sopenharmony_ci avahi_strerror(avahi_client_errno(client))); 221141cc406Sopenharmony_ci *status = SANE_STATUS_INVAL; 222141cc406Sopenharmony_ci goto fail; 223141cc406Sopenharmony_ci } 224141cc406Sopenharmony_ci avahi_simple_poll_loop(simple_poll); 225141cc406Sopenharmony_cifail: 226141cc406Sopenharmony_ci if (sb) 227141cc406Sopenharmony_ci avahi_service_browser_free(sb); 228141cc406Sopenharmony_ci if (client) 229141cc406Sopenharmony_ci avahi_client_free(client); 230141cc406Sopenharmony_ci if (simple_poll) 231141cc406Sopenharmony_ci avahi_simple_poll_free(simple_poll); 232141cc406Sopenharmony_ci return (NULL); 233141cc406Sopenharmony_ci} 234