]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
Merge changes from CUPS 1.4svn-r8227.
[thirdparty/cups.git] / cups / dest.c
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() - Copy the location history array.
35 * appleCopyNetwork() - Get the network ID for the current location.
36 * appleGetDefault() - Get the default printer for this location.
37 * appleGetPaperSize() - Get the default paper size.
38 * appleGetPrinter() - Get a printer from the history array.
39 * appleSetDefault() - Set the default printer for this location.
40 * appleUseLastPrinter() - Get the default printer preference value.
41 * cups_add_dest() - Add a destination to the array.
42 * cups_compare_dests() - Compare two destinations.
43 * cups_find_dest() - Find a destination using a binary search.
44 * cups_get_default() - Get the default destination from an lpoptions file.
45 * cups_get_dests() - Get destinations from a file.
46 * cups_get_sdests() - Get destinations from a server.
47 * cups_make_string() - Make a comma-separated string of values from an IPP
48 * attribute.
49 */
50
51 /*
52 * Include necessary headers...
53 */
54
55 #include "debug.h"
56 #include "globals.h"
57 #include "pwgmedia.h"
58 #include <stdlib.h>
59 #include <ctype.h>
60 #include <sys/stat.h>
61
62 #ifdef HAVE_NOTIFY_H
63 # include <notify.h>
64 #endif /* HAVE_NOTIFY_H */
65
66 #ifdef __APPLE__
67 # include <sys/cdefs.h>
68 # include <CoreFoundation/CoreFoundation.h>
69 # include <SystemConfiguration/SystemConfiguration.h>
70 # define kDefaultPaperIDKey CFSTR("DefaultPaperID")
71 # define kLocationHistoryArrayKey CFSTR("kLocationHistoryArrayKeyTMP")
72 # define kLocationNetworkKey CFSTR("kLocationNetworkKey")
73 # define kLocationPrinterIDKey CFSTR("kLocationPrinterIDKey")
74 # define kPMPrintingPreferences CFSTR("com.apple.print.PrintingPrefs")
75 # define kUseLastPrinterAsCurrentPrinterKey CFSTR("UseLastPrinterAsCurrentPrinter")
76 #endif /* __APPLE__ */
77
78
79 /*
80 * Local functions...
81 */
82
83 #ifdef __APPLE__
84 static CFArrayRef appleCopyLocations(void);
85 static CFStringRef appleCopyNetwork(void);
86 static char *appleGetDefault(char *name, int namesize);
87 static char *appleGetPaperSize(char *name, int namesize);
88 static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network,
89 CFIndex *locindex);
90 static void appleSetDefault(const char *name);
91 static int appleUseLastPrinter(void);
92 #endif /* __APPLE__ */
93 static cups_dest_t *cups_add_dest(const char *name, const char *instance,
94 int *num_dests, cups_dest_t **dests);
95 static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
96 static int cups_find_dest(const char *name, const char *instance,
97 int num_dests, cups_dest_t *dests, int prev,
98 int *rdiff);
99 static char *cups_get_default(const char *filename, char *namebuf,
100 size_t namesize, const char **instance);
101 static int cups_get_dests(const char *filename, const char *match_name,
102 const char *match_inst, int num_dests,
103 cups_dest_t **dests);
104 static int cups_get_sdests(http_t *http, ipp_op_t op, const char *name,
105 int num_dests, cups_dest_t **dests);
106 static char *cups_make_string(ipp_attribute_t *attr, char *buffer,
107 size_t bufsize);
108
109
110 /*
111 * 'cupsAddDest()' - Add a destination to the list of destinations.
112 *
113 * This function cannot be used to add a new class or printer queue,
114 * it only adds a new container of saved options for the named
115 * destination or instance.
116 *
117 * If the named destination already exists, the destination list is
118 * returned unchanged. Adding a new instance of a destination creates
119 * a copy of that destination's options.
120 *
121 * Use the @link cupsSaveDests@ function to save the updated list of
122 * destinations to the user's lpoptions file.
123 */
124
125 int /* O - New number of destinations */
126 cupsAddDest(const char *name, /* I - Destination name */
127 const char *instance, /* I - Instance name or @code NULL@ for none/primary */
128 int num_dests, /* I - Number of destinations */
129 cups_dest_t **dests) /* IO - Destinations */
130 {
131 int i; /* Looping var */
132 cups_dest_t *dest; /* Destination pointer */
133 cups_dest_t *parent; /* Parent destination */
134 cups_option_t *doption, /* Current destination option */
135 *poption; /* Current parent option */
136
137
138 if (!name || !dests)
139 return (0);
140
141 if (!cupsGetDest(name, instance, num_dests, *dests))
142 {
143 if (instance &&
144 (parent = cupsGetDest(name, NULL, num_dests, *dests)) == NULL)
145 return (num_dests);
146
147 dest = cups_add_dest(name, instance, &num_dests, dests);
148
149 if (instance && parent && parent->num_options > 0)
150 {
151 /*
152 * Copy options from parent...
153 */
154
155 dest->options = calloc(sizeof(cups_option_t), parent->num_options);
156
157 if (dest->options)
158 {
159 dest->num_options = parent->num_options;
160
161 for (i = dest->num_options, doption = dest->options,
162 poption = parent->options;
163 i > 0;
164 i --, doption ++, poption ++)
165 {
166 doption->name = _cupsStrRetain(poption->name);
167 doption->value = _cupsStrRetain(poption->value);
168 }
169 }
170 }
171 }
172
173 return (num_dests);
174 }
175
176
177 /*
178 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
179 */
180
181 void
182 cupsFreeDests(int num_dests, /* I - Number of destinations */
183 cups_dest_t *dests) /* I - Destinations */
184 {
185 int i; /* Looping var */
186 cups_dest_t *dest; /* Current destination */
187
188
189 if (num_dests == 0 || dests == NULL)
190 return;
191
192 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
193 {
194 _cupsStrFree(dest->name);
195 _cupsStrFree(dest->instance);
196
197 cupsFreeOptions(dest->num_options, dest->options);
198 }
199
200 free(dests);
201 }
202
203
204 /*
205 * 'cupsGetDest()' - Get the named destination from the list.
206 *
207 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
208 * list of supported destinations for the current user.
209 */
210
211 cups_dest_t * /* O - Destination pointer or @code NULL@ */
212 cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
213 const char *instance, /* I - Instance name or @code NULL@ */
214 int num_dests, /* I - Number of destinations */
215 cups_dest_t *dests) /* I - Destinations */
216 {
217 int diff, /* Result of comparison */
218 match; /* Matching index */
219
220
221 if (num_dests <= 0 || !dests)
222 return (NULL);
223
224 if (!name)
225 {
226 /*
227 * NULL name for default printer.
228 */
229
230 while (num_dests > 0)
231 {
232 if (dests->is_default)
233 return (dests);
234
235 num_dests --;
236 dests ++;
237 }
238 }
239 else
240 {
241 /*
242 * Lookup name and optionally the instance...
243 */
244
245 match = cups_find_dest(name, instance, num_dests, dests, -1, &diff);
246
247 if (!diff)
248 return (dests + match);
249 }
250
251 return (NULL);
252 }
253
254
255 /*
256 * 'cupsGetDests()' - Get the list of destinations from the default server.
257 *
258 * Starting with CUPS 1.2, the returned list of destinations include the
259 * printer-info, printer-is-accepting-jobs, printer-is-shared,
260 * printer-make-and-model, printer-state, printer-state-change-time,
261 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
262 * adds the marker-change-time, marker-colors, marker-high-levels,
263 * marker-levels, marker-low-levels, marker-message, marker-names,
264 * marker-types, and printer-commands attributes as well.
265 *
266 * Use the @link cupsFreeDests@ function to free the destination list and
267 * the @link cupsGetDest@ function to find a particular destination.
268 */
269
270 int /* O - Number of destinations */
271 cupsGetDests(cups_dest_t **dests) /* O - Destinations */
272 {
273 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
274 }
275
276
277 /*
278 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
279 *
280 * Starting with CUPS 1.2, the returned list of destinations include the
281 * printer-info, printer-is-accepting-jobs, printer-is-shared,
282 * printer-make-and-model, printer-state, printer-state-change-time,
283 * printer-state-reasons, and printer-type attributes as options. CUPS 1.4
284 * adds the marker-change-time, marker-colors, marker-high-levels,
285 * marker-levels, marker-low-levels, marker-message, marker-names,
286 * marker-types, and printer-commands attributes as well.
287 *
288 * Use the @link cupsFreeDests@ function to free the destination list and
289 * the @link cupsGetDest@ function to find a particular destination.
290 *
291 * @since CUPS 1.1.21/Mac OS X 10.4@
292 */
293
294 int /* O - Number of destinations */
295 cupsGetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
296 cups_dest_t **dests) /* O - Destinations */
297 {
298 int i; /* Looping var */
299 int num_dests; /* Number of destinations */
300 cups_dest_t *dest; /* Destination pointer */
301 const char *home; /* HOME environment variable */
302 char filename[1024]; /* Local ~/.cups/lpoptions file */
303 const char *defprinter; /* Default printer */
304 char name[1024], /* Copy of printer name */
305 *instance; /* Pointer to instance name */
306 int num_reals; /* Number of real queues */
307 cups_dest_t *reals; /* Real queues */
308 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
309
310
311 /*
312 * Range check the input...
313 */
314
315 if (!dests)
316 {
317 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad NULL dests pointer"), 1);
318 return (0);
319 }
320
321 /*
322 * Initialize destination array...
323 */
324
325 num_dests = 0;
326 *dests = (cups_dest_t *)0;
327
328 /*
329 * Grab the printers and classes...
330 */
331
332 num_dests = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, num_dests, dests);
333
334 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
335 {
336 cupsFreeDests(num_dests, *dests);
337 *dests = (cups_dest_t *)0;
338 return (0);
339 }
340
341 /*
342 * Make a copy of the "real" queues for a later sanity check...
343 */
344
345 if (num_dests > 0)
346 {
347 num_reals = num_dests;
348 reals = calloc(num_reals, sizeof(cups_dest_t));
349
350 if (reals)
351 memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
352 else
353 num_reals = 0;
354 }
355 else
356 {
357 num_reals = 0;
358 reals = NULL;
359 }
360
361 /*
362 * Grab the default destination...
363 */
364
365 #ifdef __APPLE__
366 if ((defprinter = appleGetDefault(name, sizeof(name))) == NULL)
367 #endif /* __APPLE__ */
368 defprinter = cupsGetDefault2(http);
369
370 if (defprinter)
371 {
372 /*
373 * Grab printer and instance name...
374 */
375
376 #ifdef __APPLE__
377 if (name != defprinter)
378 #endif /* __APPLE__ */
379 strlcpy(name, defprinter, sizeof(name));
380
381 if ((instance = strchr(name, '/')) != NULL)
382 *instance++ = '\0';
383
384 /*
385 * Lookup the printer and instance and make it the default...
386 */
387
388 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
389 dest->is_default = 1;
390 }
391 else
392 {
393 /*
394 * This initialization of "instance" is unnecessary, but avoids a
395 * compiler warning...
396 */
397
398 instance = NULL;
399 }
400
401 /*
402 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
403 */
404
405 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
406 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
407
408 if ((home = getenv("HOME")) != NULL)
409 {
410 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
411 if (access(filename, 0))
412 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
413
414 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
415 }
416
417 /*
418 * Validate the current default destination - this prevents old
419 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
420 * pointing to a non-existent printer or class...
421 */
422
423 if (num_reals)
424 {
425 /*
426 * See if we have a default printer...
427 */
428
429 if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
430 {
431 /*
432 * Have a default; see if it is real...
433 */
434
435 dest = cupsGetDest(dest->name, NULL, num_reals, reals);
436 }
437
438 /*
439 * If dest is NULL, then no default (that exists) is set, so we
440 * need to set a default if one exists...
441 */
442
443 if (dest == NULL && defprinter != NULL)
444 {
445 for (i = 0; i < num_dests; i ++)
446 (*dests)[i].is_default = 0;
447
448 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
449 dest->is_default = 1;
450 }
451
452 /*
453 * Free memory...
454 */
455
456 free(reals);
457 }
458
459 /*
460 * Return the number of destinations...
461 */
462
463 if (num_dests > 0)
464 _cupsSetError(IPP_OK, NULL, 0);
465
466 return (num_dests);
467 }
468
469
470 /*
471 * 'cupsGetNamedDest()' - Get options for the named destination.
472 *
473 * This function is optimized for retrieving a single destination and should
474 * be used instead of @link cupsGetDests@ and @link cupsGetDest@ when you either
475 * know the name of the destination or want to print to the default destination.
476 * If @code NULL@ is returned, the destination does not exist or there is no
477 * default destination.
478 *
479 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
480 * server will be used.
481 *
482 * If "name" is @code NULL@, the default printer for the current user will be
483 * returned.
484 *
485 * The returned destination must be freed using @link cupsFreeDests@ with a
486 * "num_dests" value of 1.
487 *
488 * @since CUPS 1.4@
489 */
490
491 cups_dest_t * /* O - Destination or @code NULL@ */
492 cupsGetNamedDest(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
493 const char *name, /* I - Destination name or @code NULL@ for the default destination */
494 const char *instance) /* I - Instance name or @code NULL@ */
495 {
496 cups_dest_t *dest; /* Destination */
497 char filename[1024], /* Path to lpoptions */
498 defname[256]; /* Default printer name */
499 const char *home = getenv("HOME"); /* Home directory */
500 ipp_op_t op = IPP_GET_PRINTER_ATTRIBUTES;
501 /* IPP operation to get server ops */
502 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
503
504
505 /*
506 * If "name" is NULL, find the default destination...
507 */
508
509 if (!name)
510 {
511 if ((name = getenv("LPDEST")) == NULL)
512 if ((name = getenv("PRINTER")) != NULL && !strcmp(name, "lp"))
513 name = NULL;
514
515 if (!name && home)
516 {
517 /*
518 * No default in the environment, try the user's lpoptions files...
519 */
520
521 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
522
523 if ((name = cups_get_default(filename, defname, sizeof(defname),
524 &instance)) == NULL)
525 {
526 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
527 name = cups_get_default(filename, defname, sizeof(defname),
528 &instance);
529 }
530 }
531
532 if (!name)
533 {
534 /*
535 * Still not there? Try the system lpoptions file...
536 */
537
538 snprintf(filename, sizeof(filename), "%s/lpoptions",
539 cg->cups_serverroot);
540 name = cups_get_default(filename, defname, sizeof(defname), &instance);
541 }
542
543 if (!name)
544 {
545 /*
546 * No locally-set default destination, ask the server...
547 */
548
549 op = CUPS_GET_DEFAULT;
550 }
551 }
552
553 /*
554 * Get the printer's attributes...
555 */
556
557 if (!cups_get_sdests(http, op, name, 0, &dest))
558 return (NULL);
559
560 if (instance)
561 dest->instance = _cupsStrAlloc(instance);
562
563 /*
564 * Then add local options...
565 */
566
567 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
568 cups_get_dests(filename, name, instance, 1, &dest);
569
570 if (home)
571 {
572 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
573
574 if (access(filename, 0))
575 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
576
577 cups_get_dests(filename, name, instance, 1, &dest);
578 }
579
580 /*
581 * Return the result...
582 */
583
584 return (dest);
585 }
586
587
588 /*
589 * 'cupsRemoveDest()' - Remove a destination from the destination list.
590 *
591 * Removing a destination/instance does not delete the class or printer
592 * queue, merely the lpoptions for that destination/instance. Use the
593 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
594 * options for the user.
595 *
596 * @since CUPS 1.3/Mac OS X 10.5@
597 */
598
599 int /* O - New number of destinations */
600 cupsRemoveDest(const char *name, /* I - Destination name */
601 const char *instance, /* I - Instance name or @code NULL@ */
602 int num_dests, /* I - Number of destinations */
603 cups_dest_t **dests) /* IO - Destinations */
604 {
605 int i; /* Index into destinations */
606 cups_dest_t *dest; /* Pointer to destination */
607
608
609 /*
610 * Find the destination...
611 */
612
613 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
614 return (num_dests);
615
616 /*
617 * Free memory...
618 */
619
620 _cupsStrFree(dest->name);
621 _cupsStrFree(dest->instance);
622 cupsFreeOptions(dest->num_options, dest->options);
623
624 /*
625 * Remove the destination from the array...
626 */
627
628 num_dests --;
629
630 i = dest - *dests;
631
632 if (i < num_dests)
633 memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
634
635 return (num_dests);
636 }
637
638
639 /*
640 * 'cupsSetDefaultDest()' - Set the default destination.
641 *
642 * @since CUPS 1.3/Mac OS X 10.5@
643 */
644
645 void
646 cupsSetDefaultDest(
647 const char *name, /* I - Destination name */
648 const char *instance, /* I - Instance name or @code NULL@ */
649 int num_dests, /* I - Number of destinations */
650 cups_dest_t *dests) /* I - Destinations */
651 {
652 int i; /* Looping var */
653 cups_dest_t *dest; /* Current destination */
654
655
656 /*
657 * Range check input...
658 */
659
660 if (!name || num_dests <= 0 || !dests)
661 return;
662
663 /*
664 * Loop through the array and set the "is_default" flag for the matching
665 * destination...
666 */
667
668 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
669 dest->is_default = !strcasecmp(name, dest->name) &&
670 ((!instance && !dest->instance) ||
671 (instance && dest->instance &&
672 !strcasecmp(instance, dest->instance)));
673 }
674
675
676 /*
677 * 'cupsSetDests()' - Save the list of destinations for the default server.
678 *
679 * This function saves the destinations to /etc/cups/lpoptions when run
680 * as root and ~/.cups/lpoptions when run as a normal user.
681 */
682
683 void
684 cupsSetDests(int num_dests, /* I - Number of destinations */
685 cups_dest_t *dests) /* I - Destinations */
686 {
687 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
688 }
689
690
691 /*
692 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
693 *
694 * This function saves the destinations to /etc/cups/lpoptions when run
695 * as root and ~/.cups/lpoptions when run as a normal user.
696 *
697 * @since CUPS 1.1.21/Mac OS X 10.4@
698 */
699
700 int /* O - 0 on success, -1 on error */
701 cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
702 int num_dests, /* I - Number of destinations */
703 cups_dest_t *dests) /* I - Destinations */
704 {
705 int i, j; /* Looping vars */
706 int wrote; /* Wrote definition? */
707 cups_dest_t *dest; /* Current destination */
708 cups_option_t *option; /* Current option */
709 _ipp_option_t *match; /* Matching attribute for option */
710 FILE *fp; /* File pointer */
711 #ifndef WIN32
712 const char *home; /* HOME environment variable */
713 #endif /* WIN32 */
714 char filename[1024]; /* lpoptions file */
715 int num_temps; /* Number of temporary destinations */
716 cups_dest_t *temps, /* Temporary destinations */
717 *temp; /* Current temporary dest */
718 const char *val; /* Value of temporary option */
719 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
720
721
722 /*
723 * Range check the input...
724 */
725
726 if (!num_dests || !dests)
727 return (-1);
728
729 /*
730 * Get the server destinations...
731 */
732
733 num_temps = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, 0, &temps);
734
735 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
736 {
737 cupsFreeDests(num_temps, temps);
738 return (-1);
739 }
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 * 'appleCopyLocations()' - Copy the location history array.
933 */
934
935 static CFArrayRef /* O - Location array or NULL */
936 appleCopyLocations(void)
937 {
938 CFArrayRef locations; /* Location array */
939
940
941 /*
942 * Look up the location array in the preferences...
943 */
944
945 if ((locations = CFPreferencesCopyAppValue(kLocationHistoryArrayKey,
946 kPMPrintingPreferences)) == NULL)
947 return (NULL);
948
949 if (CFGetTypeID(locations) != CFArrayGetTypeID())
950 {
951 CFRelease(locations);
952 return (NULL);
953 }
954
955 return (locations);
956 }
957
958
959 /*
960 * 'appleCopyNetwork()' - Get the network ID for the current location.
961 */
962
963 static CFStringRef /* O - Network ID */
964 appleCopyNetwork(void)
965 {
966 SCDynamicStoreRef dynamicStore; /* System configuration data */
967 CFStringRef key; /* Current network configuration key */
968 CFDictionaryRef ip_dict; /* Network configuration data */
969 CFStringRef network = NULL; /* Current network ID */
970
971
972 if ((dynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Printing"), NULL,
973 NULL)) != NULL)
974 {
975 if ((key = SCDynamicStoreKeyCreateNetworkGlobalEntity(
976 NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4)) != NULL)
977 {
978 if ((ip_dict = SCDynamicStoreCopyValue(dynamicStore, key)) != NULL)
979 {
980 if ((network = CFDictionaryGetValue(ip_dict,
981 kSCPropNetIPv4Router)) != NULL)
982 CFRetain(network);
983
984 CFRelease(ip_dict);
985 }
986
987 CFRelease(key);
988 }
989
990 CFRelease(dynamicStore);
991 }
992
993 return (network);
994 }
995
996
997 /*
998 * 'appleGetDefault()' - Get the default printer for this location.
999 */
1000
1001 static char * /* O - Name or NULL if no default */
1002 appleGetDefault(char *name, /* I - Name buffer */
1003 int namesize) /* I - Size of name buffer */
1004 {
1005 CFStringRef network; /* Network location */
1006 CFArrayRef locations; /* Location array */
1007 CFStringRef locprinter; /* Current printer */
1008
1009
1010 /*
1011 * Use location-based defaults if "use last printer" is selected in the
1012 * system preferences...
1013 */
1014
1015 if (!appleUseLastPrinter())
1016 {
1017 DEBUG_puts("appleGetDefault: Not using last printer as default...");
1018 return (NULL);
1019 }
1020
1021 /*
1022 * Get the current location...
1023 */
1024
1025 if ((network = appleCopyNetwork()) == NULL)
1026 {
1027 DEBUG_puts("appleGetDefault: Unable to get current network...");
1028 return (NULL);
1029 }
1030
1031 #ifdef DEBUG
1032 CFStringGetCString(network, name, namesize, kCFStringEncodingUTF8);
1033 DEBUG_printf(("appleGetDefault: network=\"%s\"\n", name));
1034 #endif /* DEBUG */
1035
1036 /*
1037 * Lookup the network in the preferences...
1038 */
1039
1040 if ((locations = appleCopyLocations()) == NULL)
1041 {
1042 /*
1043 * Missing or bad location array, so no location-based default...
1044 */
1045
1046 DEBUG_puts("appleGetDefault: Missing or bad location history array...");
1047
1048 CFRelease(network);
1049
1050 return (NULL);
1051 }
1052
1053 DEBUG_printf(("appleGetDefault: Got location, %d entries...\n",
1054 (int)CFArrayGetCount(locations)));
1055
1056 if ((locprinter = appleGetPrinter(locations, network, NULL)) != NULL)
1057 CFStringGetCString(locprinter, name, namesize, kCFStringEncodingUTF8);
1058 else
1059 name[0] = '\0';
1060
1061 CFRelease(network);
1062 CFRelease(locations);
1063
1064 DEBUG_printf(("appleGetDefault: Returning \"%s\"...\n", name));
1065
1066 return (*name ? name : NULL);
1067 }
1068
1069
1070 /*
1071 * 'appleGetPaperSize()' - Get the default paper size.
1072 */
1073
1074 static char * /* O - Default paper size */
1075 appleGetPaperSize(char *name, /* I - Paper size name buffer */
1076 int namesize) /* I - Size of buffer */
1077 {
1078 CFStringRef defaultPaperID; /* Default paper ID */
1079 _cups_pwg_media_t *pwgmedia; /* PWG media size */
1080
1081
1082 defaultPaperID = CFPreferencesCopyAppValue(kDefaultPaperIDKey,
1083 kPMPrintingPreferences);
1084 if (!defaultPaperID ||
1085 CFGetTypeID(defaultPaperID) != CFStringGetTypeID() ||
1086 !CFStringGetCString(defaultPaperID, name, namesize,
1087 kCFStringEncodingUTF8))
1088 name[0] = '\0';
1089 else if ((pwgmedia = _cupsPWGMediaByLegacy(name)) != NULL)
1090 strlcpy(name, pwgmedia->pwg, namesize);
1091
1092 if (defaultPaperID)
1093 CFRelease(defaultPaperID);
1094
1095 return (name);
1096 }
1097
1098
1099 /*
1100 * 'appleGetPrinter()' - Get a printer from the history array.
1101 */
1102
1103 static CFStringRef /* O - Printer name or NULL */
1104 appleGetPrinter(CFArrayRef locations, /* I - Location array */
1105 CFStringRef network, /* I - Network name */
1106 CFIndex *locindex) /* O - Index in array */
1107 {
1108 CFIndex i, /* Looping var */
1109 count; /* Number of locations */
1110 CFDictionaryRef location; /* Current location */
1111 CFStringRef locnetwork, /* Current network */
1112 locprinter; /* Current printer */
1113
1114
1115 for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
1116 if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
1117 CFGetTypeID(location) == CFDictionaryGetTypeID())
1118 {
1119 if ((locnetwork = CFDictionaryGetValue(location,
1120 kLocationNetworkKey)) != NULL &&
1121 CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
1122 CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
1123 (locprinter = CFDictionaryGetValue(location,
1124 kLocationPrinterIDKey)) != NULL &&
1125 CFGetTypeID(locprinter) == CFStringGetTypeID())
1126 {
1127 if (locindex)
1128 *locindex = i;
1129
1130 return (locprinter);
1131 }
1132 }
1133
1134 return (NULL);
1135 }
1136
1137
1138 /*
1139 * 'appleSetDefault()' - Set the default printer for this location.
1140 */
1141
1142 static void
1143 appleSetDefault(const char *name) /* I - Default printer/class name */
1144 {
1145 CFStringRef network; /* Current network */
1146 CFArrayRef locations; /* Old locations array */
1147 CFIndex locindex; /* Index in locations array */
1148 CFStringRef locprinter; /* Current printer */
1149 CFMutableArrayRef newlocations; /* New locations array */
1150 CFMutableDictionaryRef newlocation; /* New location */
1151 CFStringRef newprinter; /* New printer */
1152
1153
1154 /*
1155 * Get the current location...
1156 */
1157
1158 if ((network = appleCopyNetwork()) == NULL)
1159 {
1160 DEBUG_puts("appleSetDefault: Unable to get current network...");
1161 return;
1162 }
1163
1164 if ((newprinter = CFStringCreateWithCString(kCFAllocatorDefault, name,
1165 kCFStringEncodingUTF8)) == NULL)
1166 {
1167 CFRelease(network);
1168 return;
1169 }
1170
1171 /*
1172 * Lookup the network in the preferences...
1173 */
1174
1175 if ((locations = appleCopyLocations()) != NULL)
1176 locprinter = appleGetPrinter(locations, network, &locindex);
1177 else
1178 {
1179 locprinter = NULL;
1180 locindex = -1;
1181 }
1182
1183 if (!locprinter ||
1184 CFStringCompare(locprinter, newprinter, 0) != kCFCompareEqualTo)
1185 {
1186 /*
1187 * Need to change the locations array...
1188 */
1189
1190 if (locations)
1191 {
1192 newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
1193 locations);
1194
1195 if (locprinter)
1196 CFArrayRemoveValueAtIndex(newlocations, locindex);
1197 }
1198 else
1199 newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
1200
1201 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1202 &kCFTypeDictionaryKeyCallBacks,
1203 &kCFTypeDictionaryValueCallBacks);
1204
1205 if (newlocation && newlocations)
1206 {
1207 /*
1208 * Put the new location at the front of the array...
1209 */
1210
1211 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
1212 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, newprinter);
1213 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
1214
1215 /*
1216 * Limit the number of locations to 10...
1217 */
1218
1219 while (CFArrayGetCount(newlocations) > 10)
1220 CFArrayRemoveValueAtIndex(newlocations, 10);
1221
1222 /*
1223 * Push the changes out...
1224 */
1225
1226 CFPreferencesSetAppValue(kLocationHistoryArrayKey, newlocations,
1227 kPMPrintingPreferences);
1228 CFPreferencesAppSynchronize(kPMPrintingPreferences);
1229 }
1230
1231 if (newlocations)
1232 CFRelease(newlocations);
1233
1234 if (newlocation)
1235 CFRelease(newlocation);
1236 }
1237
1238 if (locations)
1239 CFRelease(locations);
1240
1241 CFRelease(network);
1242 CFRelease(newprinter);
1243 }
1244
1245
1246 /*
1247 * 'appleUseLastPrinter()' - Get the default printer preference value.
1248 */
1249
1250 static int /* O - 1 to use last printer, 0 otherwise */
1251 appleUseLastPrinter(void)
1252 {
1253 CFPropertyListRef uselast; /* Use last printer preference value */
1254
1255
1256 if ((uselast = CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey,
1257 kPMPrintingPreferences)) != NULL)
1258 {
1259 CFRelease(uselast);
1260
1261 if (uselast == kCFBooleanFalse)
1262 return (0);
1263 }
1264
1265 return (1);
1266 }
1267 #endif /* __APPLE__ */
1268
1269
1270 /*
1271 * 'cups_add_dest()' - Add a destination to the array.
1272 *
1273 * Unlike cupsAddDest(), this function does not check for duplicates.
1274 */
1275
1276 static cups_dest_t * /* O - New destination */
1277 cups_add_dest(const char *name, /* I - Name of destination */
1278 const char *instance, /* I - Instance or NULL */
1279 int *num_dests, /* IO - Number of destinations */
1280 cups_dest_t **dests) /* IO - Destinations */
1281 {
1282 int insert, /* Insertion point */
1283 diff; /* Result of comparison */
1284 cups_dest_t *dest; /* Destination pointer */
1285
1286
1287 /*
1288 * Add new destination...
1289 */
1290
1291 if (*num_dests == 0)
1292 dest = malloc(sizeof(cups_dest_t));
1293 else
1294 dest = realloc(*dests, sizeof(cups_dest_t) * (*num_dests + 1));
1295
1296 if (!dest)
1297 return (NULL);
1298
1299 *dests = dest;
1300
1301 /*
1302 * Find where to insert the destination...
1303 */
1304
1305 if (*num_dests == 0)
1306 insert = 0;
1307 else
1308 {
1309 insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
1310 &diff);
1311
1312 if (diff > 0)
1313 insert ++;
1314 }
1315
1316 /*
1317 * Move the array elements as needed...
1318 */
1319
1320 if (insert < *num_dests)
1321 memmove(*dests + insert + 1, *dests + insert,
1322 (*num_dests - insert) * sizeof(cups_dest_t));
1323
1324 (*num_dests) ++;
1325
1326 /*
1327 * Initialize the destination...
1328 */
1329
1330 dest = *dests + insert;
1331 dest->name = _cupsStrAlloc(name);
1332 dest->instance = _cupsStrAlloc(instance);
1333 dest->is_default = 0;
1334 dest->num_options = 0;
1335 dest->options = (cups_option_t *)0;
1336
1337 return (dest);
1338 }
1339
1340
1341 /*
1342 * 'cups_compare_dests()' - Compare two destinations.
1343 */
1344
1345 static int /* O - Result of comparison */
1346 cups_compare_dests(cups_dest_t *a, /* I - First destination */
1347 cups_dest_t *b) /* I - Second destination */
1348 {
1349 int diff; /* Difference */
1350
1351
1352 if ((diff = strcasecmp(a->name, b->name)) != 0)
1353 return (diff);
1354 else if (a->instance && b->instance)
1355 return (strcasecmp(a->instance, b->instance));
1356 else
1357 return ((a->instance && !b->instance) - (!a->instance && b->instance));
1358 }
1359
1360
1361 /*
1362 * 'cups_find_dest()' - Find a destination using a binary search.
1363 */
1364
1365 static int /* O - Index of match */
1366 cups_find_dest(const char *name, /* I - Destination name */
1367 const char *instance, /* I - Instance or NULL */
1368 int num_dests, /* I - Number of destinations */
1369 cups_dest_t *dests, /* I - Destinations */
1370 int prev, /* I - Previous index */
1371 int *rdiff) /* O - Difference of match */
1372 {
1373 int left, /* Low mark for binary search */
1374 right, /* High mark for binary search */
1375 current, /* Current index */
1376 diff; /* Result of comparison */
1377 cups_dest_t key; /* Search key */
1378
1379
1380 key.name = (char *)name;
1381 key.instance = (char *)instance;
1382
1383 if (prev >= 0)
1384 {
1385 /*
1386 * Start search on either side of previous...
1387 */
1388
1389 if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
1390 (diff < 0 && prev == 0) ||
1391 (diff > 0 && prev == (num_dests - 1)))
1392 {
1393 *rdiff = diff;
1394 return (prev);
1395 }
1396 else if (diff < 0)
1397 {
1398 /*
1399 * Start with previous on right side...
1400 */
1401
1402 left = 0;
1403 right = prev;
1404 }
1405 else
1406 {
1407 /*
1408 * Start wih previous on left side...
1409 */
1410
1411 left = prev;
1412 right = num_dests - 1;
1413 }
1414 }
1415 else
1416 {
1417 /*
1418 * Start search in the middle...
1419 */
1420
1421 left = 0;
1422 right = num_dests - 1;
1423 }
1424
1425 do
1426 {
1427 current = (left + right) / 2;
1428 diff = cups_compare_dests(&key, dests + current);
1429
1430 if (diff == 0)
1431 break;
1432 else if (diff < 0)
1433 right = current;
1434 else
1435 left = current;
1436 }
1437 while ((right - left) > 1);
1438
1439 if (diff != 0)
1440 {
1441 /*
1442 * Check the last 1 or 2 elements...
1443 */
1444
1445 if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
1446 current = left;
1447 else
1448 {
1449 diff = cups_compare_dests(&key, dests + right);
1450 current = right;
1451 }
1452 }
1453
1454 /*
1455 * Return the closest destination and the difference...
1456 */
1457
1458 *rdiff = diff;
1459
1460 return (current);
1461 }
1462
1463
1464 /*
1465 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1466 */
1467
1468 static char * /* O - Default destination or NULL */
1469 cups_get_default(const char *filename, /* I - File to read */
1470 char *namebuf, /* I - Name buffer */
1471 size_t namesize, /* I - Size of name buffer */
1472 const char **instance) /* I - Instance */
1473 {
1474 cups_file_t *fp; /* lpoptions file */
1475 char line[8192], /* Line from file */
1476 *value, /* Value for line */
1477 *nameptr; /* Pointer into name */
1478 int linenum; /* Current line */
1479
1480
1481 *namebuf = '\0';
1482
1483 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1484 {
1485 linenum = 0;
1486
1487 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1488 {
1489 if (!strcasecmp(line, "default") && value)
1490 {
1491 strlcpy(namebuf, value, namesize);
1492
1493 if ((nameptr = strchr(namebuf, ' ')) != NULL)
1494 *nameptr = '\0';
1495 if ((nameptr = strchr(namebuf, '\t')) != NULL)
1496 *nameptr = '\0';
1497
1498 if ((nameptr = strchr(namebuf, '/')) != NULL)
1499 *nameptr++ = '\0';
1500
1501 *instance = nameptr;
1502 break;
1503 }
1504 }
1505
1506 cupsFileClose(fp);
1507 }
1508
1509 return (*namebuf ? namebuf : NULL);
1510 }
1511
1512
1513 /*
1514 * 'cups_get_dests()' - Get destinations from a file.
1515 */
1516
1517 static int /* O - Number of destinations */
1518 cups_get_dests(const char *filename, /* I - File to read from */
1519 const char *match_name, /* I - Destination name we want */
1520 const char *match_inst, /* I - Instance name we want */
1521 int num_dests, /* I - Number of destinations */
1522 cups_dest_t **dests) /* IO - Destinations */
1523 {
1524 int i; /* Looping var */
1525 cups_dest_t *dest; /* Current destination */
1526 cups_file_t *fp; /* File pointer */
1527 char line[8192], /* Line from file */
1528 *lineptr, /* Pointer into line */
1529 *name, /* Name of destination/option */
1530 *instance; /* Instance of destination */
1531 int linenum; /* Current line number */
1532 const char *printer; /* PRINTER or LPDEST */
1533
1534
1535 DEBUG_printf(("cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1536 "match_inst=\"%s\", num_dests=%d, dests=%p)\n", filename,
1537 match_name ? match_name : "(null)",
1538 match_inst ? match_inst : "(null)", num_dests, dests));
1539
1540 /*
1541 * Try to open the file...
1542 */
1543
1544 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1545 return (num_dests);
1546
1547 /*
1548 * Check environment variables...
1549 */
1550
1551 if ((printer = getenv("LPDEST")) == NULL)
1552 if ((printer = getenv("PRINTER")) != NULL)
1553 if (strcmp(printer, "lp") == 0)
1554 printer = NULL;
1555
1556 DEBUG_printf(("cups_get_dests: printer=\"%s\"\n",
1557 printer ? printer : "(null)"));
1558
1559 /*
1560 * Read each printer; each line looks like:
1561 *
1562 * Dest name[/instance] options
1563 * Default name[/instance] options
1564 */
1565
1566 linenum = 0;
1567
1568 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
1569 {
1570 /*
1571 * See what type of line it is...
1572 */
1573
1574 DEBUG_printf(("cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"\n",
1575 linenum, line, lineptr ? lineptr : "(null)"));
1576
1577 if ((strcasecmp(line, "dest") && strcasecmp(line, "default")) || !lineptr)
1578 {
1579 DEBUG_puts("cups_get_dests: Not a dest or default line...");
1580 continue;
1581 }
1582
1583 name = lineptr;
1584
1585 /*
1586 * Search for an instance...
1587 */
1588
1589 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1590 lineptr ++;
1591
1592 if (*lineptr == '/')
1593 {
1594 /*
1595 * Found an instance...
1596 */
1597
1598 *lineptr++ = '\0';
1599 instance = lineptr;
1600
1601 /*
1602 * Search for an instance...
1603 */
1604
1605 while (!isspace(*lineptr & 255) && *lineptr)
1606 lineptr ++;
1607 }
1608 else
1609 instance = NULL;
1610
1611 if (*lineptr)
1612 *lineptr++ = '\0';
1613
1614 DEBUG_printf(("cups_get_dests: name=\"%s\", instance=\"%s\"\n", name,
1615 instance));
1616
1617 /*
1618 * See if the primary instance of the destination exists; if not,
1619 * ignore this entry and move on...
1620 */
1621
1622 if (match_name)
1623 {
1624 if (strcasecmp(name, match_name) ||
1625 (!instance && match_inst) ||
1626 (instance && !match_inst) ||
1627 (instance && strcasecmp(instance, match_inst)))
1628 continue;
1629
1630 dest = *dests;
1631 }
1632 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
1633 {
1634 DEBUG_puts("cups_get_dests: Not found!");
1635 continue;
1636 }
1637 else
1638 {
1639 /*
1640 * Add the destination...
1641 */
1642
1643 num_dests = cupsAddDest(name, instance, num_dests, dests);
1644
1645 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1646 {
1647 /*
1648 * Out of memory!
1649 */
1650
1651 DEBUG_puts("cups_get_dests: Out of memory!");
1652 break;
1653 }
1654 }
1655
1656 /*
1657 * Add options until we hit the end of the line...
1658 */
1659
1660 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
1661 &(dest->options));
1662
1663 /*
1664 * If we found what we were looking for, stop now...
1665 */
1666
1667 if (match_name)
1668 break;
1669
1670 /*
1671 * Set this as default if needed...
1672 */
1673
1674 if (!printer && !strcasecmp(line, "default"))
1675 {
1676 DEBUG_puts("cups_get_dests: Setting as default...");
1677
1678 for (i = 0; i < num_dests; i ++)
1679 (*dests)[i].is_default = 0;
1680
1681 dest->is_default = 1;
1682 }
1683 }
1684
1685 /*
1686 * Close the file and return...
1687 */
1688
1689 cupsFileClose(fp);
1690
1691 return (num_dests);
1692 }
1693
1694
1695 /*
1696 * 'cups_get_sdests()' - Get destinations from a server.
1697 */
1698
1699 static int /* O - Number of destinations */
1700 cups_get_sdests(http_t *http, /* I - Connection to server or CUPS_HTTP_DEFAULT */
1701 ipp_op_t op, /* I - IPP operation */
1702 const char *name, /* I - Name of destination */
1703 int num_dests, /* I - Number of destinations */
1704 cups_dest_t **dests) /* IO - Destinations */
1705 {
1706 int i; /* Looping var */
1707 cups_dest_t *dest; /* Current destination */
1708 ipp_t *request, /* IPP Request */
1709 *response; /* IPP Response */
1710 ipp_attribute_t *attr; /* Current attribute */
1711 const char *printer_name; /* printer-name attribute */
1712 char uri[1024]; /* printer-uri value */
1713 int num_options; /* Number of options */
1714 cups_option_t *options; /* Options */
1715 #ifdef __APPLE__
1716 char media_default[41]; /* Default paper size */
1717 #endif /* __APPLE__ */
1718 char optname[1024], /* Option name */
1719 value[2048], /* Option value */
1720 *ptr; /* Pointer into name/value */
1721 static const char * const pattrs[] = /* Attributes we're interested in */
1722 {
1723 "auth-info-required",
1724 "job-sheets-default",
1725 "marker-change-time",
1726 "marker-colors",
1727 "marker-high-levels",
1728 "marker-levels",
1729 "marker-low-levels",
1730 "marker-message",
1731 "marker-names",
1732 "marker-types",
1733 #ifdef __APPLE__
1734 "media-supported",
1735 #endif /* __APPLE__ */
1736 "printer-commands",
1737 "printer-info",
1738 "printer-is-accepting-jobs",
1739 "printer-is-shared",
1740 "printer-location",
1741 "printer-make-and-model",
1742 "printer-name",
1743 "printer-state",
1744 "printer-state-change-time",
1745 "printer-state-reasons",
1746 "printer-type",
1747 "printer-defaults"
1748 };
1749
1750
1751 #ifdef __APPLE__
1752 /*
1753 * Get the default paper size...
1754 */
1755
1756 appleGetPaperSize(media_default, sizeof(media_default));
1757 #endif /* __APPLE__ */
1758
1759 /*
1760 * Build a CUPS_GET_PRINTERS or IPP_GET_PRINTER_ATTRIBUTES request, which
1761 * require the following attributes:
1762 *
1763 * attributes-charset
1764 * attributes-natural-language
1765 * requesting-user-name
1766 * printer-uri [for IPP_GET_PRINTER_ATTRIBUTES]
1767 */
1768
1769 request = ippNewRequest(op);
1770
1771 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1772 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1773 NULL, pattrs);
1774
1775 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1776 "requesting-user-name", NULL, cupsUser());
1777
1778 if (name)
1779 {
1780 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1781 "localhost", ippPort(), "/printers/%s", name);
1782 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1783 uri);
1784 }
1785
1786 /*
1787 * Do the request and get back a response...
1788 */
1789
1790 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1791 {
1792 for (attr = response->attrs; attr != NULL; attr = attr->next)
1793 {
1794 /*
1795 * Skip leading attributes until we hit a printer...
1796 */
1797
1798 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1799 attr = attr->next;
1800
1801 if (attr == NULL)
1802 break;
1803
1804 /*
1805 * Pull the needed attributes from this printer...
1806 */
1807
1808 printer_name = NULL;
1809 num_options = 0;
1810 options = NULL;
1811
1812 for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
1813 {
1814 if (attr->value_tag != IPP_TAG_INTEGER &&
1815 attr->value_tag != IPP_TAG_ENUM &&
1816 attr->value_tag != IPP_TAG_BOOLEAN &&
1817 attr->value_tag != IPP_TAG_TEXT &&
1818 attr->value_tag != IPP_TAG_TEXTLANG &&
1819 attr->value_tag != IPP_TAG_NAME &&
1820 attr->value_tag != IPP_TAG_NAMELANG &&
1821 attr->value_tag != IPP_TAG_KEYWORD &&
1822 attr->value_tag != IPP_TAG_RANGE)
1823 continue;
1824
1825 if (!strcmp(attr->name, "auth-info-required") ||
1826 !strcmp(attr->name, "marker-change-time") ||
1827 !strcmp(attr->name, "marker-colors") ||
1828 !strcmp(attr->name, "marker-high-levels") ||
1829 !strcmp(attr->name, "marker-levels") ||
1830 !strcmp(attr->name, "marker-low-levels") ||
1831 !strcmp(attr->name, "marker-message") ||
1832 !strcmp(attr->name, "marker-names") ||
1833 !strcmp(attr->name, "marker-types") ||
1834 !strcmp(attr->name, "printer-commands") ||
1835 !strcmp(attr->name, "printer-info") ||
1836 !strcmp(attr->name, "printer-is-shared") ||
1837 !strcmp(attr->name, "printer-make-and-model") ||
1838 !strcmp(attr->name, "printer-state") ||
1839 !strcmp(attr->name, "printer-state-change-time") ||
1840 !strcmp(attr->name, "printer-type") ||
1841 !strcmp(attr->name, "printer-is-accepting-jobs") ||
1842 !strcmp(attr->name, "printer-location") ||
1843 !strcmp(attr->name, "printer-state-reasons"))
1844 {
1845 /*
1846 * Add a printer description attribute...
1847 */
1848
1849 num_options = cupsAddOption(attr->name,
1850 cups_make_string(attr, value,
1851 sizeof(value)),
1852 num_options, &options);
1853 }
1854 #ifdef __APPLE__
1855 else if (!strcmp(attr->name, "media-supported"))
1856 {
1857 /*
1858 * See if we can set a default media size...
1859 */
1860
1861 for (i = 0; i < attr->num_values; i ++)
1862 if (!strcasecmp(media_default, attr->values[i].string.text))
1863 {
1864 num_options = cupsAddOption("media", media_default, num_options,
1865 &options);
1866 break;
1867 }
1868 }
1869 #endif /* __APPLE__ */
1870 else if (!strcmp(attr->name, "printer-name") &&
1871 attr->value_tag == IPP_TAG_NAME)
1872 printer_name = attr->values[0].string.text;
1873 else if (strncmp(attr->name, "notify-", 7) &&
1874 (attr->value_tag == IPP_TAG_BOOLEAN ||
1875 attr->value_tag == IPP_TAG_ENUM ||
1876 attr->value_tag == IPP_TAG_INTEGER ||
1877 attr->value_tag == IPP_TAG_KEYWORD ||
1878 attr->value_tag == IPP_TAG_NAME ||
1879 attr->value_tag == IPP_TAG_RANGE) &&
1880 (ptr = strstr(attr->name, "-default")) != NULL)
1881 {
1882 /*
1883 * Add a default option...
1884 */
1885
1886 strlcpy(optname, attr->name, sizeof(optname));
1887 optname[ptr - attr->name] = '\0';
1888
1889 if (strcasecmp(optname, "media") ||
1890 !cupsGetOption("media", num_options, options))
1891 num_options = cupsAddOption(optname,
1892 cups_make_string(attr, value,
1893 sizeof(value)),
1894 num_options, &options);
1895 }
1896 }
1897
1898 /*
1899 * See if we have everything needed...
1900 */
1901
1902 if (!printer_name)
1903 {
1904 cupsFreeOptions(num_options, options);
1905
1906 if (attr == NULL)
1907 break;
1908 else
1909 continue;
1910 }
1911
1912 if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
1913 {
1914 dest->num_options = num_options;
1915 dest->options = options;
1916 }
1917 else
1918 cupsFreeOptions(num_options, options);
1919
1920 if (attr == NULL)
1921 break;
1922 }
1923
1924 ippDelete(response);
1925 }
1926
1927 /*
1928 * Return the count...
1929 */
1930
1931 return (num_dests);
1932 }
1933
1934
1935 /*
1936 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
1937 * attribute.
1938 */
1939
1940 static char * /* O - New string */
1941 cups_make_string(
1942 ipp_attribute_t *attr, /* I - Attribute to convert */
1943 char *buffer, /* I - Buffer */
1944 size_t bufsize) /* I - Size of buffer */
1945 {
1946 int i; /* Looping var */
1947 char *ptr, /* Pointer into buffer */
1948 *end, /* Pointer to end of buffer */
1949 *valptr; /* Pointer into string attribute */
1950
1951
1952 /*
1953 * Return quickly if we have a single string value...
1954 */
1955
1956 if (attr->num_values == 1 &&
1957 attr->value_tag != IPP_TAG_INTEGER &&
1958 attr->value_tag != IPP_TAG_ENUM &&
1959 attr->value_tag != IPP_TAG_BOOLEAN &&
1960 attr->value_tag != IPP_TAG_RANGE)
1961 return (attr->values[0].string.text);
1962
1963 /*
1964 * Copy the values to the string, separating with commas and escaping strings
1965 * as needed...
1966 */
1967
1968 end = buffer + bufsize - 1;
1969
1970 for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
1971 {
1972 if (i)
1973 *ptr++ = ',';
1974
1975 switch (attr->value_tag)
1976 {
1977 case IPP_TAG_INTEGER :
1978 case IPP_TAG_ENUM :
1979 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].integer);
1980 break;
1981
1982 case IPP_TAG_BOOLEAN :
1983 if (attr->values[i].boolean)
1984 strlcpy(ptr, "true", end - ptr + 1);
1985 else
1986 strlcpy(ptr, "false", end - ptr + 1);
1987 break;
1988
1989 case IPP_TAG_RANGE :
1990 if (attr->values[i].range.lower == attr->values[i].range.upper)
1991 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].range.lower);
1992 else
1993 snprintf(ptr, end - ptr + 1, "%d-%d", attr->values[i].range.lower,
1994 attr->values[i].range.upper);
1995 break;
1996
1997 default :
1998 for (valptr = attr->values[i].string.text;
1999 *valptr && ptr < end;)
2000 {
2001 if (strchr(" \t\n\\\'\"", *valptr))
2002 {
2003 if (ptr >= (end - 1))
2004 break;
2005
2006 *ptr++ = '\\';
2007 }
2008
2009 *ptr++ = *valptr++;
2010 }
2011
2012 *ptr = '\0';
2013 break;
2014 }
2015
2016 ptr += strlen(ptr);
2017 }
2018
2019 *ptr = '\0';
2020
2021 return (buffer);
2022 }
2023
2024
2025 /*
2026 * End of "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $".
2027 */