]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest.c
Merge changes from CUPS 1.4svn-r7791.
[thirdparty/cups.git] / cups / dest.c
CommitLineData
ef416fc2 1/*
75bd9771 2 * "$Id: dest.c 7460 2008-04-16 02:19:54Z mike $"
ef416fc2 3 *
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
6 *
080811b1 7 * Copyright 2007-2008 by Apple Inc.
b86bc4cf 8 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 9 *
10 * These coded instructions, statements, and computer programs are the
bc44d920 11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 15 *
16 * This file is subject to the Apple OS-Developed Software exception.
17 *
18 * Contents:
19 *
080811b1
MS
20 * cupsAddDest() - Add a destination to the list of destinations.
21 * cupsFreeDests() - Free the memory used by the list of destinations.
22 * cupsGetDest() - Get the named destination from the list.
23 * cupsGetDests() - Get the list of destinations from the default
24 * server.
25 * cupsGetDests2() - Get the list of destinations from the specified
26 * server.
27 * cupsGetNamedDest() - Get options for the named destination.
28 * cupsRemoveDest() - Remove a destination from the destination list.
29 * cupsSetDefaultDest() - Set the default destination.
30 * cupsSetDests() - Save the list of destinations for the default
31 * server.
32 * cupsSetDests2() - Save the list of destinations for the specified
33 * server.
34 * appleGetDefault() - Get the default printer for this location.
35 * appleGetLocations() - Get the location history array.
36 * appleGetNetwork() - Get the network ID for the current location.
37 * appleGetPrinter() - Get a printer from the history array.
38 * appleSetDefault() - Set the default printer for this location.
39 * appleUseLastPrinter() - Get the default printer preference value.
40 * cups_get_default() - Get the default destination from an lpoptions file.
41 * cups_get_dests() - Get destinations from a file.
42 * cups_get_sdests() - Get destinations from a server.
ef416fc2 43 */
44
45/*
46 * Include necessary headers...
47 */
48
a4924f6c 49#include "debug.h"
ef416fc2 50#include "globals.h"
51#include <stdlib.h>
52#include <ctype.h>
b423cd4c 53#include <sys/stat.h>
ef416fc2 54
fa73b229 55#ifdef HAVE_NOTIFY_H
56# include <notify.h>
57#endif /* HAVE_NOTIFY_H */
58
080811b1
MS
59#ifdef __APPLE__
60# include <sys/cdefs.h>
61# include <CoreFoundation/CoreFoundation.h>
62# include <SystemConfiguration/SystemConfiguration.h>
63# define kLocationHistoryArrayKey CFSTR("kLocationHistoryArrayKeyTMP")
64# define kLocationNetworkKey CFSTR("kLocationNetworkKey")
65# define kLocationPrinterIDKey CFSTR("kLocationPrinterIDKey")
66# define kPMPrintingPreferences CFSTR("com.apple.print.PrintingPrefs")
67# define kUseLastPrinterAsCurrentPrinterKey CFSTR("UseLastPrinterAsCurrentPrinter")
68#endif /* __APPLE__ */
69
ef416fc2 70
71/*
72 * Local functions...
73 */
74
080811b1
MS
75#ifdef __APPLE__
76static char *appleGetDefault(char *name, int namesize);
77static CFArrayRef appleGetLocations(void);
78static CFStringRef appleGetNetwork(void);
79static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network,
80 CFIndex *locindex);
81static void appleSetDefault(const char *name);
82static int appleUseLastPrinter(void);
83#endif /* __APPLE__ */
84static char *cups_get_default(const char *filename, char *namebuf,
a4924f6c
MS
85 size_t namesize, const char **instance);
86static int cups_get_dests(const char *filename, const char *match_name,
87 const char *match_inst, int num_dests,
ef416fc2 88 cups_dest_t **dests);
a4924f6c
MS
89static int cups_get_sdests(http_t *http, ipp_op_t op, const char *name,
90 int num_dests, cups_dest_t **dests);
ef416fc2 91
92
93/*
94 * 'cupsAddDest()' - Add a destination to the list of destinations.
95 *
2abf387c 96 * This function cannot be used to add a new class or printer queue,
97 * it only adds a new container of saved options for the named
98 * destination or instance.
99 *
100 * If the named destination already exists, the destination list is
101 * returned unchanged. Adding a new instance of a destination creates
102 * a copy of that destination's options.
103 *
5a738aea 104 * Use the @link cupsSaveDests@ function to save the updated list of
2abf387c 105 * destinations to the user's lpoptions file.
ef416fc2 106 */
107
108int /* O - New number of destinations */
2abf387c 109cupsAddDest(const char *name, /* I - Destination name */
5a738aea 110 const char *instance, /* I - Instance name or @code NULL@ for none/primary */
ef416fc2 111 int num_dests, /* I - Number of destinations */
112 cups_dest_t **dests) /* IO - Destinations */
113{
114 int i; /* Looping var */
115 cups_dest_t *dest; /* Destination pointer */
2abf387c 116 cups_dest_t *parent; /* Parent destination */
117 cups_option_t *option; /* Current option */
ef416fc2 118
119
2abf387c 120 if (!name || !dests)
ef416fc2 121 return (0);
122
1f0275e3 123 if (cupsGetDest(name, instance, num_dests, *dests))
ef416fc2 124 return (num_dests);
125
126 /*
127 * Add new destination...
128 */
129
130 if (num_dests == 0)
131 dest = malloc(sizeof(cups_dest_t));
132 else
133 dest = realloc(*dests, sizeof(cups_dest_t) * (num_dests + 1));
134
135 if (dest == NULL)
136 return (num_dests);
137
138 *dests = dest;
139
2abf387c 140 /*
141 * Find where to insert the destination...
142 */
143
ef416fc2 144 for (i = num_dests; i > 0; i --, dest ++)
145 if (strcasecmp(name, dest->name) < 0)
146 break;
2abf387c 147 else if (!instance && dest->instance)
148 break;
b423cd4c 149 else if (!strcasecmp(name, dest->name) &&
2abf387c 150 instance && dest->instance &&
ef416fc2 151 strcasecmp(instance, dest->instance) < 0)
152 break;
153
154 if (i > 0)
155 memmove(dest + 1, dest, i * sizeof(cups_dest_t));
156
2abf387c 157 /*
158 * Initialize the destination...
159 */
160
2e4ff8af 161 dest->name = _cupsStrAlloc(name);
ef416fc2 162 dest->is_default = 0;
163 dest->num_options = 0;
164 dest->options = (cups_option_t *)0;
165
2abf387c 166 if (!instance)
ef416fc2 167 dest->instance = NULL;
168 else
2abf387c 169 {
170 /*
171 * Copy options from the primary instance...
172 */
173
2e4ff8af 174 dest->instance = _cupsStrAlloc(instance);
ef416fc2 175
2abf387c 176 if ((parent = cupsGetDest(name, NULL, num_dests + 1, *dests)) != NULL)
177 {
178 for (i = parent->num_options, option = parent->options;
179 i > 0;
180 i --, option ++)
181 dest->num_options = cupsAddOption(option->name, option->value,
182 dest->num_options,
183 &(dest->options));
184 }
185 }
186
ef416fc2 187 return (num_dests + 1);
188}
189
190
191/*
192 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
193 */
194
195void
196cupsFreeDests(int num_dests, /* I - Number of destinations */
197 cups_dest_t *dests) /* I - Destinations */
198{
199 int i; /* Looping var */
200 cups_dest_t *dest; /* Current destination */
201
202
203 if (num_dests == 0 || dests == NULL)
204 return;
205
206 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
207 {
2e4ff8af
MS
208 _cupsStrFree(dest->name);
209 _cupsStrFree(dest->instance);
ef416fc2 210
211 cupsFreeOptions(dest->num_options, dest->options);
212 }
213
214 free(dests);
215}
216
217
218/*
219 * 'cupsGetDest()' - Get the named destination from the list.
220 *
5a738aea 221 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
ef416fc2 222 * list of supported destinations for the current user.
223 */
224
5a738aea
MS
225cups_dest_t * /* O - Destination pointer or @code NULL@ */
226cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
227 const char *instance, /* I - Instance name or @code NULL@ */
ef416fc2 228 int num_dests, /* I - Number of destinations */
229 cups_dest_t *dests) /* I - Destinations */
230{
231 int comp; /* Result of comparison */
232
233
2abf387c 234 if (num_dests <= 0 || !dests)
ef416fc2 235 return (NULL);
236
2abf387c 237 if (!name)
ef416fc2 238 {
239 /*
240 * NULL name for default printer.
241 */
242
243 while (num_dests > 0)
244 {
245 if (dests->is_default)
246 return (dests);
247
248 num_dests --;
249 dests ++;
250 }
251 }
252 else
253 {
254 /*
255 * Lookup name and optionally the instance...
256 */
257
258 while (num_dests > 0)
259 {
260 if ((comp = strcasecmp(name, dests->name)) < 0)
261 return (NULL);
262 else if (comp == 0)
263 {
2abf387c 264 if ((!instance && !dests->instance) ||
ef416fc2 265 (instance != NULL && dests->instance != NULL &&
2abf387c 266 !strcasecmp(instance, dests->instance)))
ef416fc2 267 return (dests);
268 }
269
270 num_dests --;
271 dests ++;
272 }
273 }
274
275 return (NULL);
276}
277
278
279/*
280 * 'cupsGetDests()' - Get the list of destinations from the default server.
ecdc0628 281 *
282 * Starting with CUPS 1.2, the returned list of destinations include the
283 * printer-info, printer-is-accepting-jobs, printer-is-shared,
284 * printer-make-and-model, printer-state, printer-state-change-time,
285 * printer-state-reasons, and printer-type attributes as options.
2abf387c 286 *
5a738aea
MS
287 * Use the @link cupsFreeDests@ function to free the destination list and
288 * the @link cupsGetDest@ function to find a particular destination.
ef416fc2 289 */
290
291int /* O - Number of destinations */
292cupsGetDests(cups_dest_t **dests) /* O - Destinations */
293{
3d052e43 294 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
ef416fc2 295}
296
297
298/*
299 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
300 *
ecdc0628 301 * Starting with CUPS 1.2, the returned list of destinations include the
302 * printer-info, printer-is-accepting-jobs, printer-is-shared,
303 * printer-make-and-model, printer-state, printer-state-change-time,
304 * printer-state-reasons, and printer-type attributes as options.
305 *
5a738aea
MS
306 * Use the @link cupsFreeDests@ function to free the destination list and
307 * the @link cupsGetDest@ function to find a particular destination.
2abf387c 308 *
ef416fc2 309 * @since CUPS 1.1.21@
310 */
311
312int /* O - Number of destinations */
568fa3fa 313cupsGetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 314 cups_dest_t **dests) /* O - Destinations */
315{
316 int i; /* Looping var */
317 int num_dests; /* Number of destinations */
318 cups_dest_t *dest; /* Destination pointer */
319 const char *home; /* HOME environment variable */
b423cd4c 320 char filename[1024]; /* Local ~/.cups/lpoptions file */
ef416fc2 321 const char *defprinter; /* Default printer */
322 char name[1024], /* Copy of printer name */
323 *instance; /* Pointer to instance name */
324 int num_reals; /* Number of real queues */
325 cups_dest_t *reals; /* Real queues */
3d052e43 326 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 327
328
329 /*
330 * Range check the input...
331 */
332
3d052e43 333 if (!dests)
ef416fc2 334 return (0);
335
336 /*
337 * Initialize destination array...
338 */
339
340 num_dests = 0;
341 *dests = (cups_dest_t *)0;
342
343 /*
344 * Grab the printers and classes...
345 */
346
a4924f6c
MS
347 num_dests = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, num_dests, dests);
348 num_dests = cups_get_sdests(http, CUPS_GET_CLASSES, NULL, num_dests, dests);
ef416fc2 349
350 /*
351 * Make a copy of the "real" queues for a later sanity check...
352 */
353
354 if (num_dests > 0)
355 {
356 num_reals = num_dests;
357 reals = calloc(num_reals, sizeof(cups_dest_t));
358
359 if (reals)
360 memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
361 else
362 num_reals = 0;
363 }
364 else
365 {
366 num_reals = 0;
367 reals = NULL;
368 }
369
370 /*
371 * Grab the default destination...
372 */
373
080811b1
MS
374#ifdef __APPLE__
375 if ((defprinter = appleGetDefault(name, sizeof(name))) == NULL)
376#endif /* __APPLE__ */
377 defprinter = cupsGetDefault2(http);
378
379 if (defprinter)
ef416fc2 380 {
381 /*
382 * Grab printer and instance name...
383 */
384
080811b1
MS
385#ifdef __APPLE__
386 if (name != defprinter)
387#endif /* __APPLE__ */
ef416fc2 388 strlcpy(name, defprinter, sizeof(name));
389
390 if ((instance = strchr(name, '/')) != NULL)
391 *instance++ = '\0';
392
393 /*
394 * Lookup the printer and instance and make it the default...
395 */
396
397 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
398 dest->is_default = 1;
399 }
400 else
401 {
402 /*
403 * This initialization of "instance" is unnecessary, but avoids a
404 * compiler warning...
405 */
406
407 instance = NULL;
408 }
409
410 /*
b423cd4c 411 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
ef416fc2 412 */
413
414 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
a4924f6c 415 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 416
417 if ((home = getenv("HOME")) != NULL)
418 {
b423cd4c 419 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
420 if (access(filename, 0))
421 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
422
a4924f6c 423 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 424 }
425
426 /*
427 * Validate the current default destination - this prevents old
b423cd4c 428 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
ef416fc2 429 * pointing to a non-existent printer or class...
430 */
431
432 if (num_reals)
433 {
434 /*
435 * See if we have a default printer...
436 */
437
438 if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
439 {
440 /*
441 * Have a default; see if it is real...
442 */
443
444 dest = cupsGetDest(dest->name, NULL, num_reals, reals);
445 }
446
447 /*
448 * If dest is NULL, then no default (that exists) is set, so we
449 * need to set a default if one exists...
450 */
451
452 if (dest == NULL && defprinter != NULL)
453 {
454 for (i = 0; i < num_dests; i ++)
455 (*dests)[i].is_default = 0;
456
457 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
458 dest->is_default = 1;
459 }
460
461 /*
462 * Free memory...
463 */
464
465 free(reals);
466 }
467
468 /*
469 * Return the number of destinations...
470 */
471
472 return (num_dests);
473}
474
475
a4924f6c
MS
476/*
477 * 'cupsGetNamedDest()' - Get options for the named destination.
478 *
479 * This function is optimized for retrieving a single destination and should
5a738aea
MS
480 * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
481 * know the name of the destination or want to print to the default destination.
482 * If @code NULL@ is returned, the destination does not exist or there is no
483 * default destination.
a4924f6c 484 *
5a738aea
MS
485 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
486 * server will be used.
a4924f6c 487 *
5a738aea
MS
488 * If "name" is @code NULL@, the default printer for the current user will be
489 * returned.
a4924f6c 490 *
5a738aea
MS
491 * The returned destination must be freed using @link cupsFreeDests@ with a
492 * "num_dests" value of 1.
a4924f6c
MS
493 *
494 * @since CUPS 1.4@
495 */
496
5a738aea 497cups_dest_t * /* O - Destination or @code NULL@ */
568fa3fa
MS
498cupsGetNamedDest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
499 const char *name, /* I - Destination name or @code NULL@ for the default destination */
5a738aea 500 const char *instance) /* I - Instance name or @code NULL@ */
a4924f6c
MS
501{
502 cups_dest_t *dest; /* Destination */
503 char filename[1024], /* Path to lpoptions */
504 defname[256]; /* Default printer name */
505 const char *home = getenv("HOME"); /* Home directory */
506 ipp_op_t op = IPP_GET_PRINTER_ATTRIBUTES;
507 /* IPP operation to get server ops */
508 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
509
510
a4924f6c
MS
511 /*
512 * If "name" is NULL, find the default destination...
513 */
514
515 if (!name)
516 {
517 if ((name = getenv("LPDEST")) == NULL)
518 if ((name = getenv("PRINTER")) != NULL && !strcmp(name, "lp"))
519 name = NULL;
520
521 if (!name && home)
522 {
523 /*
524 * No default in the environment, try the user's lpoptions files...
525 */
526
527 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
528
529 if ((name = cups_get_default(filename, defname, sizeof(defname),
530 &instance)) == NULL)
531 {
532 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
533 name = cups_get_default(filename, defname, sizeof(defname),
534 &instance);
535 }
536 }
537
538 if (!name)
539 {
540 /*
541 * Still not there? Try the system lpoptions file...
542 */
543
544 snprintf(filename, sizeof(filename), "%s/lpoptions",
545 cg->cups_serverroot);
546 name = cups_get_default(filename, defname, sizeof(defname), &instance);
547 }
548
549 if (!name)
550 {
551 /*
552 * No locally-set default destination, ask the server...
553 */
554
555 op = CUPS_GET_DEFAULT;
556 }
557 }
558
559 /*
560 * Get the printer's attributes...
561 */
562
563 if (!cups_get_sdests(http, op, name, 0, &dest))
564 return (NULL);
565
566 if (instance)
567 dest->instance = _cupsStrAlloc(instance);
568
569 /*
570 * Then add local options...
571 */
572
573 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
574 cups_get_dests(filename, name, instance, 1, &dest);
575
576 if (home)
577 {
578 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
579
580 if (access(filename, 0))
581 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
582
583 cups_get_dests(filename, name, instance, 1, &dest);
584 }
585
586 /*
587 * Return the result...
588 */
589
590 return (dest);
591}
592
593
f7deaa1a 594/*
595 * 'cupsRemoveDest()' - Remove a destination from the destination list.
596 *
597 * Removing a destination/instance does not delete the class or printer
598 * queue, merely the lpoptions for that destination/instance. Use the
5a738aea
MS
599 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
600 * options for the user.
f7deaa1a 601 *
602 * @since CUPS 1.3@
603 */
604
605int /* O - New number of destinations */
606cupsRemoveDest(const char *name, /* I - Destination name */
5a738aea 607 const char *instance, /* I - Instance name or @code NULL@ */
f7deaa1a 608 int num_dests, /* I - Number of destinations */
609 cups_dest_t **dests) /* IO - Destinations */
610{
611 int i; /* Index into destinations */
612 cups_dest_t *dest; /* Pointer to destination */
613
614
615 /*
616 * Find the destination...
617 */
618
619 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
620 return (num_dests);
621
622 /*
623 * Free memory...
624 */
625
2e4ff8af
MS
626 _cupsStrFree(dest->name);
627 _cupsStrFree(dest->instance);
f7deaa1a 628 cupsFreeOptions(dest->num_options, dest->options);
629
630 /*
631 * Remove the destination from the array...
632 */
633
634 num_dests --;
635
636 i = dest - *dests;
637
638 if (i < num_dests)
639 memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
640
641 return (num_dests);
642}
643
644
645/*
3d052e43 646 * 'cupsSetDefaultDest()' - Set the default destination.
f7deaa1a 647 *
648 * @since CUPS 1.3@
649 */
650
651void
652cupsSetDefaultDest(
653 const char *name, /* I - Destination name */
5a738aea 654 const char *instance, /* I - Instance name or @code NULL@ */
f7deaa1a 655 int num_dests, /* I - Number of destinations */
656 cups_dest_t *dests) /* I - Destinations */
657{
658 int i; /* Looping var */
659 cups_dest_t *dest; /* Current destination */
660
661
662 /*
663 * Range check input...
664 */
665
666 if (!name || num_dests <= 0 || !dests)
667 return;
668
669 /*
670 * Loop through the array and set the "is_default" flag for the matching
671 * destination...
672 */
673
674 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
675 dest->is_default = !strcasecmp(name, dest->name) &&
676 ((!instance && !dest->instance) ||
677 (instance && dest->instance &&
678 !strcasecmp(instance, dest->instance)));
679}
680
681
ef416fc2 682/*
683 * 'cupsSetDests()' - Save the list of destinations for the default server.
684 *
685 * This function saves the destinations to /etc/cups/lpoptions when run
b423cd4c 686 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 687 */
688
689void
690cupsSetDests(int num_dests, /* I - Number of destinations */
691 cups_dest_t *dests) /* I - Destinations */
692{
3d052e43 693 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
ef416fc2 694}
695
696
697/*
698 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
699 *
700 * This function saves the destinations to /etc/cups/lpoptions when run
b423cd4c 701 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 702 *
703 * @since CUPS 1.1.21@
704 */
705
706int /* O - 0 on success, -1 on error */
568fa3fa 707cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 708 int num_dests, /* I - Number of destinations */
709 cups_dest_t *dests) /* I - Destinations */
710{
711 int i, j; /* Looping vars */
712 int wrote; /* Wrote definition? */
713 cups_dest_t *dest; /* Current destination */
714 cups_option_t *option; /* Current option */
8ca02f3c 715 _ipp_option_t *match; /* Matching attribute for option */
ef416fc2 716 FILE *fp; /* File pointer */
b86bc4cf 717#ifndef WIN32
ef416fc2 718 const char *home; /* HOME environment variable */
b86bc4cf 719#endif /* WIN32 */
ef416fc2 720 char filename[1024]; /* lpoptions file */
721 int num_temps; /* Number of temporary destinations */
722 cups_dest_t *temps, /* Temporary destinations */
723 *temp; /* Current temporary dest */
724 const char *val; /* Value of temporary option */
3d052e43 725 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 726
727
728 /*
729 * Range check the input...
730 */
731
3d052e43 732 if (!num_dests || !dests)
ef416fc2 733 return (-1);
734
735 /*
736 * Get the server destinations...
737 */
738
a4924f6c
MS
739 num_temps = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, 0, &temps);
740 num_temps = cups_get_sdests(http, CUPS_GET_CLASSES, NULL, num_temps, &temps);
ef416fc2 741
742 /*
743 * Figure out which file to write to...
744 */
745
746 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
747
748#ifndef WIN32
749 if (getuid())
750 {
751 /*
752 * Merge in server defaults...
753 */
754
a4924f6c 755 num_temps = cups_get_dests(filename, NULL, NULL, num_temps, &temps);
ef416fc2 756
757 /*
758 * Point to user defaults...
759 */
760
761 if ((home = getenv("HOME")) != NULL)
b423cd4c 762 {
763 /*
764 * Remove the old ~/.lpoptions file...
765 */
766
ef416fc2 767 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
b423cd4c 768 unlink(filename);
769
770 /*
771 * Create ~/.cups subdirectory...
772 */
773
774 snprintf(filename, sizeof(filename), "%s/.cups", home);
775 if (access(filename, 0))
776 mkdir(filename, 0700);
777
778 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
779 }
ef416fc2 780 }
781#endif /* !WIN32 */
782
783 /*
784 * Try to open the file...
785 */
786
787 if ((fp = fopen(filename, "w")) == NULL)
788 {
789 cupsFreeDests(num_temps, temps);
790 return (-1);
791 }
792
d6ae789d 793#ifndef WIN32
794 /*
795 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
796 * file...
797 */
798
799 if (!getuid())
800 fchmod(fileno(fp), 0644);
801#endif /* !WIN32 */
802
ef416fc2 803 /*
804 * Write each printer; each line looks like:
805 *
806 * Dest name[/instance] options
807 * Default name[/instance] options
808 */
809
810 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
811 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
812 {
813 if (dest->is_default)
814 {
815 fprintf(fp, "Default %s", dest->name);
816 if (dest->instance)
817 fprintf(fp, "/%s", dest->instance);
818
819 wrote = 1;
820 }
821 else
822 wrote = 0;
823
824 if ((temp = cupsGetDest(dest->name, dest->instance, num_temps, temps)) == NULL)
825 temp = cupsGetDest(dest->name, NULL, num_temps, temps);
826
827 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
828 {
8ca02f3c 829 /*
830 * See if this option is a printer attribute; if so, skip it...
831 */
832
833 if ((match = _ippFindOption(option->name)) != NULL &&
834 match->group_tag == IPP_TAG_PRINTER)
835 continue;
836
ef416fc2 837 /*
838 * See if the server/global options match these; if so, don't
839 * write 'em.
840 */
841
8ca02f3c 842 if (temp &&
843 (val = cupsGetOption(option->name, temp->num_options,
844 temp->options)) != NULL &&
845 !strcasecmp(val, option->value))
846 continue;
ef416fc2 847
848 /*
849 * Options don't match, write to the file...
850 */
851
852 if (!wrote)
853 {
854 fprintf(fp, "Dest %s", dest->name);
855 if (dest->instance)
856 fprintf(fp, "/%s", dest->instance);
857 wrote = 1;
858 }
859
860 if (option->value[0])
861 {
8ca02f3c 862 if (strchr(option->value, ' ') ||
863 strchr(option->value, '\\') ||
864 strchr(option->value, '\"') ||
865 strchr(option->value, '\''))
866 {
867 /*
868 * Quote the value...
869 */
870
871 fprintf(fp, " %s=\"", option->name);
872
873 for (val = option->value; *val; val ++)
874 {
875 if (strchr("\"\'\\", *val))
876 putc('\\', fp);
877
878 putc(*val, fp);
879 }
880
881 putc('\"', fp);
882 }
883 else
884 {
885 /*
886 * Store the literal value...
887 */
888
ef416fc2 889 fprintf(fp, " %s=%s", option->name, option->value);
8ca02f3c 890 }
ef416fc2 891 }
892 else
893 fprintf(fp, " %s", option->name);
894 }
895
896 if (wrote)
897 fputs("\n", fp);
898 }
899
900 /*
fa73b229 901 * Free the temporary destinations and close the file...
ef416fc2 902 */
903
904 cupsFreeDests(num_temps, temps);
905
fa73b229 906 fclose(fp);
907
080811b1
MS
908#ifdef __APPLE__
909 /*
910 * Set the default printer for this location - this allows command-line
911 * and GUI applications to share the same default destination...
912 */
913
914 if ((dest = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
915 appleSetDefault(dest->name);
916#endif /* __APPLE__ */
917
fa73b229 918#ifdef HAVE_NOTIFY_POST
ef416fc2 919 /*
fa73b229 920 * Send a notification so that MacOS X applications can know about the
921 * change, too.
ef416fc2 922 */
923
fa73b229 924 notify_post("com.apple.printerListChange");
925#endif /* HAVE_NOTIFY_POST */
ef416fc2 926
927 return (0);
928}
929
930
080811b1
MS
931#ifdef __APPLE__
932/*
933 * 'appleGetDefault()' - Get the default printer for this location.
934 */
935
936static char * /* O - Name or NULL if no default */
937appleGetDefault(char *name, /* I - Name buffer */
938 int namesize) /* I - Size of name buffer */
939{
940 CFStringRef network; /* Network location */
941 CFArrayRef locations; /* Location array */
942 CFStringRef locprinter; /* Current printer */
943
944
945 /*
946 * Use location-based defaults if "use last printer" is selected in the
947 * system preferences...
948 */
949
950 if (!appleUseLastPrinter())
951 {
952 DEBUG_puts("appleGetDefault: Not using last printer as default...");
953 return (NULL);
954 }
955
956 /*
957 * Get the current location...
958 */
959
960 if ((network = appleGetNetwork()) == NULL)
961 {
962 DEBUG_puts("appleGetDefault: Unable to get current network...");
963 return (NULL);
964 }
965
966#ifdef DEBUG
967 CFStringGetCString(network, name, namesize, kCFStringEncodingUTF8);
ae71f5de 968 DEBUG_printf(("appleGetDefault: network=\"%s\"\n", name));
080811b1
MS
969#endif /* DEBUG */
970
971 /*
972 * Lookup the network in the preferences...
973 */
974
975 if ((locations = appleGetLocations()) == NULL)
976 {
977 /*
978 * Missing or bad location array, so no location-based default...
979 */
980
981 DEBUG_puts("appleGetDefault: Missing or bad location history array...");
982
080811b1
MS
983 return (NULL);
984 }
985
986 DEBUG_printf(("appleGetDefault: Got location, %d entries...\n",
987 (int)CFArrayGetCount(locations)));
988
989 if ((locprinter = appleGetPrinter(locations, network, NULL)) != NULL)
990 CFStringGetCString(locprinter, name, namesize, kCFStringEncodingUTF8);
991 else
992 name[0] = '\0';
993
080811b1
MS
994 DEBUG_printf(("appleGetDefault: Returning \"%s\"...\n", name));
995
996 return (*name ? name : NULL);
997}
998
999
1000/*
1001 * 'appleGetLocations()' - Get the location history array.
1002 */
1003
1004static CFArrayRef /* O - Location array or NULL */
1005appleGetLocations(void)
1006{
1007 CFArrayRef locations; /* Location array */
1008
1009
1010 /*
1011 * Look up the location array in the preferences...
1012 */
1013
1014 if ((locations = CFPreferencesCopyAppValue(kLocationHistoryArrayKey,
1015 kPMPrintingPreferences)) == NULL)
1016 return (NULL);
1017
1018 if (CFGetTypeID(locations) != CFArrayGetTypeID())
1019 {
1020 CFRelease(locations);
1021 return (NULL);
1022 }
1023
1024 return (locations);
1025}
1026
1027
1028/*
1029 * 'appleGetNetwork()' - Get the network ID for the current location.
1030 */
1031
1032static CFStringRef /* O - Network ID */
1033appleGetNetwork(void)
1034{
1035 SCDynamicStoreRef dynamicStore; /* System configuration data */
1036 CFStringRef key; /* Current network configuration key */
1037 CFDictionaryRef ip_dict; /* Network configuration data */
1038 CFStringRef network = NULL; /* Current network ID */
1039
1040
1041 if ((dynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Printing"), NULL,
1042 NULL)) != NULL)
1043 {
1044 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
1045 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)) != NULL)
1046 {
1047 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
1048 {
1049 if ((network = CFDictionaryGetValue(ip_dict,
1050 kSCPropNetIPv4Router)) != NULL)
1051 CFRetain(network);
1052
1053 CFRelease(ip_dict);
1054 }
1055
1056 CFRelease(key);
1057 }
1058
1059 CFRelease(dynamicStore);
1060 }
1061
1062 return (network);
1063}
1064
1065
1066/*
1067 * 'appleGetPrinter()' - Get a printer from the history array.
1068 */
1069
1070static CFStringRef /* O - Printer name or NULL */
1071appleGetPrinter(CFArrayRef locations, /* I - Location array */
1072 CFStringRef network, /* I - Network name */
1073 CFIndex *locindex) /* O - Index in array */
1074{
1075 CFIndex i, /* Looping var */
1076 count; /* Number of locations */
1077 CFDictionaryRef location; /* Current location */
1078 CFStringRef locnetwork, /* Current network */
1079 locprinter; /* Current printer */
1080
1081
1082 for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
1083 if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
1084 CFGetTypeID(location) == CFDictionaryGetTypeID())
1085 {
1086 if ((locnetwork = CFDictionaryGetValue(location,
1087 kLocationNetworkKey)) != NULL &&
1088 CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
1089 CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
1090 (locprinter = CFDictionaryGetValue(location,
1091 kLocationPrinterIDKey)) != NULL &&
1092 CFGetTypeID(locprinter) == CFStringGetTypeID())
1093 {
1094 if (locindex)
1095 *locindex = i;
1096
1097 return (locprinter);
1098 }
1099 }
1100
1101 return (NULL);
1102}
1103
1104
1105/*
1106 * 'appleSetDefault()' - Set the default printer for this location.
1107 */
1108
1109static void
1110appleSetDefault(const char *name) /* I - Default printer/class name */
1111{
1112 CFStringRef network; /* Current network */
1113 CFArrayRef locations; /* Old locations array */
1114 CFIndex locindex; /* Index in locations array */
1115 CFStringRef locprinter; /* Current printer */
1116 CFMutableArrayRef newlocations; /* New locations array */
1117 CFMutableDictionaryRef newlocation; /* New location */
1118 CFStringRef newprinter; /* New printer */
1119
1120
1121 /*
1122 * Get the current location...
1123 */
1124
1125 if ((network = appleGetNetwork()) == NULL)
1126 {
1127 DEBUG_puts("appleSetDefault: Unable to get current network...");
1128 return;
1129 }
1130
080811b1
MS
1131 if ((newprinter = CFStringCreateWithCString(kCFAllocatorDefault, name,
1132 kCFStringEncodingUTF8)) == NULL)
1133 {
1134 CFRelease(network);
1135 return;
1136 }
1137
1138 /*
1139 * Lookup the network in the preferences...
1140 */
1141
1142 if ((locations = appleGetLocations()) != NULL)
1143 locprinter = appleGetPrinter(locations, network, &locindex);
1144 else
1145 {
1146 locprinter = NULL;
1147 locindex = -1;
1148 }
1149
1150 if (!locprinter ||
1151 CFStringCompare(locprinter, newprinter, 0) != kCFCompareEqualTo)
1152 {
1153 /*
1154 * Need to change the locations array...
1155 */
1156
1157 if (locations)
1158 {
1159 newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
1160 locations);
1161
1162 if (locprinter)
1163 CFArrayRemoveValueAtIndex(newlocations, locindex);
1164 }
1165 else
1166 newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
1167
1168 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1169 &kCFTypeDictionaryKeyCallBacks,
1170 &kCFTypeDictionaryValueCallBacks);
1171
1172 if (newlocation && newlocations)
1173 {
1174 /*
1175 * Put the new location at the front of the array...
1176 */
1177
1178 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
1179 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, newprinter);
1180 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
1181
1182 /*
1183 * Limit the number of locations to 10...
1184 */
1185
1186 while (CFArrayGetCount(newlocations) > 10)
1187 CFArrayRemoveValueAtIndex(newlocations, 10);
1188
1189 /*
1190 * Push the changes out...
1191 */
1192
1193 CFPreferencesSetAppValue(kLocationHistoryArrayKey, newlocations,
1194 kPMPrintingPreferences);
1195 CFPreferencesAppSynchronize(kPMPrintingPreferences);
1196 }
1197
1198 if (newlocations)
1199 CFRelease(newlocations);
1200
1201 if (newlocation)
1202 CFRelease(newlocation);
1203 }
91c84a35 1204
080811b1
MS
1205 CFRelease(newprinter);
1206}
1207
1208
1209/*
1210 * 'appleUseLastPrinter()' - Get the default printer preference value.
1211 */
1212
1213static int /* O - 1 to use last printer, 0 otherwise */
1214appleUseLastPrinter(void)
1215{
1216 CFPropertyListRef uselast; /* Use last printer preference value */
1217
1218
1219 if ((uselast = CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey,
1220 kPMPrintingPreferences)) != NULL)
1221 {
1222 CFRelease(uselast);
1223
1224 if (uselast == kCFBooleanFalse)
1225 return (0);
1226 }
1227
1228 return (1);
1229}
1230#endif /* __APPLE__ */
1231
1232
a4924f6c
MS
1233/*
1234 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1235 */
1236
080811b1 1237static char * /* O - Default destination or NULL */
a4924f6c
MS
1238cups_get_default(const char *filename, /* I - File to read */
1239 char *namebuf, /* I - Name buffer */
1240 size_t namesize, /* I - Size of name buffer */
1241 const char **instance) /* I - Instance */
1242{
1243 cups_file_t *fp; /* lpoptions file */
1244 char line[8192], /* Line from file */
1245 *value, /* Value for line */
1246 *nameptr; /* Pointer into name */
1247 int linenum; /* Current line */
1248
1249
1250 *namebuf = '\0';
1251
1252 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1253 {
1254 linenum = 0;
1255
1256 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1257 {
1258 if (!strcasecmp(line, "default") && value)
1259 {
1260 strlcpy(namebuf, value, namesize);
1261
1262 if ((nameptr = strchr(namebuf, ' ')) != NULL)
1263 *nameptr = '\0';
1264 if ((nameptr = strchr(namebuf, '\t')) != NULL)
1265 *nameptr = '\0';
1266
1267 if ((nameptr = strchr(namebuf, '/')) != NULL)
1268 *nameptr++ = '\0';
1269
1270 *instance = nameptr;
1271 break;
1272 }
1273 }
1274
1275 cupsFileClose(fp);
1276 }
1277
1278 return (*namebuf ? namebuf : NULL);
1279}
1280
1281
ef416fc2 1282/*
1283 * 'cups_get_dests()' - Get destinations from a file.
1284 */
1285
1286static int /* O - Number of destinations */
1287cups_get_dests(const char *filename, /* I - File to read from */
a4924f6c
MS
1288 const char *match_name, /* I - Destination name we want */
1289 const char *match_inst, /* I - Instance name we want */
ef416fc2 1290 int num_dests, /* I - Number of destinations */
1291 cups_dest_t **dests) /* IO - Destinations */
1292{
1293 int i; /* Looping var */
1294 cups_dest_t *dest; /* Current destination */
a4924f6c 1295 cups_file_t *fp; /* File pointer */
ef416fc2 1296 char line[8192], /* Line from file */
1297 *lineptr, /* Pointer into line */
1298 *name, /* Name of destination/option */
1299 *instance; /* Instance of destination */
a4924f6c 1300 int linenum; /* Current line number */
ef416fc2 1301 const char *printer; /* PRINTER or LPDEST */
1302
1303
a4924f6c
MS
1304 DEBUG_printf(("cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1305 "match_inst=\"%s\", num_dests=%d, dests=%p)\n", filename,
1306 match_name ? match_name : "(null)",
1307 match_inst ? match_inst : "(null)", num_dests, dests));
1308
1309 /*
1310 * Try to open the file...
1311 */
1312
1313 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1314 return (num_dests);
1315
ef416fc2 1316 /*
1317 * Check environment variables...
1318 */
1319
1320 if ((printer = getenv("LPDEST")) == NULL)
1321 if ((printer = getenv("PRINTER")) != NULL)
1322 if (strcmp(printer, "lp") == 0)
1323 printer = NULL;
1324
a4924f6c
MS
1325 DEBUG_printf(("cups_get_dests: printer=\"%s\"\n",
1326 printer ? printer : "(null)"));
ef416fc2 1327
1328 /*
1329 * Read each printer; each line looks like:
1330 *
1331 * Dest name[/instance] options
1332 * Default name[/instance] options
1333 */
1334
a4924f6c
MS
1335 linenum = 0;
1336
1337 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
ef416fc2 1338 {
1339 /*
1340 * See what type of line it is...
1341 */
1342
a4924f6c
MS
1343 DEBUG_printf(("cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"\n",
1344 linenum, line, lineptr ? lineptr : "(null)"));
ef416fc2 1345
a4924f6c
MS
1346 if ((strcasecmp(line, "dest") && strcasecmp(line, "default")) || !lineptr)
1347 {
1348 DEBUG_puts("cups_get_dests: Not a dest or default line...");
ef416fc2 1349 continue;
a4924f6c 1350 }
ef416fc2 1351
1352 name = lineptr;
1353
1354 /*
1355 * Search for an instance...
1356 */
1357
1358 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1359 lineptr ++;
1360
ef416fc2 1361 if (*lineptr == '/')
1362 {
1363 /*
1364 * Found an instance...
1365 */
1366
1367 *lineptr++ = '\0';
1368 instance = lineptr;
1369
1370 /*
1371 * Search for an instance...
1372 */
1373
1374 while (!isspace(*lineptr & 255) && *lineptr)
1375 lineptr ++;
1376 }
1377 else
1378 instance = NULL;
1379
a4924f6c
MS
1380 if (*lineptr)
1381 *lineptr++ = '\0';
1382
1383 DEBUG_printf(("cups_get_dests: name=\"%s\", instance=\"%s\"\n", name,
1384 instance));
ef416fc2 1385
1386 /*
1387 * See if the primary instance of the destination exists; if not,
1388 * ignore this entry and move on...
1389 */
1390
a4924f6c
MS
1391 if (match_name)
1392 {
1393 if (strcasecmp(name, match_name) ||
1394 (!instance && match_inst) ||
1395 (instance && !match_inst) ||
1396 (instance && strcasecmp(instance, match_inst)))
1397 continue;
ef416fc2 1398
a4924f6c
MS
1399 dest = *dests;
1400 }
1401 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
1402 {
1403 DEBUG_puts("cups_get_dests: Not found!");
1404 continue;
1405 }
1406 else
ef416fc2 1407 {
1408 /*
a4924f6c 1409 * Add the destination...
ef416fc2 1410 */
1411
a4924f6c
MS
1412 num_dests = cupsAddDest(name, instance, num_dests, dests);
1413
1414 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1415 {
1416 /*
1417 * Out of memory!
1418 */
1419
1420 DEBUG_puts("cups_get_dests: Out of memory!");
1421 break;
1422 }
ef416fc2 1423 }
1424
1425 /*
1426 * Add options until we hit the end of the line...
1427 */
1428
1429 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
1430 &(dest->options));
1431
a4924f6c
MS
1432 /*
1433 * If we found what we were looking for, stop now...
1434 */
1435
1436 if (match_name)
1437 break;
1438
ef416fc2 1439 /*
1440 * Set this as default if needed...
1441 */
1442
a4924f6c 1443 if (!printer && !strcasecmp(line, "default"))
ef416fc2 1444 {
a4924f6c
MS
1445 DEBUG_puts("cups_get_dests: Setting as default...");
1446
ef416fc2 1447 for (i = 0; i < num_dests; i ++)
1448 (*dests)[i].is_default = 0;
1449
1450 dest->is_default = 1;
1451 }
1452 }
1453
1454 /*
1455 * Close the file and return...
1456 */
1457
a4924f6c 1458 cupsFileClose(fp);
ef416fc2 1459
1460 return (num_dests);
1461}
1462
1463
1464/*
1465 * 'cups_get_sdests()' - Get destinations from a server.
1466 */
1467
1468static int /* O - Number of destinations */
568fa3fa 1469cups_get_sdests(http_t *http, /* I - Connection to server or CUPS_HTTP_DEFAULT */
a4924f6c
MS
1470 ipp_op_t op, /* I - IPP operation */
1471 const char *name, /* I - Name of destination */
ef416fc2 1472 int num_dests, /* I - Number of destinations */
1473 cups_dest_t **dests) /* IO - Destinations */
1474{
fa73b229 1475 int i; /* Looping var */
ef416fc2 1476 cups_dest_t *dest; /* Current destination */
1477 ipp_t *request, /* IPP Request */
1478 *response; /* IPP Response */
1479 ipp_attribute_t *attr; /* Current attribute */
fa73b229 1480 int accepting, /* printer-is-accepting-jobs attribute */
1481 shared, /* printer-is-shared attribute */
1482 state, /* printer-state attribute */
1483 change_time, /* printer-state-change-time attribute */
1484 type; /* printer-type attribute */
1485 const char *info, /* printer-info attribute */
f42414bf 1486 *location, /* printer-location attribute */
fa73b229 1487 *make_model, /* printer-make-and-model attribute */
a4924f6c
MS
1488 *printer_name; /* printer-name attribute */
1489 char uri[1024], /* printer-uri value */
1490 job_sheets[1024], /* job-sheets-default attribute */
f42414bf 1491 auth_info_req[1024], /* auth-info-required attribute */
f7deaa1a 1492 reasons[1024]; /* printer-state-reasons attribute */
1493 int num_options; /* Number of options */
1494 cups_option_t *options; /* Options */
1495 char optname[1024], /* Option name */
1496 value[2048], /* Option value */
1497 *ptr; /* Pointer into name/value */
ef416fc2 1498 static const char * const pattrs[] = /* Attributes we're interested in */
1499 {
f42414bf 1500 "auth-info-required",
fa73b229 1501 "job-sheets-default",
1502 "printer-info",
1503 "printer-is-accepting-jobs",
1504 "printer-is-shared",
f42414bf 1505 "printer-location",
fa73b229 1506 "printer-make-and-model",
ef416fc2 1507 "printer-name",
fa73b229 1508 "printer-state",
1509 "printer-state-change-time",
1510 "printer-state-reasons",
f7deaa1a 1511 "printer-type",
1512 "printer-defaults"
ef416fc2 1513 };
1514
1515
1516 /*
1517 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
1518 * the following attributes:
1519 *
1520 * attributes-charset
1521 * attributes-natural-language
fa73b229 1522 * requesting-user-name
ef416fc2 1523 */
1524
fa73b229 1525 request = ippNewRequest(op);
ef416fc2 1526
1527 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1528 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1529 NULL, pattrs);
1530
fa73b229 1531 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1532 "requesting-user-name", NULL, cupsUser());
1533
a4924f6c
MS
1534 if (name)
1535 {
1536 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1537 "localhost", ippPort(), "/printers/%s", name);
1538 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1539 uri);
1540 }
1541
ef416fc2 1542 /*
1543 * Do the request and get back a response...
1544 */
1545
1546 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1547 {
1548 for (attr = response->attrs; attr != NULL; attr = attr->next)
1549 {
1550 /*
1551 * Skip leading attributes until we hit a printer...
1552 */
1553
1554 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1555 attr = attr->next;
1556
1557 if (attr == NULL)
1558 break;
1559
1560 /*
f7deaa1a 1561 * Pull the needed attributes from this printer...
ef416fc2 1562 */
1563
a4924f6c
MS
1564 accepting = 0;
1565 change_time = 0;
1566 info = NULL;
1567 location = NULL;
1568 make_model = NULL;
1569 printer_name = NULL;
1570 num_options = 0;
1571 options = NULL;
1572 shared = 1;
1573 state = IPP_PRINTER_IDLE;
1574 type = CUPS_PRINTER_LOCAL;
ef416fc2 1575
f42414bf 1576 auth_info_req[0] = '\0';
1577 job_sheets[0] = '\0';
1578 reasons[0] = '\0';
ef416fc2 1579
1580 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1581 {
f42414bf 1582 if (!strcmp(attr->name, "auth-info-required") &&
1583 attr->value_tag == IPP_TAG_KEYWORD)
1584 {
1585 strlcpy(auth_info_req, attr->values[0].string.text,
1586 sizeof(auth_info_req));
1587
1588 for (i = 1, ptr = auth_info_req + strlen(auth_info_req);
1589 i < attr->num_values;
1590 i ++)
1591 {
1592 snprintf(ptr, sizeof(auth_info_req) - (ptr - auth_info_req), ",%s",
1593 attr->values[i].string.text);
1594 ptr += strlen(ptr);
1595 }
1596 }
1597 else if (!strcmp(attr->name, "job-sheets-default") &&
1598 (attr->value_tag == IPP_TAG_KEYWORD ||
1599 attr->value_tag == IPP_TAG_NAME))
ef416fc2 1600 {
1601 if (attr->num_values == 2)
1602 snprintf(job_sheets, sizeof(job_sheets), "%s,%s",
1603 attr->values[0].string.text, attr->values[1].string.text);
1604 else
fa73b229 1605 strlcpy(job_sheets, attr->values[0].string.text,
1606 sizeof(job_sheets));
ef416fc2 1607 }
fa73b229 1608 else if (!strcmp(attr->name, "printer-info") &&
1609 attr->value_tag == IPP_TAG_TEXT)
1610 info = attr->values[0].string.text;
1611 else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
1612 attr->value_tag == IPP_TAG_BOOLEAN)
1613 accepting = attr->values[0].boolean;
1614 else if (!strcmp(attr->name, "printer-is-shared") &&
1615 attr->value_tag == IPP_TAG_BOOLEAN)
1616 shared = attr->values[0].boolean;
f42414bf 1617 else if (!strcmp(attr->name, "printer-location") &&
1618 attr->value_tag == IPP_TAG_TEXT)
1619 location = attr->values[0].string.text;
fa73b229 1620 else if (!strcmp(attr->name, "printer-make-and-model") &&
1621 attr->value_tag == IPP_TAG_TEXT)
1622 make_model = attr->values[0].string.text;
1623 else if (!strcmp(attr->name, "printer-name") &&
1624 attr->value_tag == IPP_TAG_NAME)
a4924f6c 1625 printer_name = attr->values[0].string.text;
fa73b229 1626 else if (!strcmp(attr->name, "printer-state") &&
1627 attr->value_tag == IPP_TAG_ENUM)
1628 state = attr->values[0].integer;
1629 else if (!strcmp(attr->name, "printer-state-change-time") &&
1630 attr->value_tag == IPP_TAG_INTEGER)
1631 change_time = attr->values[0].integer;
1632 else if (!strcmp(attr->name, "printer-state-reasons") &&
1633 attr->value_tag == IPP_TAG_KEYWORD)
1634 {
1635 strlcpy(reasons, attr->values[0].string.text, sizeof(reasons));
f7deaa1a 1636 for (i = 1, ptr = reasons + strlen(reasons);
fa73b229 1637 i < attr->num_values;
1638 i ++)
1639 {
f7deaa1a 1640 snprintf(ptr, sizeof(reasons) - (ptr - reasons), ",%s",
fa73b229 1641 attr->values[i].string.text);
f7deaa1a 1642 ptr += strlen(ptr);
fa73b229 1643 }
1644 }
1645 else if (!strcmp(attr->name, "printer-type") &&
1646 attr->value_tag == IPP_TAG_ENUM)
1647 type = attr->values[0].integer;
f7deaa1a 1648 else if (strncmp(attr->name, "notify-", 7) &&
1649 (attr->value_tag == IPP_TAG_BOOLEAN ||
1650 attr->value_tag == IPP_TAG_ENUM ||
1651 attr->value_tag == IPP_TAG_INTEGER ||
1652 attr->value_tag == IPP_TAG_KEYWORD ||
1653 attr->value_tag == IPP_TAG_NAME ||
1654 attr->value_tag == IPP_TAG_RANGE) &&
1655 strstr(attr->name, "-default"))
1656 {
1657 char *valptr; /* Pointer into attribute value */
1658
1659
1660 /*
1661 * Add a default option...
1662 */
1663
1664 strlcpy(optname, attr->name, sizeof(optname));
1665 if ((ptr = strstr(optname, "-default")) != NULL)
1666 *ptr = '\0';
1667
1668 value[0] = '\0';
1669 for (i = 0, ptr = value; i < attr->num_values; i ++)
1670 {
1671 if (ptr >= (value + sizeof(value) - 1))
1672 break;
1673
1674 if (i)
1675 *ptr++ = ',';
1676
1677 switch (attr->value_tag)
1678 {
1679 case IPP_TAG_INTEGER :
1680 case IPP_TAG_ENUM :
1681 snprintf(ptr, sizeof(value) - (ptr - value), "%d",
1682 attr->values[i].integer);
1683 break;
1684
1685 case IPP_TAG_BOOLEAN :
1686 if (attr->values[i].boolean)
1687 strlcpy(ptr, "true", sizeof(value) - (ptr - value));
1688 else
1689 strlcpy(ptr, "false", sizeof(value) - (ptr - value));
1690 break;
1691
1692 case IPP_TAG_RANGE :
1693 if (attr->values[i].range.lower ==
1694 attr->values[i].range.upper)
1695 snprintf(ptr, sizeof(value) - (ptr - value), "%d",
1696 attr->values[i].range.lower);
1697 else
1698 snprintf(ptr, sizeof(value) - (ptr - value), "%d-%d",
1699 attr->values[i].range.lower,
1700 attr->values[i].range.upper);
1701 break;
1702
1703 default :
1704 for (valptr = attr->values[i].string.text;
1705 *valptr && ptr < (value + sizeof(value) - 2);)
1706 {
1707 if (strchr(" \t\n\\\'\"", *valptr))
1708 *ptr++ = '\\';
1709
1710 *ptr++ = *valptr++;
1711 }
1712
1713 *ptr = '\0';
1714 break;
1715 }
1716
1717 ptr += strlen(ptr);
1718 }
1719
1720 num_options = cupsAddOption(optname, value, num_options, &options);
1721 }
ef416fc2 1722
1723 attr = attr->next;
1724 }
1725
1726 /*
1727 * See if we have everything needed...
1728 */
1729
a4924f6c 1730 if (!printer_name)
ef416fc2 1731 {
f7deaa1a 1732 cupsFreeOptions(num_options, options);
1733
ef416fc2 1734 if (attr == NULL)
1735 break;
1736 else
1737 continue;
1738 }
1739
a4924f6c 1740 num_dests = cupsAddDest(printer_name, NULL, num_dests, dests);
ef416fc2 1741
a4924f6c 1742 if ((dest = cupsGetDest(printer_name, NULL, num_dests, *dests)) != NULL)
fa73b229 1743 {
f7deaa1a 1744 dest->num_options = num_options;
1745 dest->options = options;
1746
1747 num_options = 0;
1748 options = NULL;
1749
f42414bf 1750 if (auth_info_req[0])
1751 dest->num_options = cupsAddOption("auth-info-required", auth_info_req,
1752 dest->num_options,
1753 &(dest->options));
1754
ef416fc2 1755 if (job_sheets[0])
fa73b229 1756 dest->num_options = cupsAddOption("job-sheets", job_sheets,
1757 dest->num_options,
1758 &(dest->options));
1759
1760 if (info)
1761 dest->num_options = cupsAddOption("printer-info", info,
1762 dest->num_options,
1763 &(dest->options));
1764
f7deaa1a 1765 sprintf(value, "%d", accepting);
1766 dest->num_options = cupsAddOption("printer-is-accepting-jobs", value,
fa73b229 1767 dest->num_options,
1768 &(dest->options));
1769
f7deaa1a 1770 sprintf(value, "%d", shared);
1771 dest->num_options = cupsAddOption("printer-is-shared", value,
fa73b229 1772 dest->num_options,
1773 &(dest->options));
1774
f42414bf 1775 if (location)
1776 dest->num_options = cupsAddOption("printer-location",
1777 location, dest->num_options,
1778 &(dest->options));
1779
fa73b229 1780 if (make_model)
1781 dest->num_options = cupsAddOption("printer-make-and-model",
1782 make_model, dest->num_options,
ef416fc2 1783 &(dest->options));
1784
f7deaa1a 1785 sprintf(value, "%d", state);
1786 dest->num_options = cupsAddOption("printer-state", value,
fa73b229 1787 dest->num_options,
1788 &(dest->options));
1789
1790 if (change_time)
1791 {
f7deaa1a 1792 sprintf(value, "%d", change_time);
1793 dest->num_options = cupsAddOption("printer-state-change-time", value,
fa73b229 1794 dest->num_options,
1795 &(dest->options));
1796 }
1797
1798 if (reasons[0])
1799 dest->num_options = cupsAddOption("printer-state-reasons", reasons,
1800 dest->num_options,
1801 &(dest->options));
1802
f7deaa1a 1803 sprintf(value, "%d", type);
1804 dest->num_options = cupsAddOption("printer-type", value,
fa73b229 1805 dest->num_options,
1806 &(dest->options));
1807 }
1808
f7deaa1a 1809 cupsFreeOptions(num_options, options);
1810
ef416fc2 1811 if (attr == NULL)
1812 break;
1813 }
1814
1815 ippDelete(response);
1816 }
1817
1818 /*
1819 * Return the count...
1820 */
1821
1822 return (num_dests);
1823}
1824
1825
1826/*
75bd9771 1827 * End of "$Id: dest.c 7460 2008-04-16 02:19:54Z mike $".
ef416fc2 1828 */