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