Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/tools/ippfind.c
1090 views
1
/*
2
* Utility to find IPP printers via Bonjour/DNS-SD and optionally run
3
* commands such as IPP and Bonjour conformance tests. This tool is
4
* inspired by the UNIX "find" command, thus its name.
5
*
6
* Copyright © 2021-2022 by OpenPrinting.
7
* Copyright © 2020 by the IEEE-ISTO Printer Working Group
8
* Copyright © 2008-2018 by Apple Inc.
9
*
10
* Licensed under Apache License v2.0. See the file "LICENSE" for more
11
* information.
12
*/
13
14
/*
15
* Include necessary headers.
16
*/
17
18
#define _CUPS_NO_DEPRECATED
19
#include <cups/cups-private.h>
20
#ifdef _WIN32
21
# include <process.h>
22
# include <sys/timeb.h>
23
#else
24
# include <sys/wait.h>
25
#endif /* _WIN32 */
26
#include <regex.h>
27
#ifdef HAVE_MDNSRESPONDER
28
# include <dns_sd.h>
29
#elif defined(HAVE_AVAHI)
30
# include <avahi-client/client.h>
31
# include <avahi-client/lookup.h>
32
# include <avahi-common/simple-watch.h>
33
# include <avahi-common/domain.h>
34
# include <avahi-common/error.h>
35
# include <avahi-common/malloc.h>
36
# define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
37
#endif /* HAVE_MDNSRESPONDER */
38
39
#ifndef _WIN32
40
extern char **environ; /* Process environment variables */
41
#endif /* !_WIN32 */
42
43
44
/*
45
* Structures...
46
*/
47
48
typedef enum ippfind_exit_e /* Exit codes */
49
{
50
IPPFIND_EXIT_TRUE = 0, /* OK and result is true */
51
IPPFIND_EXIT_FALSE, /* OK but result is false*/
52
IPPFIND_EXIT_BONJOUR, /* Browse/resolve failure */
53
IPPFIND_EXIT_SYNTAX, /* Bad option or syntax error */
54
IPPFIND_EXIT_MEMORY /* Out of memory */
55
} ippfind_exit_t;
56
57
typedef enum ippfind_op_e /* Operations for expressions */
58
{
59
/* "Evaluation" operations */
60
IPPFIND_OP_NONE, /* No operation */
61
IPPFIND_OP_AND, /* Logical AND of all children */
62
IPPFIND_OP_OR, /* Logical OR of all children */
63
IPPFIND_OP_TRUE, /* Always true */
64
IPPFIND_OP_FALSE, /* Always false */
65
IPPFIND_OP_IS_LOCAL, /* Is a local service */
66
IPPFIND_OP_IS_REMOTE, /* Is a remote service */
67
IPPFIND_OP_DOMAIN_REGEX, /* Domain matches regular expression */
68
IPPFIND_OP_NAME_REGEX, /* Name matches regular expression */
69
IPPFIND_OP_NAME_LITERAL, /* Name matches literal string */
70
IPPFIND_OP_HOST_REGEX, /* Hostname matches regular expression */
71
IPPFIND_OP_PORT_RANGE, /* Port matches range */
72
IPPFIND_OP_PATH_REGEX, /* Path matches regular expression */
73
IPPFIND_OP_TXT_EXISTS, /* TXT record key exists */
74
IPPFIND_OP_TXT_REGEX, /* TXT record key matches regular expression */
75
IPPFIND_OP_URI_REGEX, /* URI matches regular expression */
76
77
/* "Output" operations */
78
IPPFIND_OP_EXEC, /* Execute when true */
79
IPPFIND_OP_LIST, /* List when true */
80
IPPFIND_OP_PRINT_NAME, /* Print URI when true */
81
IPPFIND_OP_PRINT_URI, /* Print name when true */
82
IPPFIND_OP_QUIET /* No output when true */
83
} ippfind_op_t;
84
85
typedef struct ippfind_expr_s /* Expression */
86
{
87
struct ippfind_expr_s
88
*prev, /* Previous expression */
89
*next, /* Next expression */
90
*parent, /* Parent expressions */
91
*child; /* Child expressions */
92
ippfind_op_t op; /* Operation code (see above) */
93
int invert; /* Invert the result */
94
char *name; /* TXT record key or literal name */
95
regex_t re; /* Regular expression for matching */
96
int range[2]; /* Port number range */
97
int num_args; /* Number of arguments for exec */
98
char **args; /* Arguments for exec */
99
} ippfind_expr_t;
100
101
typedef struct ippfind_srv_s /* Service information */
102
{
103
#ifdef HAVE_MDNSRESPONDER
104
DNSServiceRef ref; /* Service reference for query */
105
#elif defined(HAVE_AVAHI)
106
AvahiServiceResolver *ref; /* Resolver */
107
#endif /* HAVE_MDNSRESPONDER */
108
char *name, /* Service name */
109
*domain, /* Domain name */
110
*regtype, /* Registration type */
111
*fullName, /* Full name */
112
*host, /* Hostname */
113
*resource, /* Resource path */
114
*uri; /* URI */
115
int num_txt; /* Number of TXT record keys */
116
cups_option_t *txt; /* TXT record keys */
117
int port, /* Port number */
118
is_local, /* Is a local service? */
119
is_processed, /* Did we process the service? */
120
is_resolved; /* Got the resolve data? */
121
} ippfind_srv_t;
122
123
124
/*
125
* Local globals...
126
*/
127
128
#ifdef HAVE_MDNSRESPONDER
129
static DNSServiceRef dnssd_ref; /* Master service reference */
130
#elif defined(HAVE_AVAHI)
131
static AvahiClient *avahi_client = NULL;/* Client information */
132
static int avahi_got_data = 0; /* Got data from poll? */
133
static AvahiSimplePoll *avahi_poll = NULL;
134
/* Poll information */
135
#endif /* HAVE_MDNSRESPONDER */
136
137
static int address_family = AF_UNSPEC;
138
/* Address family for LIST */
139
static int bonjour_error = 0; /* Error browsing/resolving? */
140
static double bonjour_timeout = 1.0; /* Timeout in seconds */
141
static int ipp_version = 20; /* IPP version for LIST */
142
143
144
/*
145
* Local functions...
146
*/
147
148
#ifdef HAVE_MDNSRESPONDER
149
static void DNSSD_API browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
150
static void DNSSD_API browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, const char *replyDomain, void *context) _CUPS_NONNULL(1,5,6,7,8);
151
#elif defined(HAVE_AVAHI)
152
static void browse_callback(AvahiServiceBrowser *browser,
153
AvahiIfIndex interface,
154
AvahiProtocol protocol,
155
AvahiBrowserEvent event,
156
const char *serviceName,
157
const char *regtype,
158
const char *replyDomain,
159
AvahiLookupResultFlags flags,
160
void *context);
161
static void client_callback(AvahiClient *client,
162
AvahiClientState state,
163
void *context);
164
#endif /* HAVE_MDNSRESPONDER */
165
166
static int compare_services(ippfind_srv_t *a, ippfind_srv_t *b);
167
static const char *dnssd_error_string(int error);
168
static int eval_expr(ippfind_srv_t *service,
169
ippfind_expr_t *expressions);
170
static int exec_program(ippfind_srv_t *service, int num_args,
171
char **args);
172
static ippfind_srv_t *get_service(cups_array_t *services, const char *serviceName, const char *regtype, const char *replyDomain) _CUPS_NONNULL(1,2,3,4);
173
static double get_time(void);
174
static int list_service(ippfind_srv_t *service);
175
static ippfind_expr_t *new_expr(ippfind_op_t op, int invert,
176
const char *value, const char *regex,
177
char **args);
178
#ifdef HAVE_MDNSRESPONDER
179
static void DNSSD_API resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *fullName, const char *hostTarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) _CUPS_NONNULL(1,5,6,9, 10);
180
#elif defined(HAVE_AVAHI)
181
static int poll_callback(struct pollfd *pollfds,
182
unsigned int num_pollfds, int timeout,
183
void *context);
184
static void resolve_callback(AvahiServiceResolver *res,
185
AvahiIfIndex interface,
186
AvahiProtocol protocol,
187
AvahiResolverEvent event,
188
const char *serviceName,
189
const char *regtype,
190
const char *replyDomain,
191
const char *host_name,
192
const AvahiAddress *address,
193
uint16_t port,
194
AvahiStringList *txt,
195
AvahiLookupResultFlags flags,
196
void *context);
197
#endif /* HAVE_MDNSRESPONDER */
198
static void set_service_uri(ippfind_srv_t *service);
199
static void show_usage(void) _CUPS_NORETURN;
200
static void show_version(void) _CUPS_NORETURN;
201
202
203
/*
204
* 'main()' - Browse for printers.
205
*/
206
207
int /* O - Exit status */
208
main(int argc, /* I - Number of command-line args */
209
char *argv[]) /* I - Command-line arguments */
210
{
211
int i, /* Looping var */
212
have_output = 0,/* Have output expression */
213
status = IPPFIND_EXIT_FALSE;
214
/* Exit status */
215
const char *opt, /* Option character */
216
*search; /* Current browse/resolve string */
217
cups_array_t *searches; /* Things to browse/resolve */
218
cups_array_t *services; /* Service array */
219
ippfind_srv_t *service; /* Current service */
220
ippfind_expr_t *expressions = NULL,
221
/* Expression tree */
222
*temp = NULL, /* New expression */
223
*parent = NULL, /* Parent expression */
224
*current = NULL,/* Current expression */
225
*parens[100]; /* Markers for parenthesis */
226
int num_parens = 0; /* Number of parenthesis */
227
ippfind_op_t logic = IPPFIND_OP_AND;
228
/* Logic for next expression */
229
int invert = 0; /* Invert expression? */
230
int err; /* DNS-SD error */
231
#ifdef HAVE_MDNSRESPONDER
232
fd_set sinput; /* Input set for select() */
233
struct timeval stimeout; /* Timeout for select() */
234
#endif /* HAVE_MDNSRESPONDER */
235
double endtime; /* End time */
236
static const char * const ops[] = /* Node operation names */
237
{
238
"NONE",
239
"AND",
240
"OR",
241
"TRUE",
242
"FALSE",
243
"IS_LOCAL",
244
"IS_REMOTE",
245
"DOMAIN_REGEX",
246
"NAME_REGEX",
247
"NAME_LITERAL",
248
"HOST_REGEX",
249
"PORT_RANGE",
250
"PATH_REGEX",
251
"TXT_EXISTS",
252
"TXT_REGEX",
253
"URI_REGEX",
254
"EXEC",
255
"LIST",
256
"PRINT_NAME",
257
"PRINT_URI",
258
"QUIET"
259
};
260
261
262
/*
263
* Initialize the locale...
264
*/
265
266
_cupsSetLocale(argv);
267
268
/*
269
* Create arrays to track services and things we want to browse/resolve...
270
*/
271
272
searches = cupsArrayNew(NULL, NULL);
273
services = cupsArrayNew((cups_array_func_t)compare_services, NULL);
274
275
/*
276
* Parse command-line...
277
*/
278
279
if (getenv("IPPFIND_DEBUG"))
280
for (i = 1; i < argc; i ++)
281
fprintf(stderr, "argv[%d]=\"%s\"\n", i, argv[i]);
282
283
for (i = 1; i < argc; i ++)
284
{
285
if (argv[i][0] == '-')
286
{
287
if (argv[i][1] == '-')
288
{
289
/*
290
* Parse --option options...
291
*/
292
293
if (!strcmp(argv[i], "--and"))
294
{
295
if (logic == IPPFIND_OP_OR)
296
{
297
_cupsLangPuts(stderr, _("ippfind: Cannot use --and after --or."));
298
show_usage();
299
}
300
301
if (!current)
302
{
303
_cupsLangPuts(stderr,
304
_("ippfind: Missing expression before \"--and\"."));
305
show_usage();
306
}
307
308
temp = NULL;
309
}
310
else if (!strcmp(argv[i], "--domain"))
311
{
312
i ++;
313
if (i >= argc)
314
{
315
_cupsLangPrintf(stderr,
316
_("ippfind: Missing regular expression after %s."),
317
"--domain");
318
show_usage();
319
}
320
321
if ((temp = new_expr(IPPFIND_OP_DOMAIN_REGEX, invert, NULL, argv[i],
322
NULL)) == NULL)
323
return (IPPFIND_EXIT_MEMORY);
324
}
325
else if (!strcmp(argv[i], "--exec"))
326
{
327
i ++;
328
if (i >= argc)
329
{
330
_cupsLangPrintf(stderr, _("ippfind: Expected program after %s."),
331
"--exec");
332
show_usage();
333
}
334
335
if ((temp = new_expr(IPPFIND_OP_EXEC, invert, NULL, NULL,
336
argv + i)) == NULL)
337
return (IPPFIND_EXIT_MEMORY);
338
339
while (i < argc)
340
if (!strcmp(argv[i], ";"))
341
break;
342
else
343
i ++;
344
345
if (i >= argc)
346
{
347
_cupsLangPrintf(stderr, _("ippfind: Expected semi-colon after %s."),
348
"--exec");
349
show_usage();
350
}
351
352
have_output = 1;
353
}
354
else if (!strcmp(argv[i], "--false"))
355
{
356
if ((temp = new_expr(IPPFIND_OP_FALSE, invert, NULL, NULL,
357
NULL)) == NULL)
358
return (IPPFIND_EXIT_MEMORY);
359
}
360
else if (!strcmp(argv[i], "--help"))
361
{
362
show_usage();
363
}
364
else if (!strcmp(argv[i], "--host"))
365
{
366
i ++;
367
if (i >= argc)
368
{
369
_cupsLangPrintf(stderr,
370
_("ippfind: Missing regular expression after %s."),
371
"--host");
372
show_usage();
373
}
374
375
if ((temp = new_expr(IPPFIND_OP_HOST_REGEX, invert, NULL, argv[i],
376
NULL)) == NULL)
377
return (IPPFIND_EXIT_MEMORY);
378
}
379
else if (!strcmp(argv[i], "--ls"))
380
{
381
if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
382
NULL)) == NULL)
383
return (IPPFIND_EXIT_MEMORY);
384
385
have_output = 1;
386
}
387
else if (!strcmp(argv[i], "--local"))
388
{
389
if ((temp = new_expr(IPPFIND_OP_IS_LOCAL, invert, NULL, NULL,
390
NULL)) == NULL)
391
return (IPPFIND_EXIT_MEMORY);
392
}
393
else if (!strcmp(argv[i], "--literal-name"))
394
{
395
i ++;
396
if (i >= argc)
397
{
398
_cupsLangPrintf(stderr, _("ippfind: Missing name after %s."), "--literal-name");
399
show_usage();
400
}
401
402
if ((temp = new_expr(IPPFIND_OP_NAME_LITERAL, invert, argv[i], NULL, NULL)) == NULL)
403
return (IPPFIND_EXIT_MEMORY);
404
}
405
else if (!strcmp(argv[i], "--name"))
406
{
407
i ++;
408
if (i >= argc)
409
{
410
_cupsLangPrintf(stderr,
411
_("ippfind: Missing regular expression after %s."),
412
"--name");
413
show_usage();
414
}
415
416
if ((temp = new_expr(IPPFIND_OP_NAME_REGEX, invert, NULL, argv[i],
417
NULL)) == NULL)
418
return (IPPFIND_EXIT_MEMORY);
419
}
420
else if (!strcmp(argv[i], "--not"))
421
{
422
invert = 1;
423
}
424
else if (!strcmp(argv[i], "--or"))
425
{
426
if (!current)
427
{
428
_cupsLangPuts(stderr,
429
_("ippfind: Missing expression before \"--or\"."));
430
show_usage();
431
}
432
433
logic = IPPFIND_OP_OR;
434
435
if (parent && parent->op == IPPFIND_OP_OR)
436
{
437
/*
438
* Already setup to do "foo --or bar --or baz"...
439
*/
440
441
temp = NULL;
442
}
443
else if (!current->prev && parent)
444
{
445
/*
446
* Change parent node into an OR node...
447
*/
448
449
parent->op = IPPFIND_OP_OR;
450
temp = NULL;
451
}
452
else if (!current->prev)
453
{
454
/*
455
* Need to group "current" in a new OR node...
456
*/
457
458
if ((temp = new_expr(IPPFIND_OP_OR, 0, NULL, NULL,
459
NULL)) == NULL)
460
return (IPPFIND_EXIT_MEMORY);
461
462
temp->parent = parent;
463
temp->child = current;
464
current->parent = temp;
465
466
if (parent)
467
parent->child = temp;
468
else
469
expressions = temp;
470
471
parent = temp;
472
temp = NULL;
473
}
474
else
475
{
476
/*
477
* Need to group previous expressions in an AND node, and then
478
* put that in an OR node...
479
*/
480
481
if ((temp = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
482
NULL)) == NULL)
483
return (IPPFIND_EXIT_MEMORY);
484
485
while (current->prev)
486
{
487
current->parent = temp;
488
current = current->prev;
489
}
490
491
current->parent = temp;
492
temp->child = current;
493
current = temp;
494
495
if ((temp = new_expr(IPPFIND_OP_OR, 0, NULL, NULL,
496
NULL)) == NULL)
497
return (IPPFIND_EXIT_MEMORY);
498
499
temp->parent = parent;
500
current->parent = temp;
501
502
if (parent)
503
parent->child = temp;
504
else
505
expressions = temp;
506
507
parent = temp;
508
temp = NULL;
509
}
510
}
511
else if (!strcmp(argv[i], "--path"))
512
{
513
i ++;
514
if (i >= argc)
515
{
516
_cupsLangPrintf(stderr,
517
_("ippfind: Missing regular expression after %s."),
518
"--path");
519
show_usage();
520
}
521
522
if ((temp = new_expr(IPPFIND_OP_PATH_REGEX, invert, NULL, argv[i],
523
NULL)) == NULL)
524
return (IPPFIND_EXIT_MEMORY);
525
}
526
else if (!strcmp(argv[i], "--port"))
527
{
528
i ++;
529
if (i >= argc)
530
{
531
_cupsLangPrintf(stderr,
532
_("ippfind: Expected port range after %s."),
533
"--port");
534
show_usage();
535
}
536
537
if ((temp = new_expr(IPPFIND_OP_PORT_RANGE, invert, argv[i], NULL,
538
NULL)) == NULL)
539
return (IPPFIND_EXIT_MEMORY);
540
}
541
else if (!strcmp(argv[i], "--print"))
542
{
543
if ((temp = new_expr(IPPFIND_OP_PRINT_URI, invert, NULL, NULL,
544
NULL)) == NULL)
545
return (IPPFIND_EXIT_MEMORY);
546
547
have_output = 1;
548
}
549
else if (!strcmp(argv[i], "--print-name"))
550
{
551
if ((temp = new_expr(IPPFIND_OP_PRINT_NAME, invert, NULL, NULL,
552
NULL)) == NULL)
553
return (IPPFIND_EXIT_MEMORY);
554
555
have_output = 1;
556
}
557
else if (!strcmp(argv[i], "--quiet"))
558
{
559
if ((temp = new_expr(IPPFIND_OP_QUIET, invert, NULL, NULL,
560
NULL)) == NULL)
561
return (IPPFIND_EXIT_MEMORY);
562
563
have_output = 1;
564
}
565
else if (!strcmp(argv[i], "--remote"))
566
{
567
if ((temp = new_expr(IPPFIND_OP_IS_REMOTE, invert, NULL, NULL,
568
NULL)) == NULL)
569
return (IPPFIND_EXIT_MEMORY);
570
}
571
else if (!strcmp(argv[i], "--true"))
572
{
573
if ((temp = new_expr(IPPFIND_OP_TRUE, invert, NULL, argv[i],
574
NULL)) == NULL)
575
return (IPPFIND_EXIT_MEMORY);
576
}
577
else if (!strcmp(argv[i], "--txt"))
578
{
579
i ++;
580
if (i >= argc)
581
{
582
_cupsLangPrintf(stderr, _("ippfind: Expected key name after %s."),
583
"--txt");
584
show_usage();
585
}
586
587
if ((temp = new_expr(IPPFIND_OP_TXT_EXISTS, invert, argv[i], NULL,
588
NULL)) == NULL)
589
return (IPPFIND_EXIT_MEMORY);
590
}
591
else if (!strncmp(argv[i], "--txt-", 6))
592
{
593
const char *key = argv[i] + 6;/* TXT key */
594
595
i ++;
596
if (i >= argc)
597
{
598
_cupsLangPrintf(stderr,
599
_("ippfind: Missing regular expression after %s."),
600
argv[i - 1]);
601
show_usage();
602
}
603
604
if ((temp = new_expr(IPPFIND_OP_TXT_REGEX, invert, key, argv[i],
605
NULL)) == NULL)
606
return (IPPFIND_EXIT_MEMORY);
607
}
608
else if (!strcmp(argv[i], "--uri"))
609
{
610
i ++;
611
if (i >= argc)
612
{
613
_cupsLangPrintf(stderr,
614
_("ippfind: Missing regular expression after %s."),
615
"--uri");
616
show_usage();
617
}
618
619
if ((temp = new_expr(IPPFIND_OP_URI_REGEX, invert, NULL, argv[i],
620
NULL)) == NULL)
621
return (IPPFIND_EXIT_MEMORY);
622
}
623
else if (!strcmp(argv[i], "--version"))
624
{
625
show_version();
626
}
627
else
628
{
629
_cupsLangPrintf(stderr, _("%s: Unknown option \"%s\"."),
630
"ippfind", argv[i]);
631
show_usage();
632
}
633
634
if (temp)
635
{
636
/*
637
* Add new expression...
638
*/
639
640
if (logic == IPPFIND_OP_AND &&
641
current && current->prev &&
642
parent && parent->op != IPPFIND_OP_AND)
643
{
644
/*
645
* Need to re-group "current" in a new AND node...
646
*/
647
648
ippfind_expr_t *tempand; /* Temporary AND node */
649
650
if ((tempand = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
651
NULL)) == NULL)
652
return (IPPFIND_EXIT_MEMORY);
653
654
/*
655
* Replace "current" with new AND node at the end of this list...
656
*/
657
658
current->prev->next = tempand;
659
tempand->prev = current->prev;
660
tempand->parent = parent;
661
662
/*
663
* Add "current to the new AND node...
664
*/
665
666
tempand->child = current;
667
current->parent = tempand;
668
current->prev = NULL;
669
parent = tempand;
670
}
671
672
/*
673
* Add the new node at current level...
674
*/
675
676
temp->parent = parent;
677
temp->prev = current;
678
679
if (current)
680
current->next = temp;
681
else if (parent)
682
parent->child = temp;
683
else
684
expressions = temp;
685
686
current = temp;
687
invert = 0;
688
logic = IPPFIND_OP_AND;
689
temp = NULL;
690
}
691
}
692
else
693
{
694
/*
695
* Parse -o options
696
*/
697
698
for (opt = argv[i] + 1; *opt; opt ++)
699
{
700
switch (*opt)
701
{
702
case '4' :
703
address_family = AF_INET;
704
break;
705
706
case '6' :
707
address_family = AF_INET6;
708
break;
709
710
case 'N' : /* Literal name */
711
i ++;
712
if (i >= argc)
713
{
714
_cupsLangPrintf(stderr, _("ippfind: Missing name after %s."), "-N");
715
show_usage();
716
}
717
718
if ((temp = new_expr(IPPFIND_OP_NAME_LITERAL, invert, argv[i], NULL, NULL)) == NULL)
719
return (IPPFIND_EXIT_MEMORY);
720
break;
721
722
case 'P' :
723
i ++;
724
if (i >= argc)
725
{
726
_cupsLangPrintf(stderr,
727
_("ippfind: Expected port range after %s."),
728
"-P");
729
show_usage();
730
}
731
732
if ((temp = new_expr(IPPFIND_OP_PORT_RANGE, invert, argv[i],
733
NULL, NULL)) == NULL)
734
return (IPPFIND_EXIT_MEMORY);
735
break;
736
737
case 'T' :
738
i ++;
739
if (i >= argc)
740
{
741
_cupsLangPrintf(stderr,
742
_("%s: Missing timeout for \"-T\"."),
743
"ippfind");
744
show_usage();
745
}
746
747
bonjour_timeout = atof(argv[i]);
748
break;
749
750
case 'V' :
751
i ++;
752
if (i >= argc)
753
{
754
_cupsLangPrintf(stderr,
755
_("%s: Missing version for \"-V\"."),
756
"ippfind");
757
show_usage();
758
}
759
760
if (!strcmp(argv[i], "1.1"))
761
ipp_version = 11;
762
else if (!strcmp(argv[i], "2.0"))
763
ipp_version = 20;
764
else if (!strcmp(argv[i], "2.1"))
765
ipp_version = 21;
766
else if (!strcmp(argv[i], "2.2"))
767
ipp_version = 22;
768
else
769
{
770
_cupsLangPrintf(stderr, _("%s: Bad version %s for \"-V\"."),
771
"ippfind", argv[i]);
772
show_usage();
773
}
774
break;
775
776
case 'd' :
777
i ++;
778
if (i >= argc)
779
{
780
_cupsLangPrintf(stderr,
781
_("ippfind: Missing regular expression after "
782
"%s."), "-d");
783
show_usage();
784
}
785
786
if ((temp = new_expr(IPPFIND_OP_DOMAIN_REGEX, invert, NULL,
787
argv[i], NULL)) == NULL)
788
return (IPPFIND_EXIT_MEMORY);
789
break;
790
791
case 'h' :
792
i ++;
793
if (i >= argc)
794
{
795
_cupsLangPrintf(stderr,
796
_("ippfind: Missing regular expression after "
797
"%s."), "-h");
798
show_usage();
799
}
800
801
if ((temp = new_expr(IPPFIND_OP_HOST_REGEX, invert, NULL,
802
argv[i], NULL)) == NULL)
803
return (IPPFIND_EXIT_MEMORY);
804
break;
805
806
case 'l' :
807
if ((temp = new_expr(IPPFIND_OP_LIST, invert, NULL, NULL,
808
NULL)) == NULL)
809
return (IPPFIND_EXIT_MEMORY);
810
811
have_output = 1;
812
break;
813
814
case 'n' :
815
i ++;
816
if (i >= argc)
817
{
818
_cupsLangPrintf(stderr,
819
_("ippfind: Missing regular expression after "
820
"%s."), "-n");
821
show_usage();
822
}
823
824
if ((temp = new_expr(IPPFIND_OP_NAME_REGEX, invert, NULL,
825
argv[i], NULL)) == NULL)
826
return (IPPFIND_EXIT_MEMORY);
827
break;
828
829
case 'p' :
830
if ((temp = new_expr(IPPFIND_OP_PRINT_URI, invert, NULL, NULL,
831
NULL)) == NULL)
832
return (IPPFIND_EXIT_MEMORY);
833
834
have_output = 1;
835
break;
836
837
case 'q' :
838
if ((temp = new_expr(IPPFIND_OP_QUIET, invert, NULL, NULL,
839
NULL)) == NULL)
840
return (IPPFIND_EXIT_MEMORY);
841
842
have_output = 1;
843
break;
844
845
case 'r' :
846
if ((temp = new_expr(IPPFIND_OP_IS_REMOTE, invert, NULL, NULL,
847
NULL)) == NULL)
848
return (IPPFIND_EXIT_MEMORY);
849
break;
850
851
case 's' :
852
if ((temp = new_expr(IPPFIND_OP_PRINT_NAME, invert, NULL, NULL,
853
NULL)) == NULL)
854
return (IPPFIND_EXIT_MEMORY);
855
856
have_output = 1;
857
break;
858
859
case 't' :
860
i ++;
861
if (i >= argc)
862
{
863
_cupsLangPrintf(stderr,
864
_("ippfind: Missing key name after %s."),
865
"-t");
866
show_usage();
867
}
868
869
if ((temp = new_expr(IPPFIND_OP_TXT_EXISTS, invert, argv[i],
870
NULL, NULL)) == NULL)
871
return (IPPFIND_EXIT_MEMORY);
872
break;
873
874
case 'u' :
875
i ++;
876
if (i >= argc)
877
{
878
_cupsLangPrintf(stderr,
879
_("ippfind: Missing regular expression after "
880
"%s."), "-u");
881
show_usage();
882
}
883
884
if ((temp = new_expr(IPPFIND_OP_URI_REGEX, invert, NULL,
885
argv[i], NULL)) == NULL)
886
return (IPPFIND_EXIT_MEMORY);
887
break;
888
889
case 'x' :
890
i ++;
891
if (i >= argc)
892
{
893
_cupsLangPrintf(stderr,
894
_("ippfind: Missing program after %s."),
895
"-x");
896
show_usage();
897
}
898
899
if ((temp = new_expr(IPPFIND_OP_EXEC, invert, NULL, NULL,
900
argv + i)) == NULL)
901
return (IPPFIND_EXIT_MEMORY);
902
903
while (i < argc)
904
if (!strcmp(argv[i], ";"))
905
break;
906
else
907
i ++;
908
909
if (i >= argc)
910
{
911
_cupsLangPrintf(stderr,
912
_("ippfind: Missing semi-colon after %s."),
913
"-x");
914
show_usage();
915
}
916
917
have_output = 1;
918
break;
919
920
default :
921
_cupsLangPrintf(stderr, _("%s: Unknown option \"-%c\"."),
922
"ippfind", *opt);
923
show_usage();
924
}
925
926
if (temp)
927
{
928
/*
929
* Add new expression...
930
*/
931
932
if (logic == IPPFIND_OP_AND &&
933
current && current->prev &&
934
parent && parent->op != IPPFIND_OP_AND)
935
{
936
/*
937
* Need to re-group "current" in a new AND node...
938
*/
939
940
ippfind_expr_t *tempand; /* Temporary AND node */
941
942
if ((tempand = new_expr(IPPFIND_OP_AND, 0, NULL, NULL,
943
NULL)) == NULL)
944
return (IPPFIND_EXIT_MEMORY);
945
946
/*
947
* Replace "current" with new AND node at the end of this list...
948
*/
949
950
current->prev->next = tempand;
951
tempand->prev = current->prev;
952
tempand->parent = parent;
953
954
/*
955
* Add "current to the new AND node...
956
*/
957
958
tempand->child = current;
959
current->parent = tempand;
960
current->prev = NULL;
961
parent = tempand;
962
}
963
964
/*
965
* Add the new node at current level...
966
*/
967
968
temp->parent = parent;
969
temp->prev = current;
970
971
if (current)
972
current->next = temp;
973
else if (parent)
974
parent->child = temp;
975
else
976
expressions = temp;
977
978
current = temp;
979
invert = 0;
980
logic = IPPFIND_OP_AND;
981
temp = NULL;
982
}
983
}
984
}
985
}
986
else if (!strcmp(argv[i], "("))
987
{
988
if (num_parens >= 100)
989
{
990
_cupsLangPuts(stderr, _("ippfind: Too many parenthesis."));
991
show_usage();
992
}
993
994
if ((temp = new_expr(IPPFIND_OP_AND, invert, NULL, NULL, NULL)) == NULL)
995
return (IPPFIND_EXIT_MEMORY);
996
997
parens[num_parens++] = temp;
998
999
if (current)
1000
{
1001
temp->parent = current->parent;
1002
current->next = temp;
1003
temp->prev = current;
1004
}
1005
else
1006
expressions = temp;
1007
1008
parent = temp;
1009
current = NULL;
1010
invert = 0;
1011
logic = IPPFIND_OP_AND;
1012
}
1013
else if (!strcmp(argv[i], ")"))
1014
{
1015
if (num_parens <= 0)
1016
{
1017
_cupsLangPuts(stderr, _("ippfind: Missing open parenthesis."));
1018
show_usage();
1019
}
1020
1021
current = parens[--num_parens];
1022
parent = current->parent;
1023
invert = 0;
1024
logic = IPPFIND_OP_AND;
1025
}
1026
else if (!strcmp(argv[i], "!"))
1027
{
1028
invert = 1;
1029
}
1030
else
1031
{
1032
/*
1033
* _regtype._tcp[,subtype][.domain]
1034
*
1035
* OR
1036
*
1037
* service-name[._regtype._tcp[.domain]]
1038
*/
1039
1040
cupsArrayAdd(searches, argv[i]);
1041
}
1042
}
1043
1044
if (num_parens > 0)
1045
{
1046
_cupsLangPuts(stderr, _("ippfind: Missing close parenthesis."));
1047
show_usage();
1048
}
1049
1050
if (!have_output)
1051
{
1052
/*
1053
* Add an implicit --print-uri to the end...
1054
*/
1055
1056
if ((temp = new_expr(IPPFIND_OP_PRINT_URI, 0, NULL, NULL, NULL)) == NULL)
1057
return (IPPFIND_EXIT_MEMORY);
1058
1059
if (current)
1060
{
1061
while (current->parent)
1062
current = current->parent;
1063
1064
current->next = temp;
1065
temp->prev = current;
1066
}
1067
else
1068
expressions = temp;
1069
}
1070
1071
if (cupsArrayCount(searches) == 0)
1072
{
1073
/*
1074
* Add an implicit browse for IPP printers ("_ipp._tcp")...
1075
*/
1076
1077
cupsArrayAdd(searches, "_ipp._tcp");
1078
}
1079
1080
if (getenv("IPPFIND_DEBUG"))
1081
{
1082
int indent = 4; /* Indentation */
1083
1084
puts("Expression tree:");
1085
current = expressions;
1086
while (current)
1087
{
1088
/*
1089
* Print the current node...
1090
*/
1091
1092
printf("%*s%s%s\n", indent, "", current->invert ? "!" : "",
1093
ops[current->op]);
1094
1095
/*
1096
* Advance to the next node...
1097
*/
1098
1099
if (current->child)
1100
{
1101
current = current->child;
1102
indent += 4;
1103
}
1104
else if (current->next)
1105
current = current->next;
1106
else if (current->parent)
1107
{
1108
while (current->parent)
1109
{
1110
indent -= 4;
1111
current = current->parent;
1112
if (current->next)
1113
break;
1114
}
1115
1116
current = current->next;
1117
}
1118
else
1119
current = NULL;
1120
}
1121
1122
puts("\nSearch items:");
1123
for (search = (const char *)cupsArrayFirst(searches);
1124
search;
1125
search = (const char *)cupsArrayNext(searches))
1126
printf(" %s\n", search);
1127
}
1128
1129
/*
1130
* Start up browsing/resolving...
1131
*/
1132
1133
#ifdef HAVE_MDNSRESPONDER
1134
if ((err = DNSServiceCreateConnection(&dnssd_ref)) != kDNSServiceErr_NoError)
1135
{
1136
_cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
1137
dnssd_error_string(err));
1138
return (IPPFIND_EXIT_BONJOUR);
1139
}
1140
1141
#elif defined(HAVE_AVAHI)
1142
if ((avahi_poll = avahi_simple_poll_new()) == NULL)
1143
{
1144
_cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
1145
strerror(errno));
1146
return (IPPFIND_EXIT_BONJOUR);
1147
}
1148
1149
avahi_simple_poll_set_func(avahi_poll, poll_callback, NULL);
1150
1151
avahi_client = avahi_client_new(avahi_simple_poll_get(avahi_poll),
1152
0, client_callback, avahi_poll, &err);
1153
if (!avahi_client)
1154
{
1155
_cupsLangPrintf(stderr, _("ippfind: Unable to use Bonjour: %s"),
1156
dnssd_error_string(err));
1157
return (IPPFIND_EXIT_BONJOUR);
1158
}
1159
#endif /* HAVE_MDNSRESPONDER */
1160
1161
for (search = (const char *)cupsArrayFirst(searches);
1162
search;
1163
search = (const char *)cupsArrayNext(searches))
1164
{
1165
char buf[1024], /* Full name string */
1166
*name = NULL, /* Service instance name */
1167
*regtype, /* Registration type */
1168
*domain; /* Domain, if any */
1169
1170
strlcpy(buf, search, sizeof(buf));
1171
1172
if (!strncmp(buf, "_http._", 7) || !strncmp(buf, "_https._", 8) || !strncmp(buf, "_ipp._", 6) || !strncmp(buf, "_ipps._", 7))
1173
{
1174
regtype = buf;
1175
}
1176
else if ((regtype = strstr(buf, "._")) != NULL)
1177
{
1178
if (strcmp(regtype, "._tcp"))
1179
{
1180
/*
1181
* "something._protocol._tcp" -> search for something with the given
1182
* protocol...
1183
*/
1184
1185
name = buf;
1186
*regtype++ = '\0';
1187
}
1188
else
1189
{
1190
/*
1191
* "_protocol._tcp" -> search for everything with the given protocol...
1192
*/
1193
1194
/* name = NULL; */
1195
regtype = buf;
1196
}
1197
}
1198
else
1199
{
1200
/*
1201
* "something" -> search for something with IPP protocol...
1202
*/
1203
1204
name = buf;
1205
regtype = "_ipp._tcp";
1206
}
1207
1208
for (domain = regtype; *domain; domain ++)
1209
{
1210
if (*domain == '.' && domain[1] != '_')
1211
{
1212
*domain++ = '\0';
1213
break;
1214
}
1215
}
1216
1217
if (!*domain)
1218
domain = NULL;
1219
1220
if (name)
1221
{
1222
/*
1223
* Resolve the given service instance name, regtype, and domain...
1224
*/
1225
1226
if (!domain)
1227
domain = "local.";
1228
1229
service = get_service(services, name, regtype, domain);
1230
1231
if (getenv("IPPFIND_DEBUG"))
1232
fprintf(stderr, "Resolving name=\"%s\", regtype=\"%s\", domain=\"%s\"\n", name, regtype, domain);
1233
1234
#ifdef HAVE_MDNSRESPONDER
1235
service->ref = dnssd_ref;
1236
err = DNSServiceResolve(&(service->ref),
1237
kDNSServiceFlagsShareConnection, 0, name,
1238
regtype, domain, resolve_callback,
1239
service);
1240
1241
#elif defined(HAVE_AVAHI)
1242
service->ref = avahi_service_resolver_new(avahi_client, AVAHI_IF_UNSPEC,
1243
AVAHI_PROTO_UNSPEC, name,
1244
regtype, domain,
1245
AVAHI_PROTO_UNSPEC, 0,
1246
resolve_callback, service);
1247
if (service->ref)
1248
err = 0;
1249
else
1250
err = avahi_client_errno(avahi_client);
1251
#endif /* HAVE_MDNSRESPONDER */
1252
}
1253
else
1254
{
1255
/*
1256
* Browse for services of the given type...
1257
*/
1258
1259
if (getenv("IPPFIND_DEBUG"))
1260
fprintf(stderr, "Browsing for regtype=\"%s\", domain=\"%s\"\n", regtype, domain);
1261
1262
#ifdef HAVE_MDNSRESPONDER
1263
DNSServiceRef ref; /* Browse reference */
1264
1265
ref = dnssd_ref;
1266
err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection, 0, regtype,
1267
domain, browse_callback, services);
1268
1269
if (!err)
1270
{
1271
ref = dnssd_ref;
1272
err = DNSServiceBrowse(&ref, kDNSServiceFlagsShareConnection,
1273
kDNSServiceInterfaceIndexLocalOnly, regtype,
1274
domain, browse_local_callback, services);
1275
}
1276
1277
#elif defined(HAVE_AVAHI)
1278
char *subtype, /* Sub-type, if any */
1279
subtype_buf[256]; /* Sub-type buffer */
1280
1281
if ((subtype = strstr(regtype, ",_")) != NULL)
1282
{
1283
*subtype++ = '\0';
1284
snprintf(subtype_buf, sizeof(subtype_buf), "%s._sub.%s", subtype, regtype);
1285
regtype = subtype_buf;
1286
}
1287
1288
if (avahi_service_browser_new(avahi_client, AVAHI_IF_UNSPEC,
1289
AVAHI_PROTO_UNSPEC, regtype, domain, 0,
1290
browse_callback, services))
1291
err = 0;
1292
else
1293
err = avahi_client_errno(avahi_client);
1294
#endif /* HAVE_MDNSRESPONDER */
1295
}
1296
1297
if (err)
1298
{
1299
_cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
1300
dnssd_error_string(err));
1301
1302
return (IPPFIND_EXIT_BONJOUR);
1303
}
1304
}
1305
1306
/*
1307
* Process browse/resolve requests...
1308
*/
1309
1310
if (bonjour_timeout > 1.0)
1311
endtime = get_time() + bonjour_timeout;
1312
else
1313
endtime = get_time() + 300.0;
1314
1315
while (get_time() < endtime)
1316
{
1317
int process = 0; /* Process services? */
1318
1319
#ifdef HAVE_MDNSRESPONDER
1320
int fd = DNSServiceRefSockFD(dnssd_ref);
1321
/* File descriptor for DNS-SD */
1322
1323
FD_ZERO(&sinput);
1324
FD_SET(fd, &sinput);
1325
1326
stimeout.tv_sec = 0;
1327
stimeout.tv_usec = 500000;
1328
1329
if (select(fd + 1, &sinput, NULL, NULL, &stimeout) < 0)
1330
continue;
1331
1332
if (FD_ISSET(fd, &sinput))
1333
{
1334
/*
1335
* Process responses...
1336
*/
1337
1338
DNSServiceProcessResult(dnssd_ref);
1339
}
1340
else
1341
{
1342
/*
1343
* Time to process services...
1344
*/
1345
1346
process = 1;
1347
}
1348
1349
#elif defined(HAVE_AVAHI)
1350
avahi_got_data = 0;
1351
1352
if (avahi_simple_poll_iterate(avahi_poll, 500) > 0)
1353
{
1354
/*
1355
* We've been told to exit the loop. Perhaps the connection to
1356
* Avahi failed.
1357
*/
1358
1359
return (IPPFIND_EXIT_BONJOUR);
1360
}
1361
1362
if (!avahi_got_data)
1363
{
1364
/*
1365
* Time to process services...
1366
*/
1367
1368
process = 1;
1369
}
1370
#endif /* HAVE_MDNSRESPONDER */
1371
1372
if (process)
1373
{
1374
/*
1375
* Process any services that we have found...
1376
*/
1377
1378
int active = 0, /* Number of active resolves */
1379
resolved = 0, /* Number of resolved services */
1380
processed = 0; /* Number of processed services */
1381
1382
for (service = (ippfind_srv_t *)cupsArrayFirst(services);
1383
service;
1384
service = (ippfind_srv_t *)cupsArrayNext(services))
1385
{
1386
if (service->is_processed)
1387
processed ++;
1388
1389
if (service->is_resolved)
1390
resolved ++;
1391
1392
if (!service->ref && !service->is_resolved)
1393
{
1394
/*
1395
* Found a service, now resolve it (but limit to 50 active resolves...)
1396
*/
1397
1398
if (active < 50)
1399
{
1400
#ifdef HAVE_MDNSRESPONDER
1401
service->ref = dnssd_ref;
1402
err = DNSServiceResolve(&(service->ref),
1403
kDNSServiceFlagsShareConnection, 0,
1404
service->name, service->regtype,
1405
service->domain, resolve_callback,
1406
service);
1407
1408
#elif defined(HAVE_AVAHI)
1409
service->ref = avahi_service_resolver_new(avahi_client,
1410
AVAHI_IF_UNSPEC,
1411
AVAHI_PROTO_UNSPEC,
1412
service->name,
1413
service->regtype,
1414
service->domain,
1415
AVAHI_PROTO_UNSPEC, 0,
1416
resolve_callback,
1417
service);
1418
if (service->ref)
1419
err = 0;
1420
else
1421
err = avahi_client_errno(avahi_client);
1422
#endif /* HAVE_MDNSRESPONDER */
1423
1424
if (err)
1425
{
1426
_cupsLangPrintf(stderr,
1427
_("ippfind: Unable to browse or resolve: %s"),
1428
dnssd_error_string(err));
1429
return (IPPFIND_EXIT_BONJOUR);
1430
}
1431
1432
active ++;
1433
}
1434
}
1435
else if (service->is_resolved && !service->is_processed)
1436
{
1437
/*
1438
* Resolved, not process this service against the expressions...
1439
*/
1440
1441
if (service->ref)
1442
{
1443
#ifdef HAVE_MDNSRESPONDER
1444
DNSServiceRefDeallocate(service->ref);
1445
#else
1446
avahi_service_resolver_free(service->ref);
1447
#endif /* HAVE_MDNSRESPONDER */
1448
1449
service->ref = NULL;
1450
}
1451
1452
if (eval_expr(service, expressions))
1453
status = IPPFIND_EXIT_TRUE;
1454
1455
service->is_processed = 1;
1456
}
1457
else if (service->ref)
1458
active ++;
1459
}
1460
1461
/*
1462
* If we have processed all services we have discovered, then we are done.
1463
*/
1464
1465
if (processed == cupsArrayCount(services) && bonjour_timeout <= 1.0)
1466
break;
1467
}
1468
}
1469
1470
if (bonjour_error)
1471
return (IPPFIND_EXIT_BONJOUR);
1472
else
1473
return (status);
1474
}
1475
1476
1477
#ifdef HAVE_MDNSRESPONDER
1478
/*
1479
* 'browse_callback()' - Browse devices.
1480
*/
1481
1482
static void DNSSD_API
1483
browse_callback(
1484
DNSServiceRef sdRef, /* I - Service reference */
1485
DNSServiceFlags flags, /* I - Option flags */
1486
uint32_t interfaceIndex, /* I - Interface number */
1487
DNSServiceErrorType errorCode, /* I - Error, if any */
1488
const char *serviceName, /* I - Name of service/device */
1489
const char *regtype, /* I - Type of service */
1490
const char *replyDomain, /* I - Service domain */
1491
void *context) /* I - Services array */
1492
{
1493
/*
1494
* Only process "add" data...
1495
*/
1496
1497
(void)sdRef;
1498
(void)interfaceIndex;
1499
1500
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1501
return;
1502
1503
/*
1504
* Get the device...
1505
*/
1506
1507
get_service((cups_array_t *)context, serviceName, regtype, replyDomain);
1508
}
1509
1510
1511
/*
1512
* 'browse_local_callback()' - Browse local devices.
1513
*/
1514
1515
static void DNSSD_API
1516
browse_local_callback(
1517
DNSServiceRef sdRef, /* I - Service reference */
1518
DNSServiceFlags flags, /* I - Option flags */
1519
uint32_t interfaceIndex, /* I - Interface number */
1520
DNSServiceErrorType errorCode, /* I - Error, if any */
1521
const char *serviceName, /* I - Name of service/device */
1522
const char *regtype, /* I - Type of service */
1523
const char *replyDomain, /* I - Service domain */
1524
void *context) /* I - Services array */
1525
{
1526
ippfind_srv_t *service; /* Service */
1527
1528
1529
/*
1530
* Only process "add" data...
1531
*/
1532
1533
(void)sdRef;
1534
(void)interfaceIndex;
1535
1536
if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1537
return;
1538
1539
/*
1540
* Get the device...
1541
*/
1542
1543
service = get_service((cups_array_t *)context, serviceName, regtype,
1544
replyDomain);
1545
service->is_local = 1;
1546
}
1547
#endif /* HAVE_MDNSRESPONDER */
1548
1549
1550
#ifdef HAVE_AVAHI
1551
/*
1552
* 'browse_callback()' - Browse devices.
1553
*/
1554
1555
static void
1556
browse_callback(
1557
AvahiServiceBrowser *browser, /* I - Browser */
1558
AvahiIfIndex interface, /* I - Interface index (unused) */
1559
AvahiProtocol protocol, /* I - Network protocol (unused) */
1560
AvahiBrowserEvent event, /* I - What happened */
1561
const char *name, /* I - Service name */
1562
const char *type, /* I - Registration type */
1563
const char *domain, /* I - Domain */
1564
AvahiLookupResultFlags flags, /* I - Flags */
1565
void *context) /* I - Services array */
1566
{
1567
AvahiClient *client = avahi_service_browser_get_client(browser);
1568
/* Client information */
1569
ippfind_srv_t *service; /* Service information */
1570
1571
1572
(void)interface;
1573
(void)protocol;
1574
(void)context;
1575
1576
switch (event)
1577
{
1578
case AVAHI_BROWSER_FAILURE:
1579
fprintf(stderr, "DEBUG: browse_callback: %s\n",
1580
avahi_strerror(avahi_client_errno(client)));
1581
bonjour_error = 1;
1582
avahi_simple_poll_quit(avahi_poll);
1583
break;
1584
1585
case AVAHI_BROWSER_NEW:
1586
/*
1587
* This object is new on the network. Create a device entry for it if
1588
* it doesn't yet exist.
1589
*/
1590
1591
service = get_service((cups_array_t *)context, name, type, domain);
1592
1593
if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
1594
service->is_local = 1;
1595
break;
1596
1597
case AVAHI_BROWSER_REMOVE:
1598
case AVAHI_BROWSER_ALL_FOR_NOW:
1599
case AVAHI_BROWSER_CACHE_EXHAUSTED:
1600
break;
1601
}
1602
}
1603
1604
1605
/*
1606
* 'client_callback()' - Avahi client callback function.
1607
*/
1608
1609
static void
1610
client_callback(
1611
AvahiClient *client, /* I - Client information (unused) */
1612
AvahiClientState state, /* I - Current state */
1613
void *context) /* I - User data (unused) */
1614
{
1615
(void)client;
1616
(void)context;
1617
1618
/*
1619
* If the connection drops, quit.
1620
*/
1621
1622
if (state == AVAHI_CLIENT_FAILURE)
1623
{
1624
fputs("DEBUG: Avahi connection failed.\n", stderr);
1625
bonjour_error = 1;
1626
avahi_simple_poll_quit(avahi_poll);
1627
}
1628
}
1629
#endif /* HAVE_AVAHI */
1630
1631
1632
/*
1633
* 'compare_services()' - Compare two devices.
1634
*/
1635
1636
static int /* O - Result of comparison */
1637
compare_services(ippfind_srv_t *a, /* I - First device */
1638
ippfind_srv_t *b) /* I - Second device */
1639
{
1640
return (strcmp(a->name, b->name));
1641
}
1642
1643
1644
/*
1645
* 'dnssd_error_string()' - Return an error string for an error code.
1646
*/
1647
1648
static const char * /* O - Error message */
1649
dnssd_error_string(int error) /* I - Error number */
1650
{
1651
# ifdef HAVE_MDNSRESPONDER
1652
switch (error)
1653
{
1654
case kDNSServiceErr_NoError :
1655
return ("OK.");
1656
1657
default :
1658
case kDNSServiceErr_Unknown :
1659
return ("Unknown error.");
1660
1661
case kDNSServiceErr_NoSuchName :
1662
return ("Service not found.");
1663
1664
case kDNSServiceErr_NoMemory :
1665
return ("Out of memory.");
1666
1667
case kDNSServiceErr_BadParam :
1668
return ("Bad parameter.");
1669
1670
case kDNSServiceErr_BadReference :
1671
return ("Bad service reference.");
1672
1673
case kDNSServiceErr_BadState :
1674
return ("Bad state.");
1675
1676
case kDNSServiceErr_BadFlags :
1677
return ("Bad flags.");
1678
1679
case kDNSServiceErr_Unsupported :
1680
return ("Unsupported.");
1681
1682
case kDNSServiceErr_NotInitialized :
1683
return ("Not initialized.");
1684
1685
case kDNSServiceErr_AlreadyRegistered :
1686
return ("Already registered.");
1687
1688
case kDNSServiceErr_NameConflict :
1689
return ("Name conflict.");
1690
1691
case kDNSServiceErr_Invalid :
1692
return ("Invalid name.");
1693
1694
case kDNSServiceErr_Firewall :
1695
return ("Firewall prevents registration.");
1696
1697
case kDNSServiceErr_Incompatible :
1698
return ("Client library incompatible.");
1699
1700
case kDNSServiceErr_BadInterfaceIndex :
1701
return ("Bad interface index.");
1702
1703
case kDNSServiceErr_Refused :
1704
return ("Server prevents registration.");
1705
1706
case kDNSServiceErr_NoSuchRecord :
1707
return ("Record not found.");
1708
1709
case kDNSServiceErr_NoAuth :
1710
return ("Authentication required.");
1711
1712
case kDNSServiceErr_NoSuchKey :
1713
return ("Encryption key not found.");
1714
1715
case kDNSServiceErr_NATTraversal :
1716
return ("Unable to traverse NAT boundary.");
1717
1718
case kDNSServiceErr_DoubleNAT :
1719
return ("Unable to traverse double-NAT boundary.");
1720
1721
case kDNSServiceErr_BadTime :
1722
return ("Bad system time.");
1723
1724
case kDNSServiceErr_BadSig :
1725
return ("Bad signature.");
1726
1727
case kDNSServiceErr_BadKey :
1728
return ("Bad encryption key.");
1729
1730
case kDNSServiceErr_Transient :
1731
return ("Transient error occurred - please try again.");
1732
1733
case kDNSServiceErr_ServiceNotRunning :
1734
return ("Server not running.");
1735
1736
case kDNSServiceErr_NATPortMappingUnsupported :
1737
return ("NAT doesn't support NAT-PMP or UPnP.");
1738
1739
case kDNSServiceErr_NATPortMappingDisabled :
1740
return ("NAT supports NAT-PNP or UPnP but it is disabled.");
1741
1742
case kDNSServiceErr_NoRouter :
1743
return ("No Internet/default router configured.");
1744
1745
case kDNSServiceErr_PollingMode :
1746
return ("Service polling mode error.");
1747
1748
#ifndef _WIN32
1749
case kDNSServiceErr_Timeout :
1750
return ("Service timeout.");
1751
#endif /* !_WIN32 */
1752
}
1753
1754
# elif defined(HAVE_AVAHI)
1755
return (avahi_strerror(error));
1756
# endif /* HAVE_MDNSRESPONDER */
1757
}
1758
1759
1760
/*
1761
* 'eval_expr()' - Evaluate the expressions against the specified service.
1762
*
1763
* Returns 1 for true and 0 for false.
1764
*/
1765
1766
static int /* O - Result of evaluation */
1767
eval_expr(ippfind_srv_t *service, /* I - Service */
1768
ippfind_expr_t *expressions) /* I - Expressions */
1769
{
1770
ippfind_op_t logic; /* Logical operation */
1771
int result; /* Result of current expression */
1772
ippfind_expr_t *expression; /* Current expression */
1773
const char *val; /* TXT value */
1774
1775
/*
1776
* Loop through the expressions...
1777
*/
1778
1779
if (expressions && expressions->parent)
1780
logic = expressions->parent->op;
1781
else
1782
logic = IPPFIND_OP_AND;
1783
1784
for (expression = expressions; expression; expression = expression->next)
1785
{
1786
switch (expression->op)
1787
{
1788
default :
1789
case IPPFIND_OP_AND :
1790
case IPPFIND_OP_OR :
1791
if (expression->child)
1792
result = eval_expr(service, expression->child);
1793
else
1794
result = expression->op == IPPFIND_OP_AND;
1795
break;
1796
case IPPFIND_OP_TRUE :
1797
result = 1;
1798
break;
1799
case IPPFIND_OP_FALSE :
1800
result = 0;
1801
break;
1802
case IPPFIND_OP_IS_LOCAL :
1803
result = service->is_local;
1804
break;
1805
case IPPFIND_OP_IS_REMOTE :
1806
result = !service->is_local;
1807
break;
1808
case IPPFIND_OP_DOMAIN_REGEX :
1809
result = !regexec(&(expression->re), service->domain, 0, NULL, 0);
1810
break;
1811
case IPPFIND_OP_NAME_REGEX :
1812
result = !regexec(&(expression->re), service->name, 0, NULL, 0);
1813
break;
1814
case IPPFIND_OP_NAME_LITERAL :
1815
result = !_cups_strcasecmp(expression->name, service->name);
1816
break;
1817
case IPPFIND_OP_HOST_REGEX :
1818
result = !regexec(&(expression->re), service->host, 0, NULL, 0);
1819
break;
1820
case IPPFIND_OP_PORT_RANGE :
1821
result = service->port >= expression->range[0] &&
1822
service->port <= expression->range[1];
1823
break;
1824
case IPPFIND_OP_PATH_REGEX :
1825
result = !regexec(&(expression->re), service->resource, 0, NULL, 0);
1826
break;
1827
case IPPFIND_OP_TXT_EXISTS :
1828
result = cupsGetOption(expression->name, service->num_txt,
1829
service->txt) != NULL;
1830
break;
1831
case IPPFIND_OP_TXT_REGEX :
1832
val = cupsGetOption(expression->name, service->num_txt,
1833
service->txt);
1834
if (val)
1835
result = !regexec(&(expression->re), val, 0, NULL, 0);
1836
else
1837
result = 0;
1838
1839
if (getenv("IPPFIND_DEBUG"))
1840
printf("TXT_REGEX of \"%s\": %d\n", val, result);
1841
break;
1842
case IPPFIND_OP_URI_REGEX :
1843
result = !regexec(&(expression->re), service->uri, 0, NULL, 0);
1844
break;
1845
case IPPFIND_OP_EXEC :
1846
result = exec_program(service, expression->num_args,
1847
expression->args);
1848
break;
1849
case IPPFIND_OP_LIST :
1850
result = list_service(service);
1851
break;
1852
case IPPFIND_OP_PRINT_NAME :
1853
_cupsLangPuts(stdout, service->name);
1854
result = 1;
1855
break;
1856
case IPPFIND_OP_PRINT_URI :
1857
_cupsLangPuts(stdout, service->uri);
1858
result = 1;
1859
break;
1860
case IPPFIND_OP_QUIET :
1861
result = 1;
1862
break;
1863
}
1864
1865
if (expression->invert)
1866
result = !result;
1867
1868
if (logic == IPPFIND_OP_AND && !result)
1869
return (0);
1870
else if (logic == IPPFIND_OP_OR && result)
1871
return (1);
1872
}
1873
1874
return (logic == IPPFIND_OP_AND);
1875
}
1876
1877
1878
/*
1879
* 'exec_program()' - Execute a program for a service.
1880
*/
1881
1882
static int /* O - 1 if program terminated
1883
successfully, 0 otherwise. */
1884
exec_program(ippfind_srv_t *service, /* I - Service */
1885
int num_args, /* I - Number of command-line args */
1886
char **args) /* I - Command-line arguments */
1887
{
1888
char **myargv, /* Command-line arguments */
1889
**myenvp, /* Environment variables */
1890
*ptr, /* Pointer into variable */
1891
domain[1024], /* IPPFIND_SERVICE_DOMAIN */
1892
hostname[1024], /* IPPFIND_SERVICE_HOSTNAME */
1893
name[256], /* IPPFIND_SERVICE_NAME */
1894
port[32], /* IPPFIND_SERVICE_PORT */
1895
regtype[256], /* IPPFIND_SERVICE_REGTYPE */
1896
scheme[128], /* IPPFIND_SERVICE_SCHEME */
1897
uri[1024], /* IPPFIND_SERVICE_URI */
1898
txt[100][256]; /* IPPFIND_TXT_foo */
1899
int i, /* Looping var */
1900
myenvc, /* Number of environment variables */
1901
status; /* Exit status of program */
1902
#ifndef _WIN32
1903
char program[1024]; /* Program to execute */
1904
int pid; /* Process ID */
1905
#endif /* !_WIN32 */
1906
1907
1908
/*
1909
* Environment variables...
1910
*/
1911
1912
snprintf(domain, sizeof(domain), "IPPFIND_SERVICE_DOMAIN=%s",
1913
service->domain);
1914
snprintf(hostname, sizeof(hostname), "IPPFIND_SERVICE_HOSTNAME=%s",
1915
service->host);
1916
snprintf(name, sizeof(name), "IPPFIND_SERVICE_NAME=%s", service->name);
1917
snprintf(port, sizeof(port), "IPPFIND_SERVICE_PORT=%d", service->port);
1918
snprintf(regtype, sizeof(regtype), "IPPFIND_SERVICE_REGTYPE=%s",
1919
service->regtype);
1920
snprintf(scheme, sizeof(scheme), "IPPFIND_SERVICE_SCHEME=%s",
1921
!strncmp(service->regtype, "_http._tcp", 10) ? "http" :
1922
!strncmp(service->regtype, "_https._tcp", 11) ? "https" :
1923
!strncmp(service->regtype, "_ipp._tcp", 9) ? "ipp" :
1924
!strncmp(service->regtype, "_ipps._tcp", 10) ? "ipps" : "lpd");
1925
snprintf(uri, sizeof(uri), "IPPFIND_SERVICE_URI=%s", service->uri);
1926
for (i = 0; i < service->num_txt && i < 100; i ++)
1927
{
1928
snprintf(txt[i], sizeof(txt[i]), "IPPFIND_TXT_%s=%s", service->txt[i].name,
1929
service->txt[i].value);
1930
for (ptr = txt[i] + 12; *ptr && *ptr != '='; ptr ++)
1931
*ptr = (char)_cups_toupper(*ptr);
1932
}
1933
1934
for (i = 0, myenvc = 7 + service->num_txt; environ[i]; i ++)
1935
if (strncmp(environ[i], "IPPFIND_", 8))
1936
myenvc ++;
1937
1938
if ((myenvp = calloc(sizeof(char *), (size_t)(myenvc + 1))) == NULL)
1939
{
1940
_cupsLangPuts(stderr, _("ippfind: Out of memory."));
1941
exit(IPPFIND_EXIT_MEMORY);
1942
}
1943
1944
for (i = 0, myenvc = 0; environ[i]; i ++)
1945
if (strncmp(environ[i], "IPPFIND_", 8))
1946
myenvp[myenvc++] = environ[i];
1947
1948
myenvp[myenvc++] = domain;
1949
myenvp[myenvc++] = hostname;
1950
myenvp[myenvc++] = name;
1951
myenvp[myenvc++] = port;
1952
myenvp[myenvc++] = regtype;
1953
myenvp[myenvc++] = scheme;
1954
myenvp[myenvc++] = uri;
1955
1956
for (i = 0; i < service->num_txt && i < 100; i ++)
1957
myenvp[myenvc++] = txt[i];
1958
1959
/*
1960
* Allocate and copy command-line arguments...
1961
*/
1962
1963
if ((myargv = calloc(sizeof(char *), (size_t)(num_args + 1))) == NULL)
1964
{
1965
_cupsLangPuts(stderr, _("ippfind: Out of memory."));
1966
exit(IPPFIND_EXIT_MEMORY);
1967
}
1968
1969
for (i = 0; i < num_args; i ++)
1970
{
1971
if (strchr(args[i], '{'))
1972
{
1973
char temp[2048], /* Temporary string */
1974
*tptr, /* Pointer into temporary string */
1975
keyword[256], /* {keyword} */
1976
*kptr; /* Pointer into keyword */
1977
1978
for (ptr = args[i], tptr = temp; *ptr; ptr ++)
1979
{
1980
if (*ptr == '{')
1981
{
1982
/*
1983
* Do a {var} substitution...
1984
*/
1985
1986
for (kptr = keyword, ptr ++; *ptr && *ptr != '}'; ptr ++)
1987
if (kptr < (keyword + sizeof(keyword) - 1))
1988
*kptr++ = *ptr;
1989
1990
if (*ptr != '}')
1991
{
1992
_cupsLangPuts(stderr,
1993
_("ippfind: Missing close brace in substitution."));
1994
exit(IPPFIND_EXIT_SYNTAX);
1995
}
1996
1997
*kptr = '\0';
1998
if (!keyword[0] || !strcmp(keyword, "service_uri"))
1999
strlcpy(tptr, service->uri, sizeof(temp) - (size_t)(tptr - temp));
2000
else if (!strcmp(keyword, "service_domain"))
2001
strlcpy(tptr, service->domain, sizeof(temp) - (size_t)(tptr - temp));
2002
else if (!strcmp(keyword, "service_hostname"))
2003
strlcpy(tptr, service->host, sizeof(temp) - (size_t)(tptr - temp));
2004
else if (!strcmp(keyword, "service_name"))
2005
strlcpy(tptr, service->name, sizeof(temp) - (size_t)(tptr - temp));
2006
else if (!strcmp(keyword, "service_path"))
2007
strlcpy(tptr, service->resource, sizeof(temp) - (size_t)(tptr - temp));
2008
else if (!strcmp(keyword, "service_port"))
2009
strlcpy(tptr, port + 21, sizeof(temp) - (size_t)(tptr - temp));
2010
else if (!strcmp(keyword, "service_scheme"))
2011
strlcpy(tptr, scheme + 22, sizeof(temp) - (size_t)(tptr - temp));
2012
else if (!strncmp(keyword, "txt_", 4))
2013
{
2014
const char *val = cupsGetOption(keyword + 4, service->num_txt, service->txt);
2015
if (val)
2016
strlcpy(tptr, val, sizeof(temp) - (size_t)(tptr - temp));
2017
else
2018
*tptr = '\0';
2019
}
2020
else
2021
{
2022
_cupsLangPrintf(stderr, _("ippfind: Unknown variable \"{%s}\"."),
2023
keyword);
2024
exit(IPPFIND_EXIT_SYNTAX);
2025
}
2026
2027
tptr += strlen(tptr);
2028
}
2029
else if (tptr < (temp + sizeof(temp) - 1))
2030
*tptr++ = *ptr;
2031
}
2032
2033
*tptr = '\0';
2034
myargv[i] = strdup(temp);
2035
}
2036
else
2037
myargv[i] = strdup(args[i]);
2038
}
2039
2040
#ifdef _WIN32
2041
if (getenv("IPPFIND_DEBUG"))
2042
{
2043
printf("\nProgram:\n %s\n", args[0]);
2044
puts("\nArguments:");
2045
for (i = 0; i < num_args; i ++)
2046
printf(" %s\n", myargv[i]);
2047
puts("\nEnvironment:");
2048
for (i = 0; i < myenvc; i ++)
2049
printf(" %s\n", myenvp[i]);
2050
}
2051
2052
status = _spawnvpe(_P_WAIT, args[0], myargv, myenvp);
2053
2054
#else
2055
/*
2056
* Execute the program...
2057
*/
2058
2059
if (strchr(args[0], '/') && !access(args[0], X_OK))
2060
strlcpy(program, args[0], sizeof(program));
2061
else if (!cupsFileFind(args[0], getenv("PATH"), 1, program, sizeof(program)))
2062
{
2063
_cupsLangPrintf(stderr, _("ippfind: Unable to execute \"%s\": %s"),
2064
args[0], strerror(ENOENT));
2065
exit(IPPFIND_EXIT_SYNTAX);
2066
}
2067
2068
if (getenv("IPPFIND_DEBUG"))
2069
{
2070
printf("\nProgram:\n %s\n", program);
2071
puts("\nArguments:");
2072
for (i = 0; i < num_args; i ++)
2073
printf(" %s\n", myargv[i]);
2074
puts("\nEnvironment:");
2075
for (i = 0; i < myenvc; i ++)
2076
printf(" %s\n", myenvp[i]);
2077
}
2078
2079
if ((pid = fork()) == 0)
2080
{
2081
/*
2082
* Child comes here...
2083
*/
2084
2085
execve(program, myargv, myenvp);
2086
exit(1);
2087
}
2088
else if (pid < 0)
2089
{
2090
_cupsLangPrintf(stderr, _("ippfind: Unable to execute \"%s\": %s"),
2091
args[0], strerror(errno));
2092
exit(IPPFIND_EXIT_SYNTAX);
2093
}
2094
else
2095
{
2096
/*
2097
* Wait for it to complete...
2098
*/
2099
2100
while (wait(&status) != pid)
2101
;
2102
}
2103
#endif /* _WIN32 */
2104
2105
/*
2106
* Free memory...
2107
*/
2108
2109
for (i = 0; i < num_args; i ++)
2110
free(myargv[i]);
2111
2112
free(myargv);
2113
free(myenvp);
2114
2115
/*
2116
* Return whether the program succeeded or crashed...
2117
*/
2118
2119
if (getenv("IPPFIND_DEBUG"))
2120
{
2121
#ifdef _WIN32
2122
printf("Exit Status: %d\n", status);
2123
#else
2124
if (WIFEXITED(status))
2125
printf("Exit Status: %d\n", WEXITSTATUS(status));
2126
else
2127
printf("Terminating Signal: %d\n", WTERMSIG(status));
2128
#endif /* _WIN32 */
2129
}
2130
2131
return (status == 0);
2132
}
2133
2134
2135
/*
2136
* 'get_service()' - Create or update a device.
2137
*/
2138
2139
static ippfind_srv_t * /* O - Service */
2140
get_service(cups_array_t *services, /* I - Service array */
2141
const char *serviceName, /* I - Name of service/device */
2142
const char *regtype, /* I - Type of service */
2143
const char *replyDomain) /* I - Service domain */
2144
{
2145
ippfind_srv_t key, /* Search key */
2146
*service; /* Service */
2147
char fullName[kDNSServiceMaxDomainName];
2148
/* Full name for query */
2149
2150
2151
/*
2152
* See if this is a new device...
2153
*/
2154
2155
key.name = (char *)serviceName;
2156
key.regtype = (char *)regtype;
2157
2158
for (service = cupsArrayFind(services, &key);
2159
service;
2160
service = cupsArrayNext(services))
2161
if (_cups_strcasecmp(service->name, key.name))
2162
break;
2163
else if (!strcmp(service->regtype, key.regtype))
2164
return (service);
2165
2166
/*
2167
* Yes, add the service...
2168
*/
2169
2170
if ((service = calloc(sizeof(ippfind_srv_t), 1)) == NULL)
2171
return (NULL);
2172
2173
service->name = strdup(serviceName);
2174
service->domain = strdup(replyDomain);
2175
service->regtype = strdup(regtype);
2176
2177
cupsArrayAdd(services, service);
2178
2179
/*
2180
* Set the "full name" of this service, which is used for queries and
2181
* resolves...
2182
*/
2183
2184
#ifdef HAVE_MDNSRESPONDER
2185
DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
2186
#else /* HAVE_AVAHI */
2187
avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName,
2188
regtype, replyDomain);
2189
#endif /* HAVE_MDNSRESPONDER */
2190
2191
service->fullName = strdup(fullName);
2192
2193
return (service);
2194
}
2195
2196
2197
/*
2198
* 'get_time()' - Get the current time-of-day in seconds.
2199
*/
2200
2201
static double
2202
get_time(void)
2203
{
2204
#ifdef _WIN32
2205
struct _timeb curtime; /* Current Windows time */
2206
2207
_ftime(&curtime);
2208
2209
return (curtime.time + 0.001 * curtime.millitm);
2210
2211
#else
2212
struct timeval curtime; /* Current UNIX time */
2213
2214
if (gettimeofday(&curtime, NULL))
2215
return (0.0);
2216
else
2217
return (curtime.tv_sec + 0.000001 * curtime.tv_usec);
2218
#endif /* _WIN32 */
2219
}
2220
2221
2222
/*
2223
* 'list_service()' - List the contents of a service.
2224
*/
2225
2226
static int /* O - 1 if successful, 0 otherwise */
2227
list_service(ippfind_srv_t *service) /* I - Service */
2228
{
2229
http_addrlist_t *addrlist; /* Address(es) of service */
2230
char port[10]; /* Port number of service */
2231
2232
2233
snprintf(port, sizeof(port), "%d", service->port);
2234
2235
if ((addrlist = httpAddrGetList(service->host, address_family, port)) == NULL)
2236
{
2237
_cupsLangPrintf(stdout, "%s unreachable", service->uri);
2238
return (0);
2239
}
2240
2241
if (!strncmp(service->regtype, "_ipp._tcp", 9) ||
2242
!strncmp(service->regtype, "_ipps._tcp", 10))
2243
{
2244
/*
2245
* IPP/IPPS printer
2246
*/
2247
2248
http_t *http; /* HTTP connection */
2249
ipp_t *request, /* IPP request */
2250
*response; /* IPP response */
2251
ipp_attribute_t *attr; /* IPP attribute */
2252
int i, /* Looping var */
2253
count, /* Number of values */
2254
version, /* IPP version */
2255
paccepting; /* printer-is-accepting-jobs value */
2256
ipp_pstate_t pstate; /* printer-state value */
2257
char preasons[1024], /* Comma-delimited printer-state-reasons */
2258
*ptr, /* Pointer into reasons */
2259
*end; /* End of reasons buffer */
2260
static const char * const rattrs[] =/* Requested attributes */
2261
{
2262
"printer-is-accepting-jobs",
2263
"printer-state",
2264
"printer-state-reasons"
2265
};
2266
2267
/*
2268
* Connect to the printer...
2269
*/
2270
2271
http = httpConnect2(service->host, service->port, addrlist, address_family,
2272
!strncmp(service->regtype, "_ipps._tcp", 10) ?
2273
HTTP_ENCRYPTION_ALWAYS :
2274
HTTP_ENCRYPTION_IF_REQUESTED,
2275
1, 30000, NULL);
2276
2277
httpAddrFreeList(addrlist);
2278
2279
if (!http)
2280
{
2281
_cupsLangPrintf(stdout, "%s unavailable", service->uri);
2282
return (0);
2283
}
2284
2285
/*
2286
* Get the current printer state...
2287
*/
2288
2289
response = NULL;
2290
version = ipp_version;
2291
2292
do
2293
{
2294
request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
2295
ippSetVersion(request, version / 10, version % 10);
2296
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
2297
service->uri);
2298
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
2299
"requesting-user-name", NULL, cupsUser());
2300
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
2301
"requested-attributes",
2302
(int)(sizeof(rattrs) / sizeof(rattrs[0])), NULL, rattrs);
2303
2304
response = cupsDoRequest(http, request, service->resource);
2305
2306
if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST && version > 11)
2307
version = 11;
2308
}
2309
while (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE && version > 11);
2310
2311
/*
2312
* Show results...
2313
*/
2314
2315
if (cupsLastError() > IPP_STATUS_OK_EVENTS_COMPLETE)
2316
{
2317
_cupsLangPrintf(stdout, "%s: unavailable", service->uri);
2318
return (0);
2319
}
2320
2321
if ((attr = ippFindAttribute(response, "printer-state",
2322
IPP_TAG_ENUM)) != NULL)
2323
pstate = (ipp_pstate_t)ippGetInteger(attr, 0);
2324
else
2325
pstate = IPP_PSTATE_STOPPED;
2326
2327
if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
2328
IPP_TAG_BOOLEAN)) != NULL)
2329
paccepting = ippGetBoolean(attr, 0);
2330
else
2331
paccepting = 0;
2332
2333
if ((attr = ippFindAttribute(response, "printer-state-reasons",
2334
IPP_TAG_KEYWORD)) != NULL)
2335
{
2336
strlcpy(preasons, ippGetString(attr, 0, NULL), sizeof(preasons));
2337
2338
for (i = 1, count = ippGetCount(attr), ptr = preasons + strlen(preasons),
2339
end = preasons + sizeof(preasons) - 1;
2340
i < count && ptr < end;
2341
i ++, ptr += strlen(ptr))
2342
{
2343
*ptr++ = ',';
2344
strlcpy(ptr, ippGetString(attr, i, NULL), (size_t)(end - ptr + 1));
2345
}
2346
}
2347
else
2348
strlcpy(preasons, "none", sizeof(preasons));
2349
2350
ippDelete(response);
2351
httpClose(http);
2352
2353
_cupsLangPrintf(stdout, "%s %s %s %s", service->uri, ippEnumString("printer-state", (int)pstate), paccepting ? "accepting-jobs" : "not-accepting-jobs", preasons);
2354
}
2355
else if (!strncmp(service->regtype, "_http._tcp", 10) ||
2356
!strncmp(service->regtype, "_https._tcp", 11))
2357
{
2358
/*
2359
* HTTP/HTTPS web page
2360
*/
2361
2362
http_t *http; /* HTTP connection */
2363
http_status_t status; /* HEAD status */
2364
2365
2366
/*
2367
* Connect to the web server...
2368
*/
2369
2370
http = httpConnect2(service->host, service->port, addrlist, address_family,
2371
!strncmp(service->regtype, "_ipps._tcp", 10) ?
2372
HTTP_ENCRYPTION_ALWAYS :
2373
HTTP_ENCRYPTION_IF_REQUESTED,
2374
1, 30000, NULL);
2375
2376
httpAddrFreeList(addrlist);
2377
2378
if (!http)
2379
{
2380
_cupsLangPrintf(stdout, "%s unavailable", service->uri);
2381
return (0);
2382
}
2383
2384
if (httpGet(http, service->resource))
2385
{
2386
_cupsLangPrintf(stdout, "%s unavailable", service->uri);
2387
return (0);
2388
}
2389
2390
do
2391
{
2392
status = httpUpdate(http);
2393
}
2394
while (status == HTTP_STATUS_CONTINUE);
2395
2396
httpFlush(http);
2397
httpClose(http);
2398
2399
if (status >= HTTP_STATUS_BAD_REQUEST)
2400
{
2401
_cupsLangPrintf(stdout, "%s unavailable", service->uri);
2402
return (0);
2403
}
2404
2405
_cupsLangPrintf(stdout, "%s available", service->uri);
2406
}
2407
else if (!strncmp(service->regtype, "_printer._tcp", 13))
2408
{
2409
/*
2410
* LPD printer
2411
*/
2412
2413
int sock; /* Socket */
2414
2415
2416
if (!httpAddrConnect(addrlist, &sock))
2417
{
2418
_cupsLangPrintf(stdout, "%s unavailable", service->uri);
2419
httpAddrFreeList(addrlist);
2420
return (0);
2421
}
2422
2423
_cupsLangPrintf(stdout, "%s available", service->uri);
2424
httpAddrFreeList(addrlist);
2425
2426
httpAddrClose(NULL, sock);
2427
}
2428
else
2429
{
2430
_cupsLangPrintf(stdout, "%s unsupported", service->uri);
2431
httpAddrFreeList(addrlist);
2432
return (0);
2433
}
2434
2435
return (1);
2436
}
2437
2438
2439
/*
2440
* 'new_expr()' - Create a new expression.
2441
*/
2442
2443
static ippfind_expr_t * /* O - New expression */
2444
new_expr(ippfind_op_t op, /* I - Operation */
2445
int invert, /* I - Invert result? */
2446
const char *value, /* I - TXT key or port range */
2447
const char *regex, /* I - Regular expression */
2448
char **args) /* I - Pointer to argument strings */
2449
{
2450
ippfind_expr_t *temp; /* New expression */
2451
2452
2453
if ((temp = calloc(1, sizeof(ippfind_expr_t))) == NULL)
2454
return (NULL);
2455
2456
temp->op = op;
2457
temp->invert = invert;
2458
2459
if (op == IPPFIND_OP_TXT_EXISTS || op == IPPFIND_OP_TXT_REGEX || op == IPPFIND_OP_NAME_LITERAL)
2460
temp->name = (char *)value;
2461
else if (op == IPPFIND_OP_PORT_RANGE)
2462
{
2463
/*
2464
* Pull port number range of the form "number", "-number" (0-number),
2465
* "number-" (number-65535), and "number-number".
2466
*/
2467
2468
if (*value == '-')
2469
{
2470
temp->range[1] = atoi(value + 1);
2471
}
2472
else if (strchr(value, '-'))
2473
{
2474
if (sscanf(value, "%d-%d", temp->range, temp->range + 1) == 1)
2475
temp->range[1] = 65535;
2476
}
2477
else
2478
{
2479
temp->range[0] = temp->range[1] = atoi(value);
2480
}
2481
}
2482
2483
if (regex)
2484
{
2485
int err = regcomp(&(temp->re), regex, REG_NOSUB | REG_ICASE | REG_EXTENDED);
2486
2487
if (err)
2488
{
2489
char message[256]; /* Error message */
2490
2491
regerror(err, &(temp->re), message, sizeof(message));
2492
_cupsLangPrintf(stderr, _("ippfind: Bad regular expression: %s"),
2493
message);
2494
exit(IPPFIND_EXIT_SYNTAX);
2495
}
2496
}
2497
2498
if (args)
2499
{
2500
int num_args; /* Number of arguments */
2501
2502
for (num_args = 1; args[num_args]; num_args ++)
2503
if (!strcmp(args[num_args], ";"))
2504
break;
2505
2506
temp->num_args = num_args;
2507
temp->args = malloc((size_t)num_args * sizeof(char *));
2508
memcpy(temp->args, args, (size_t)num_args * sizeof(char *));
2509
}
2510
2511
return (temp);
2512
}
2513
2514
2515
#ifdef HAVE_AVAHI
2516
/*
2517
* 'poll_callback()' - Wait for input on the specified file descriptors.
2518
*
2519
* Note: This function is needed because avahi_simple_poll_iterate is broken
2520
* and always uses a timeout of 0 (!) milliseconds.
2521
* (Avahi Ticket #364)
2522
*/
2523
2524
static int /* O - Number of file descriptors matching */
2525
poll_callback(
2526
struct pollfd *pollfds, /* I - File descriptors */
2527
unsigned int num_pollfds, /* I - Number of file descriptors */
2528
int timeout, /* I - Timeout in milliseconds (unused) */
2529
void *context) /* I - User data (unused) */
2530
{
2531
int val; /* Return value */
2532
2533
2534
(void)timeout;
2535
(void)context;
2536
2537
val = poll(pollfds, num_pollfds, 500);
2538
2539
if (val > 0)
2540
avahi_got_data = 1;
2541
2542
return (val);
2543
}
2544
#endif /* HAVE_AVAHI */
2545
2546
2547
/*
2548
* 'resolve_callback()' - Process resolve data.
2549
*/
2550
2551
#ifdef HAVE_MDNSRESPONDER
2552
static void DNSSD_API
2553
resolve_callback(
2554
DNSServiceRef sdRef, /* I - Service reference */
2555
DNSServiceFlags flags, /* I - Data flags */
2556
uint32_t interfaceIndex, /* I - Interface */
2557
DNSServiceErrorType errorCode, /* I - Error, if any */
2558
const char *fullName, /* I - Full service name */
2559
const char *hostTarget, /* I - Hostname */
2560
uint16_t port, /* I - Port number (network byte order) */
2561
uint16_t txtLen, /* I - Length of TXT record data */
2562
const unsigned char *txtRecord, /* I - TXT record data */
2563
void *context) /* I - Service */
2564
{
2565
char key[256], /* TXT key value */
2566
*value; /* Value from TXT record */
2567
const unsigned char *txtEnd; /* End of TXT record */
2568
uint8_t valueLen; /* Length of value */
2569
ippfind_srv_t *service = (ippfind_srv_t *)context;
2570
/* Service */
2571
2572
2573
/*
2574
* Only process "add" data...
2575
*/
2576
2577
(void)sdRef;
2578
(void)flags;
2579
(void)interfaceIndex;
2580
(void)fullName;
2581
2582
if (errorCode != kDNSServiceErr_NoError)
2583
{
2584
_cupsLangPrintf(stderr, _("ippfind: Unable to browse or resolve: %s"),
2585
dnssd_error_string(errorCode));
2586
bonjour_error = 1;
2587
return;
2588
}
2589
2590
service->is_resolved = 1;
2591
service->host = strdup(hostTarget);
2592
service->port = ntohs(port);
2593
2594
value = service->host + strlen(service->host) - 1;
2595
if (value >= service->host && *value == '.')
2596
*value = '\0';
2597
2598
/*
2599
* Loop through the TXT key/value pairs and add them to an array...
2600
*/
2601
2602
for (txtEnd = txtRecord + txtLen; txtRecord < txtEnd; txtRecord += valueLen)
2603
{
2604
/*
2605
* Ignore bogus strings...
2606
*/
2607
2608
valueLen = *txtRecord++;
2609
2610
memcpy(key, txtRecord, valueLen);
2611
key[valueLen] = '\0';
2612
2613
if ((value = strchr(key, '=')) == NULL)
2614
continue;
2615
2616
*value++ = '\0';
2617
2618
/*
2619
* Add to array of TXT values...
2620
*/
2621
2622
service->num_txt = cupsAddOption(key, value, service->num_txt,
2623
&(service->txt));
2624
}
2625
2626
set_service_uri(service);
2627
}
2628
2629
2630
#elif defined(HAVE_AVAHI)
2631
static void
2632
resolve_callback(
2633
AvahiServiceResolver *resolver, /* I - Resolver */
2634
AvahiIfIndex interface, /* I - Interface */
2635
AvahiProtocol protocol, /* I - Address protocol */
2636
AvahiResolverEvent event, /* I - Event */
2637
const char *serviceName,/* I - Service name */
2638
const char *regtype, /* I - Registration type */
2639
const char *replyDomain,/* I - Domain name */
2640
const char *hostTarget, /* I - FQDN */
2641
const AvahiAddress *address, /* I - Address */
2642
uint16_t port, /* I - Port number */
2643
AvahiStringList *txt, /* I - TXT records */
2644
AvahiLookupResultFlags flags, /* I - Lookup flags */
2645
void *context) /* I - Service */
2646
{
2647
char key[256], /* TXT key */
2648
*value; /* TXT value */
2649
ippfind_srv_t *service = (ippfind_srv_t *)context;
2650
/* Service */
2651
AvahiStringList *current; /* Current TXT key/value pair */
2652
2653
2654
(void)address;
2655
2656
if (event != AVAHI_RESOLVER_FOUND)
2657
{
2658
bonjour_error = 1;
2659
2660
avahi_service_resolver_free(resolver);
2661
avahi_simple_poll_quit(avahi_poll);
2662
return;
2663
}
2664
2665
service->is_resolved = 1;
2666
service->host = strdup(hostTarget);
2667
service->port = port;
2668
2669
value = service->host + strlen(service->host) - 1;
2670
if (value >= service->host && *value == '.')
2671
*value = '\0';
2672
2673
/*
2674
* Loop through the TXT key/value pairs and add them to an array...
2675
*/
2676
2677
for (current = txt; current; current = current->next)
2678
{
2679
/*
2680
* Ignore bogus strings...
2681
*/
2682
2683
if (current->size > (sizeof(key) - 1))
2684
continue;
2685
2686
memcpy(key, current->text, current->size);
2687
key[current->size] = '\0';
2688
2689
if ((value = strchr(key, '=')) == NULL)
2690
continue;
2691
2692
*value++ = '\0';
2693
2694
/*
2695
* Add to array of TXT values...
2696
*/
2697
2698
service->num_txt = cupsAddOption(key, value, service->num_txt,
2699
&(service->txt));
2700
}
2701
2702
set_service_uri(service);
2703
}
2704
#endif /* HAVE_MDNSRESPONDER */
2705
2706
2707
/*
2708
* 'set_service_uri()' - Set the URI of the service.
2709
*/
2710
2711
static void
2712
set_service_uri(ippfind_srv_t *service) /* I - Service */
2713
{
2714
char uri[1024]; /* URI */
2715
const char *path, /* Resource path */
2716
*scheme; /* URI scheme */
2717
2718
2719
if (!strncmp(service->regtype, "_http.", 6))
2720
{
2721
scheme = "http";
2722
path = cupsGetOption("path", service->num_txt, service->txt);
2723
}
2724
else if (!strncmp(service->regtype, "_https.", 7))
2725
{
2726
scheme = "https";
2727
path = cupsGetOption("path", service->num_txt, service->txt);
2728
}
2729
else if (!strncmp(service->regtype, "_ipp.", 5))
2730
{
2731
scheme = "ipp";
2732
path = cupsGetOption("rp", service->num_txt, service->txt);
2733
}
2734
else if (!strncmp(service->regtype, "_ipps.", 6))
2735
{
2736
scheme = "ipps";
2737
path = cupsGetOption("rp", service->num_txt, service->txt);
2738
}
2739
else if (!strncmp(service->regtype, "_printer.", 9))
2740
{
2741
scheme = "lpd";
2742
path = cupsGetOption("rp", service->num_txt, service->txt);
2743
}
2744
else
2745
return;
2746
2747
if (!path || !*path)
2748
path = "/";
2749
2750
if (*path == '/')
2751
{
2752
service->resource = strdup(path);
2753
}
2754
else
2755
{
2756
snprintf(uri, sizeof(uri), "/%s", path);
2757
service->resource = strdup(uri);
2758
}
2759
2760
httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL,
2761
service->host, service->port, service->resource);
2762
service->uri = strdup(uri);
2763
}
2764
2765
2766
/*
2767
* 'show_usage()' - Show program usage.
2768
*/
2769
2770
static void
2771
show_usage(void)
2772
{
2773
_cupsLangPuts(stderr, _("Usage: ippfind [options] regtype[,subtype]"
2774
"[.domain.] ... [expression]\n"
2775
" ippfind [options] name[.regtype[.domain.]] "
2776
"... [expression]\n"
2777
" ippfind --help\n"
2778
" ippfind --version"));
2779
_cupsLangPuts(stderr, _("Options:"));
2780
_cupsLangPuts(stderr, _("-4 Connect using IPv4"));
2781
_cupsLangPuts(stderr, _("-6 Connect using IPv6"));
2782
_cupsLangPuts(stderr, _("-T seconds Set the browse timeout in seconds"));
2783
_cupsLangPuts(stderr, _("-V version Set default IPP version"));
2784
_cupsLangPuts(stderr, _("--version Show program version"));
2785
_cupsLangPuts(stderr, _("Expressions:"));
2786
_cupsLangPuts(stderr, _("-P number[-number] Match port to number or range"));
2787
_cupsLangPuts(stderr, _("-d regex Match domain to regular expression"));
2788
_cupsLangPuts(stderr, _("-h regex Match hostname to regular expression"));
2789
_cupsLangPuts(stderr, _("-l List attributes"));
2790
_cupsLangPuts(stderr, _("-n regex Match service name to regular expression"));
2791
_cupsLangPuts(stderr, _("-N name Match service name to literal name value"));
2792
_cupsLangPuts(stderr, _("-p Print URI if true"));
2793
_cupsLangPuts(stderr, _("-q Quietly report match via exit code"));
2794
_cupsLangPuts(stderr, _("-r True if service is remote"));
2795
_cupsLangPuts(stderr, _("-s Print service name if true"));
2796
_cupsLangPuts(stderr, _("-t key True if the TXT record contains the key"));
2797
_cupsLangPuts(stderr, _("-u regex Match URI to regular expression"));
2798
_cupsLangPuts(stderr, _("-x utility [argument ...] ;\n"
2799
" Execute program if true"));
2800
_cupsLangPuts(stderr, _("--domain regex Match domain to regular expression"));
2801
_cupsLangPuts(stderr, _("--exec utility [argument ...] ;\n"
2802
" Execute program if true"));
2803
_cupsLangPuts(stderr, _("--host regex Match hostname to regular expression"));
2804
_cupsLangPuts(stderr, _("--literal-name name Match service name to literal name value"));
2805
_cupsLangPuts(stderr, _("--local True if service is local"));
2806
_cupsLangPuts(stderr, _("--ls List attributes"));
2807
_cupsLangPuts(stderr, _("--name regex Match service name to regular expression"));
2808
_cupsLangPuts(stderr, _("--path regex Match resource path to regular expression"));
2809
_cupsLangPuts(stderr, _("--port number[-number] Match port to number or range"));
2810
_cupsLangPuts(stderr, _("--print Print URI if true"));
2811
_cupsLangPuts(stderr, _("--print-name Print service name if true"));
2812
_cupsLangPuts(stderr, _("--quiet Quietly report match via exit code"));
2813
_cupsLangPuts(stderr, _("--remote True if service is remote"));
2814
_cupsLangPuts(stderr, _("--txt key True if the TXT record contains the key"));
2815
_cupsLangPuts(stderr, _("--txt-* regex Match TXT record key to regular expression"));
2816
_cupsLangPuts(stderr, _("--uri regex Match URI to regular expression"));
2817
_cupsLangPuts(stderr, _("Modifiers:"));
2818
_cupsLangPuts(stderr, _("( expressions ) Group expressions"));
2819
_cupsLangPuts(stderr, _("! expression Unary NOT of expression"));
2820
_cupsLangPuts(stderr, _("--not expression Unary NOT of expression"));
2821
_cupsLangPuts(stderr, _("--false Always false"));
2822
_cupsLangPuts(stderr, _("--true Always true"));
2823
_cupsLangPuts(stderr, _("expression expression Logical AND"));
2824
_cupsLangPuts(stderr, _("expression --and expression\n"
2825
" Logical AND"));
2826
_cupsLangPuts(stderr, _("expression --or expression\n"
2827
" Logical OR"));
2828
_cupsLangPuts(stderr, _("Substitutions:"));
2829
_cupsLangPuts(stderr, _("{} URI"));
2830
_cupsLangPuts(stderr, _("{service_domain} Domain name"));
2831
_cupsLangPuts(stderr, _("{service_hostname} Fully-qualified domain name"));
2832
_cupsLangPuts(stderr, _("{service_name} Service instance name"));
2833
_cupsLangPuts(stderr, _("{service_port} Port number"));
2834
_cupsLangPuts(stderr, _("{service_regtype} DNS-SD registration type"));
2835
_cupsLangPuts(stderr, _("{service_scheme} URI scheme"));
2836
_cupsLangPuts(stderr, _("{service_uri} URI"));
2837
_cupsLangPuts(stderr, _("{txt_*} Value of TXT record key"));
2838
_cupsLangPuts(stderr, _("Environment Variables:"));
2839
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_DOMAIN Domain name"));
2840
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_HOSTNAME\n"
2841
" Fully-qualified domain name"));
2842
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_NAME Service instance name"));
2843
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_PORT Port number"));
2844
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_REGTYPE DNS-SD registration type"));
2845
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_SCHEME URI scheme"));
2846
_cupsLangPuts(stderr, _("IPPFIND_SERVICE_URI URI"));
2847
_cupsLangPuts(stderr, _("IPPFIND_TXT_* Value of TXT record key"));
2848
2849
exit(IPPFIND_EXIT_TRUE);
2850
}
2851
2852
2853
/*
2854
* 'show_version()' - Show program version.
2855
*/
2856
2857
static void
2858
show_version(void)
2859
{
2860
_cupsLangPuts(stderr, CUPS_SVERSION);
2861
2862
exit(IPPFIND_EXIT_TRUE);
2863
}
2864
2865