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