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