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