]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
191ec7b84fe6e445806f13937cd3607e1fed14f4
[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, NULL);
1215
1216 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1217 &kCFTypeDictionaryKeyCallBacks,
1218 &kCFTypeDictionaryValueCallBacks);
1219
1220 if (newlocation && newlocations)
1221 {
1222 /*
1223 * Put the new location at the front of the array...
1224 */
1225
1226 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
1227 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, newprinter);
1228 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
1229
1230 /*
1231 * Limit the number of locations to 10...
1232 */
1233
1234 while (CFArrayGetCount(newlocations) > 10)
1235 CFArrayRemoveValueAtIndex(newlocations, 10);
1236
1237 /*
1238 * Push the changes out...
1239 */
1240
1241 CFPreferencesSetAppValue(kLocationHistoryArrayKey, newlocations,
1242 kPMPrintingPreferences);
1243 CFPreferencesAppSynchronize(kPMPrintingPreferences);
1244 notify_post("com.apple.printerPrefsChange");
1245 }
1246
1247 if (newlocations)
1248 CFRelease(newlocations);
1249
1250 if (newlocation)
1251 CFRelease(newlocation);
1252 }
1253
1254 if (locations)
1255 CFRelease(locations);
1256
1257 CFRelease(network);
1258 CFRelease(newprinter);
1259 }
1260
1261
1262 /*
1263 * 'appleUseLastPrinter()' - Get the default printer preference value.
1264 */
1265
1266 static int /* O - 1 to use last printer, 0 otherwise */
1267 appleUseLastPrinter(void)
1268 {
1269 CFPropertyListRef uselast; /* Use last printer preference value */
1270
1271
1272 if (getenv("CUPS_DISABLE_APPLE_DEFAULT"))
1273 return (0);
1274
1275 if ((uselast = CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey,
1276 kPMPrintingPreferences)) != NULL)
1277 {
1278 CFRelease(uselast);
1279
1280 if (uselast == kCFBooleanFalse)
1281 return (0);
1282 }
1283
1284 return (1);
1285 }
1286 #endif /* __APPLE__ */
1287
1288
1289 /*
1290 * 'cups_add_dest()' - Add a destination to the array.
1291 *
1292 * Unlike cupsAddDest(), this function does not check for duplicates.
1293 */
1294
1295 static cups_dest_t * /* O - New destination */
1296 cups_add_dest(const char *name, /* I - Name of destination */
1297 const char *instance, /* I - Instance or NULL */
1298 int *num_dests, /* IO - Number of destinations */
1299 cups_dest_t **dests) /* IO - Destinations */
1300 {
1301 int insert, /* Insertion point */
1302 diff; /* Result of comparison */
1303 cups_dest_t *dest; /* Destination pointer */
1304
1305
1306 /*
1307 * Add new destination...
1308 */
1309
1310 if (*num_dests == 0)
1311 dest = malloc(sizeof(cups_dest_t));
1312 else
1313 dest = realloc(*dests, sizeof(cups_dest_t) * (*num_dests + 1));
1314
1315 if (!dest)
1316 return (NULL);
1317
1318 *dests = dest;
1319
1320 /*
1321 * Find where to insert the destination...
1322 */
1323
1324 if (*num_dests == 0)
1325 insert = 0;
1326 else
1327 {
1328 insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
1329 &diff);
1330
1331 if (diff > 0)
1332 insert ++;
1333 }
1334
1335 /*
1336 * Move the array elements as needed...
1337 */
1338
1339 if (insert < *num_dests)
1340 memmove(*dests + insert + 1, *dests + insert,
1341 (*num_dests - insert) * sizeof(cups_dest_t));
1342
1343 (*num_dests) ++;
1344
1345 /*
1346 * Initialize the destination...
1347 */
1348
1349 dest = *dests + insert;
1350 dest->name = _cupsStrAlloc(name);
1351 dest->instance = _cupsStrAlloc(instance);
1352 dest->is_default = 0;
1353 dest->num_options = 0;
1354 dest->options = (cups_option_t *)0;
1355
1356 return (dest);
1357 }
1358
1359
1360 /*
1361 * 'cups_compare_dests()' - Compare two destinations.
1362 */
1363
1364 static int /* O - Result of comparison */
1365 cups_compare_dests(cups_dest_t *a, /* I - First destination */
1366 cups_dest_t *b) /* I - Second destination */
1367 {
1368 int diff; /* Difference */
1369
1370
1371 if ((diff = strcasecmp(a->name, b->name)) != 0)
1372 return (diff);
1373 else if (a->instance && b->instance)
1374 return (strcasecmp(a->instance, b->instance));
1375 else
1376 return ((a->instance && !b->instance) - (!a->instance && b->instance));
1377 }
1378
1379
1380 /*
1381 * 'cups_find_dest()' - Find a destination using a binary search.
1382 */
1383
1384 static int /* O - Index of match */
1385 cups_find_dest(const char *name, /* I - Destination name */
1386 const char *instance, /* I - Instance or NULL */
1387 int num_dests, /* I - Number of destinations */
1388 cups_dest_t *dests, /* I - Destinations */
1389 int prev, /* I - Previous index */
1390 int *rdiff) /* O - Difference of match */
1391 {
1392 int left, /* Low mark for binary search */
1393 right, /* High mark for binary search */
1394 current, /* Current index */
1395 diff; /* Result of comparison */
1396 cups_dest_t key; /* Search key */
1397
1398
1399 key.name = (char *)name;
1400 key.instance = (char *)instance;
1401
1402 if (prev >= 0)
1403 {
1404 /*
1405 * Start search on either side of previous...
1406 */
1407
1408 if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
1409 (diff < 0 && prev == 0) ||
1410 (diff > 0 && prev == (num_dests - 1)))
1411 {
1412 *rdiff = diff;
1413 return (prev);
1414 }
1415 else if (diff < 0)
1416 {
1417 /*
1418 * Start with previous on right side...
1419 */
1420
1421 left = 0;
1422 right = prev;
1423 }
1424 else
1425 {
1426 /*
1427 * Start wih previous on left side...
1428 */
1429
1430 left = prev;
1431 right = num_dests - 1;
1432 }
1433 }
1434 else
1435 {
1436 /*
1437 * Start search in the middle...
1438 */
1439
1440 left = 0;
1441 right = num_dests - 1;
1442 }
1443
1444 do
1445 {
1446 current = (left + right) / 2;
1447 diff = cups_compare_dests(&key, dests + current);
1448
1449 if (diff == 0)
1450 break;
1451 else if (diff < 0)
1452 right = current;
1453 else
1454 left = current;
1455 }
1456 while ((right - left) > 1);
1457
1458 if (diff != 0)
1459 {
1460 /*
1461 * Check the last 1 or 2 elements...
1462 */
1463
1464 if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
1465 current = left;
1466 else
1467 {
1468 diff = cups_compare_dests(&key, dests + right);
1469 current = right;
1470 }
1471 }
1472
1473 /*
1474 * Return the closest destination and the difference...
1475 */
1476
1477 *rdiff = diff;
1478
1479 return (current);
1480 }
1481
1482
1483 /*
1484 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1485 */
1486
1487 static char * /* O - Default destination or NULL */
1488 cups_get_default(const char *filename, /* I - File to read */
1489 char *namebuf, /* I - Name buffer */
1490 size_t namesize, /* I - Size of name buffer */
1491 const char **instance) /* I - Instance */
1492 {
1493 cups_file_t *fp; /* lpoptions file */
1494 char line[8192], /* Line from file */
1495 *value, /* Value for line */
1496 *nameptr; /* Pointer into name */
1497 int linenum; /* Current line */
1498
1499
1500 *namebuf = '\0';
1501
1502 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1503 {
1504 linenum = 0;
1505
1506 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1507 {
1508 if (!strcasecmp(line, "default") && value)
1509 {
1510 strlcpy(namebuf, value, namesize);
1511
1512 if ((nameptr = strchr(namebuf, ' ')) != NULL)
1513 *nameptr = '\0';
1514 if ((nameptr = strchr(namebuf, '\t')) != NULL)
1515 *nameptr = '\0';
1516
1517 if ((nameptr = strchr(namebuf, '/')) != NULL)
1518 *nameptr++ = '\0';
1519
1520 *instance = nameptr;
1521 break;
1522 }
1523 }
1524
1525 cupsFileClose(fp);
1526 }
1527
1528 return (*namebuf ? namebuf : NULL);
1529 }
1530
1531
1532 /*
1533 * 'cups_get_dests()' - Get destinations from a file.
1534 */
1535
1536 static int /* O - Number of destinations */
1537 cups_get_dests(
1538 const char *filename, /* I - File to read from */
1539 const char *match_name, /* I - Destination name we want */
1540 const char *match_inst, /* I - Instance name we want */
1541 int user_default_set, /* I - User default printer set? */
1542 int num_dests, /* I - Number of destinations */
1543 cups_dest_t **dests) /* IO - Destinations */
1544 {
1545 int i; /* Looping var */
1546 cups_dest_t *dest; /* Current destination */
1547 cups_file_t *fp; /* File pointer */
1548 char line[8192], /* Line from file */
1549 *lineptr, /* Pointer into line */
1550 *name, /* Name of destination/option */
1551 *instance; /* Instance of destination */
1552 int linenum; /* Current line number */
1553
1554
1555 DEBUG_printf(("7cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1556 "match_inst=\"%s\", user_default_set=%d, num_dests=%d, "
1557 "dests=%p)", filename, match_name, match_inst,
1558 user_default_set, num_dests, dests));
1559
1560 /*
1561 * Try to open the file...
1562 */
1563
1564 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1565 return (num_dests);
1566
1567 /*
1568 * Read each printer; each line looks like:
1569 *
1570 * Dest name[/instance] options
1571 * Default name[/instance] options
1572 */
1573
1574 linenum = 0;
1575
1576 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
1577 {
1578 /*
1579 * See what type of line it is...
1580 */
1581
1582 DEBUG_printf(("9cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"",
1583 linenum, line, lineptr));
1584
1585 if ((strcasecmp(line, "dest") && strcasecmp(line, "default")) || !lineptr)
1586 {
1587 DEBUG_puts("9cups_get_dests: Not a dest or default line...");
1588 continue;
1589 }
1590
1591 name = lineptr;
1592
1593 /*
1594 * Search for an instance...
1595 */
1596
1597 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1598 lineptr ++;
1599
1600 if (*lineptr == '/')
1601 {
1602 /*
1603 * Found an instance...
1604 */
1605
1606 *lineptr++ = '\0';
1607 instance = lineptr;
1608
1609 /*
1610 * Search for an instance...
1611 */
1612
1613 while (!isspace(*lineptr & 255) && *lineptr)
1614 lineptr ++;
1615 }
1616 else
1617 instance = NULL;
1618
1619 if (*lineptr)
1620 *lineptr++ = '\0';
1621
1622 DEBUG_printf(("9cups_get_dests: name=\"%s\", instance=\"%s\"", name,
1623 instance));
1624
1625 /*
1626 * See if the primary instance of the destination exists; if not,
1627 * ignore this entry and move on...
1628 */
1629
1630 if (match_name)
1631 {
1632 if (strcasecmp(name, match_name) ||
1633 (!instance && match_inst) ||
1634 (instance && !match_inst) ||
1635 (instance && strcasecmp(instance, match_inst)))
1636 continue;
1637
1638 dest = *dests;
1639 }
1640 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
1641 {
1642 DEBUG_puts("9cups_get_dests: Not found!");
1643 continue;
1644 }
1645 else
1646 {
1647 /*
1648 * Add the destination...
1649 */
1650
1651 num_dests = cupsAddDest(name, instance, num_dests, dests);
1652
1653 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1654 {
1655 /*
1656 * Out of memory!
1657 */
1658
1659 DEBUG_puts("9cups_get_dests: Out of memory!");
1660 break;
1661 }
1662 }
1663
1664 /*
1665 * Add options until we hit the end of the line...
1666 */
1667
1668 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
1669 &(dest->options));
1670
1671 /*
1672 * If we found what we were looking for, stop now...
1673 */
1674
1675 if (match_name)
1676 break;
1677
1678 /*
1679 * Set this as default if needed...
1680 */
1681
1682 if (!user_default_set && !strcasecmp(line, "default"))
1683 {
1684 DEBUG_puts("9cups_get_dests: Setting as default...");
1685
1686 for (i = 0; i < num_dests; i ++)
1687 (*dests)[i].is_default = 0;
1688
1689 dest->is_default = 1;
1690 }
1691 }
1692
1693 /*
1694 * Close the file and return...
1695 */
1696
1697 cupsFileClose(fp);
1698
1699 return (num_dests);
1700 }
1701
1702
1703 /*
1704 * 'cups_get_sdests()' - Get destinations from a server.
1705 */
1706
1707 static int /* O - Number of destinations */
1708 cups_get_sdests(http_t *http, /* I - Connection to server or CUPS_HTTP_DEFAULT */
1709 ipp_op_t op, /* I - IPP operation */
1710 const char *name, /* I - Name of destination */
1711 int num_dests, /* I - Number of destinations */
1712 cups_dest_t **dests) /* IO - Destinations */
1713 {
1714 cups_dest_t *dest; /* Current destination */
1715 ipp_t *request, /* IPP Request */
1716 *response; /* IPP Response */
1717 ipp_attribute_t *attr; /* Current attribute */
1718 const char *printer_name; /* printer-name attribute */
1719 char uri[1024]; /* printer-uri value */
1720 int num_options; /* Number of options */
1721 cups_option_t *options; /* Options */
1722 #ifdef __APPLE__
1723 char media_default[41]; /* Default paper size */
1724 #endif /* __APPLE__ */
1725 char optname[1024], /* Option name */
1726 value[2048], /* Option value */
1727 *ptr; /* Pointer into name/value */
1728 static const char * const pattrs[] = /* Attributes we're interested in */
1729 {
1730 "auth-info-required",
1731 "device-uri",
1732 "job-sheets-default",
1733 "marker-change-time",
1734 "marker-colors",
1735 "marker-high-levels",
1736 "marker-levels",
1737 "marker-low-levels",
1738 "marker-message",
1739 "marker-names",
1740 "marker-types",
1741 #ifdef __APPLE__
1742 "media-supported",
1743 #endif /* __APPLE__ */
1744 "printer-commands",
1745 "printer-defaults",
1746 "printer-info",
1747 "printer-is-accepting-jobs",
1748 "printer-is-shared",
1749 "printer-location",
1750 "printer-make-and-model",
1751 "printer-name",
1752 "printer-state",
1753 "printer-state-change-time",
1754 "printer-state-reasons",
1755 "printer-type",
1756 "printer-uri-supported"
1757 };
1758
1759
1760 #ifdef __APPLE__
1761 /*
1762 * Get the default paper size...
1763 */
1764
1765 appleGetPaperSize(media_default, sizeof(media_default));
1766 #endif /* __APPLE__ */
1767
1768 /*
1769 * Build a CUPS_GET_PRINTERS or IPP_GET_PRINTER_ATTRIBUTES request, which
1770 * require the following attributes:
1771 *
1772 * attributes-charset
1773 * attributes-natural-language
1774 * requesting-user-name
1775 * printer-uri [for IPP_GET_PRINTER_ATTRIBUTES]
1776 */
1777
1778 request = ippNewRequest(op);
1779
1780 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1781 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1782 NULL, pattrs);
1783
1784 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1785 "requesting-user-name", NULL, cupsUser());
1786
1787 if (name && op != CUPS_GET_DEFAULT)
1788 {
1789 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1790 "localhost", ippPort(), "/printers/%s", name);
1791 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1792 uri);
1793 }
1794
1795 /*
1796 * Do the request and get back a response...
1797 */
1798
1799 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1800 {
1801 for (attr = response->attrs; attr != NULL; attr = attr->next)
1802 {
1803 /*
1804 * Skip leading attributes until we hit a printer...
1805 */
1806
1807 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1808 attr = attr->next;
1809
1810 if (attr == NULL)
1811 break;
1812
1813 /*
1814 * Pull the needed attributes from this printer...
1815 */
1816
1817 printer_name = NULL;
1818 num_options = 0;
1819 options = NULL;
1820
1821 for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
1822 {
1823 if (attr->value_tag != IPP_TAG_INTEGER &&
1824 attr->value_tag != IPP_TAG_ENUM &&
1825 attr->value_tag != IPP_TAG_BOOLEAN &&
1826 attr->value_tag != IPP_TAG_TEXT &&
1827 attr->value_tag != IPP_TAG_TEXTLANG &&
1828 attr->value_tag != IPP_TAG_NAME &&
1829 attr->value_tag != IPP_TAG_NAMELANG &&
1830 attr->value_tag != IPP_TAG_KEYWORD &&
1831 attr->value_tag != IPP_TAG_RANGE &&
1832 attr->value_tag != IPP_TAG_URI)
1833 continue;
1834
1835 if (!strcmp(attr->name, "auth-info-required") ||
1836 !strcmp(attr->name, "device-uri") ||
1837 !strcmp(attr->name, "marker-change-time") ||
1838 !strcmp(attr->name, "marker-colors") ||
1839 !strcmp(attr->name, "marker-high-levels") ||
1840 !strcmp(attr->name, "marker-levels") ||
1841 !strcmp(attr->name, "marker-low-levels") ||
1842 !strcmp(attr->name, "marker-message") ||
1843 !strcmp(attr->name, "marker-names") ||
1844 !strcmp(attr->name, "marker-types") ||
1845 !strcmp(attr->name, "printer-commands") ||
1846 !strcmp(attr->name, "printer-info") ||
1847 !strcmp(attr->name, "printer-is-shared") ||
1848 !strcmp(attr->name, "printer-make-and-model") ||
1849 !strcmp(attr->name, "printer-state") ||
1850 !strcmp(attr->name, "printer-state-change-time") ||
1851 !strcmp(attr->name, "printer-type") ||
1852 !strcmp(attr->name, "printer-is-accepting-jobs") ||
1853 !strcmp(attr->name, "printer-location") ||
1854 !strcmp(attr->name, "printer-state-reasons") ||
1855 !strcmp(attr->name, "printer-uri-supported"))
1856 {
1857 /*
1858 * Add a printer description attribute...
1859 */
1860
1861 num_options = cupsAddOption(attr->name,
1862 cups_make_string(attr, value,
1863 sizeof(value)),
1864 num_options, &options);
1865 }
1866 #ifdef __APPLE__
1867 else if (!strcmp(attr->name, "media-supported"))
1868 {
1869 /*
1870 * See if we can set a default media size...
1871 */
1872
1873 int i; /* Looping var */
1874
1875 for (i = 0; i < attr->num_values; i ++)
1876 if (!strcasecmp(media_default, attr->values[i].string.text))
1877 {
1878 num_options = cupsAddOption("media", media_default, num_options,
1879 &options);
1880 break;
1881 }
1882 }
1883 #endif /* __APPLE__ */
1884 else if (!strcmp(attr->name, "printer-name") &&
1885 attr->value_tag == IPP_TAG_NAME)
1886 printer_name = attr->values[0].string.text;
1887 else if (strncmp(attr->name, "notify-", 7) &&
1888 (attr->value_tag == IPP_TAG_BOOLEAN ||
1889 attr->value_tag == IPP_TAG_ENUM ||
1890 attr->value_tag == IPP_TAG_INTEGER ||
1891 attr->value_tag == IPP_TAG_KEYWORD ||
1892 attr->value_tag == IPP_TAG_NAME ||
1893 attr->value_tag == IPP_TAG_RANGE) &&
1894 (ptr = strstr(attr->name, "-default")) != NULL)
1895 {
1896 /*
1897 * Add a default option...
1898 */
1899
1900 strlcpy(optname, attr->name, sizeof(optname));
1901 optname[ptr - attr->name] = '\0';
1902
1903 if (strcasecmp(optname, "media") ||
1904 !cupsGetOption("media", num_options, options))
1905 num_options = cupsAddOption(optname,
1906 cups_make_string(attr, value,
1907 sizeof(value)),
1908 num_options, &options);
1909 }
1910 }
1911
1912 /*
1913 * See if we have everything needed...
1914 */
1915
1916 if (!printer_name)
1917 {
1918 cupsFreeOptions(num_options, options);
1919
1920 if (attr == NULL)
1921 break;
1922 else
1923 continue;
1924 }
1925
1926 if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
1927 {
1928 dest->num_options = num_options;
1929 dest->options = options;
1930 }
1931 else
1932 cupsFreeOptions(num_options, options);
1933
1934 if (attr == NULL)
1935 break;
1936 }
1937
1938 ippDelete(response);
1939 }
1940
1941 /*
1942 * Return the count...
1943 */
1944
1945 return (num_dests);
1946 }
1947
1948
1949 /*
1950 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
1951 * attribute.
1952 */
1953
1954 static char * /* O - New string */
1955 cups_make_string(
1956 ipp_attribute_t *attr, /* I - Attribute to convert */
1957 char *buffer, /* I - Buffer */
1958 size_t bufsize) /* I - Size of buffer */
1959 {
1960 int i; /* Looping var */
1961 char *ptr, /* Pointer into buffer */
1962 *end, /* Pointer to end of buffer */
1963 *valptr; /* Pointer into string attribute */
1964
1965
1966 /*
1967 * Return quickly if we have a single string value...
1968 */
1969
1970 if (attr->num_values == 1 &&
1971 attr->value_tag != IPP_TAG_INTEGER &&
1972 attr->value_tag != IPP_TAG_ENUM &&
1973 attr->value_tag != IPP_TAG_BOOLEAN &&
1974 attr->value_tag != IPP_TAG_RANGE)
1975 return (attr->values[0].string.text);
1976
1977 /*
1978 * Copy the values to the string, separating with commas and escaping strings
1979 * as needed...
1980 */
1981
1982 end = buffer + bufsize - 1;
1983
1984 for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
1985 {
1986 if (i)
1987 *ptr++ = ',';
1988
1989 switch (attr->value_tag)
1990 {
1991 case IPP_TAG_INTEGER :
1992 case IPP_TAG_ENUM :
1993 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].integer);
1994 break;
1995
1996 case IPP_TAG_BOOLEAN :
1997 if (attr->values[i].boolean)
1998 strlcpy(ptr, "true", end - ptr + 1);
1999 else
2000 strlcpy(ptr, "false", end - ptr + 1);
2001 break;
2002
2003 case IPP_TAG_RANGE :
2004 if (attr->values[i].range.lower == attr->values[i].range.upper)
2005 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].range.lower);
2006 else
2007 snprintf(ptr, end - ptr + 1, "%d-%d", attr->values[i].range.lower,
2008 attr->values[i].range.upper);
2009 break;
2010
2011 default :
2012 for (valptr = attr->values[i].string.text;
2013 *valptr && ptr < end;)
2014 {
2015 if (strchr(" \t\n\\\'\"", *valptr))
2016 {
2017 if (ptr >= (end - 1))
2018 break;
2019
2020 *ptr++ = '\\';
2021 }
2022
2023 *ptr++ = *valptr++;
2024 }
2025
2026 *ptr = '\0';
2027 break;
2028 }
2029
2030 ptr += strlen(ptr);
2031 }
2032
2033 *ptr = '\0';
2034
2035 return (buffer);
2036 }
2037
2038
2039 /*
2040 * End of "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $".
2041 */