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