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