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