]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest.c
Merge changes from CUPS 1.4svn-r8196 (CUPS 1.4b2)
[thirdparty/cups.git] / cups / dest.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $"
ef416fc2 3 *
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
6 *
080811b1 7 * Copyright 2007-2008 by Apple Inc.
b86bc4cf 8 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 9 *
10 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 15 *
16 * This file is subject to the Apple OS-Developed Software exception.
17 *
18 * Contents:
19 *
080811b1
MS
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.
426c6a59 34 * appleCopyLocations() - Copy the location history array.
749b1e90 35 * appleCopyNetwork() - Get the network ID for the current location.
080811b1 36 * appleGetDefault() - Get the default printer for this location.
426c6a59 37 * appleGetPaperSize() - Get the default paper size.
080811b1
MS
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.
426c6a59
MS
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.
080811b1
MS
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.
426c6a59
MS
47 * cups_make_string() - Make a comma-separated string of values from an IPP
48 * attribute.
ef416fc2 49 */
50
51/*
52 * Include necessary headers...
53 */
54
a4924f6c 55#include "debug.h"
ef416fc2 56#include "globals.h"
57#include <stdlib.h>
58#include <ctype.h>
b423cd4c 59#include <sys/stat.h>
ef416fc2 60
fa73b229 61#ifdef HAVE_NOTIFY_H
62# include <notify.h>
63#endif /* HAVE_NOTIFY_H */
64
080811b1
MS
65#ifdef __APPLE__
66# include <sys/cdefs.h>
67# include <CoreFoundation/CoreFoundation.h>
68# include <SystemConfiguration/SystemConfiguration.h>
426c6a59 69# define kDefaultPaperIDKey CFSTR("DefaultPaperID")
080811b1
MS
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
ef416fc2 77
78/*
79 * Local functions...
80 */
81
080811b1 82#ifdef __APPLE__
06d4e77b 83static CFArrayRef appleCopyLocations(void);
749b1e90 84static CFStringRef appleCopyNetwork(void);
080811b1 85static char *appleGetDefault(char *name, int namesize);
426c6a59 86static char *appleGetPaperSize(char *name, int namesize);
080811b1
MS
87static CFStringRef appleGetPrinter(CFArrayRef locations, CFStringRef network,
88 CFIndex *locindex);
89static void appleSetDefault(const char *name);
90static int appleUseLastPrinter(void);
91#endif /* __APPLE__ */
426c6a59
MS
92static cups_dest_t *cups_add_dest(const char *name, const char *instance,
93 int *num_dests, cups_dest_t **dests);
94static int cups_compare_dests(cups_dest_t *a, cups_dest_t *b);
95static int cups_find_dest(const char *name, const char *instance,
96 int num_dests, cups_dest_t *dests, int prev,
97 int *rdiff);
080811b1 98static char *cups_get_default(const char *filename, char *namebuf,
426c6a59 99 size_t namesize, const char **instance);
a4924f6c
MS
100static int cups_get_dests(const char *filename, const char *match_name,
101 const char *match_inst, int num_dests,
ef416fc2 102 cups_dest_t **dests);
a4924f6c
MS
103static int cups_get_sdests(http_t *http, ipp_op_t op, const char *name,
104 int num_dests, cups_dest_t **dests);
426c6a59
MS
105static char *cups_make_string(ipp_attribute_t *attr, char *buffer,
106 size_t bufsize);
ef416fc2 107
108
109/*
110 * 'cupsAddDest()' - Add a destination to the list of destinations.
111 *
2abf387c 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 *
5a738aea 120 * Use the @link cupsSaveDests@ function to save the updated list of
2abf387c 121 * destinations to the user's lpoptions file.
ef416fc2 122 */
123
124int /* O - New number of destinations */
2abf387c 125cupsAddDest(const char *name, /* I - Destination name */
5a738aea 126 const char *instance, /* I - Instance name or @code NULL@ for none/primary */
ef416fc2 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 */
2abf387c 132 cups_dest_t *parent; /* Parent destination */
426c6a59
MS
133 cups_option_t *doption, /* Current destination option */
134 *poption; /* Current parent option */
ef416fc2 135
136
2abf387c 137 if (!name || !dests)
ef416fc2 138 return (0);
139
426c6a59
MS
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);
ef416fc2 145
426c6a59 146 dest = cups_add_dest(name, instance, &num_dests, dests);
2abf387c 147
426c6a59
MS
148 if (instance && parent && parent->num_options > 0)
149 {
150 /*
151 * Copy options from parent...
152 */
ef416fc2 153
426c6a59 154 dest->options = calloc(sizeof(cups_option_t), parent->num_options);
2abf387c 155
426c6a59
MS
156 if (dest->options)
157 {
158 dest->num_options = parent->num_options;
ef416fc2 159
426c6a59
MS
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 }
2abf387c 169 }
170 }
171
426c6a59 172 return (num_dests);
ef416fc2 173}
174
175
176/*
177 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
178 */
179
180void
181cupsFreeDests(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 {
2e4ff8af
MS
193 _cupsStrFree(dest->name);
194 _cupsStrFree(dest->instance);
ef416fc2 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 *
5a738aea 206 * Use the @link cupsGetDests@ or @link cupsGetDests2@ functions to get a
ef416fc2 207 * list of supported destinations for the current user.
208 */
209
5a738aea
MS
210cups_dest_t * /* O - Destination pointer or @code NULL@ */
211cupsGetDest(const char *name, /* I - Destination name or @code NULL@ for the default destination */
212 const char *instance, /* I - Instance name or @code NULL@ */
ef416fc2 213 int num_dests, /* I - Number of destinations */
214 cups_dest_t *dests) /* I - Destinations */
215{
426c6a59
MS
216 int diff, /* Result of comparison */
217 match; /* Matching index */
ef416fc2 218
219
2abf387c 220 if (num_dests <= 0 || !dests)
ef416fc2 221 return (NULL);
222
2abf387c 223 if (!name)
ef416fc2 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
426c6a59 244 match = cups_find_dest(name, instance, num_dests, dests, -1, &diff);
ef416fc2 245
426c6a59
MS
246 if (!diff)
247 return (dests + match);
ef416fc2 248 }
249
250 return (NULL);
251}
252
253
254/*
255 * 'cupsGetDests()' - Get the list of destinations from the default server.
ecdc0628 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,
426c6a59
MS
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.
2abf387c 264 *
5a738aea
MS
265 * Use the @link cupsFreeDests@ function to free the destination list and
266 * the @link cupsGetDest@ function to find a particular destination.
ef416fc2 267 */
268
269int /* O - Number of destinations */
270cupsGetDests(cups_dest_t **dests) /* O - Destinations */
271{
3d052e43 272 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
ef416fc2 273}
274
275
276/*
277 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
278 *
ecdc0628 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,
426c6a59
MS
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.
ecdc0628 286 *
5a738aea
MS
287 * Use the @link cupsFreeDests@ function to free the destination list and
288 * the @link cupsGetDest@ function to find a particular destination.
2abf387c 289 *
426c6a59 290 * @since CUPS 1.1.21/Mac OS X 10.4@
ef416fc2 291 */
292
293int /* O - Number of destinations */
568fa3fa 294cupsGetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 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 */
b423cd4c 301 char filename[1024]; /* Local ~/.cups/lpoptions file */
ef416fc2 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 */
3d052e43 307 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 308
309
310 /*
311 * Range check the input...
312 */
313
3d052e43 314 if (!dests)
1ff0402e
MS
315 {
316 _cupsSetError(IPP_INTERNAL_ERROR, _("Bad NULL dests pointer"), 1);
ef416fc2 317 return (0);
1ff0402e 318 }
ef416fc2 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
a4924f6c 331 num_dests = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, num_dests, dests);
b19ccc9e
MS
332
333 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
334 {
335 cupsFreeDests(num_dests, *dests);
336 *dests = (cups_dest_t *)0;
337 return (0);
338 }
ef416fc2 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
080811b1
MS
364#ifdef __APPLE__
365 if ((defprinter = appleGetDefault(name, sizeof(name))) == NULL)
366#endif /* __APPLE__ */
367 defprinter = cupsGetDefault2(http);
368
369 if (defprinter)
ef416fc2 370 {
371 /*
372 * Grab printer and instance name...
373 */
374
080811b1
MS
375#ifdef __APPLE__
376 if (name != defprinter)
377#endif /* __APPLE__ */
ef416fc2 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 /*
b423cd4c 401 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
ef416fc2 402 */
403
404 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
a4924f6c 405 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 406
407 if ((home = getenv("HOME")) != NULL)
408 {
b423cd4c 409 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
410 if (access(filename, 0))
411 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
412
a4924f6c 413 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 414 }
415
416 /*
417 * Validate the current default destination - this prevents old
b423cd4c 418 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
ef416fc2 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
1ff0402e
MS
462 if (num_dests > 0)
463 _cupsSetError(IPP_OK, NULL, 0);
464
ef416fc2 465 return (num_dests);
466}
467
468
a4924f6c
MS
469/*
470 * 'cupsGetNamedDest()' - Get options for the named destination.
471 *
472 * This function is optimized for retrieving a single destination and should
5a738aea
MS
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.
a4924f6c 477 *
5a738aea
MS
478 * If "http" is @code CUPS_HTTP_DEFAULT@, the connection to the default print
479 * server will be used.
a4924f6c 480 *
5a738aea
MS
481 * If "name" is @code NULL@, the default printer for the current user will be
482 * returned.
a4924f6c 483 *
5a738aea
MS
484 * The returned destination must be freed using @link cupsFreeDests@ with a
485 * "num_dests" value of 1.
a4924f6c
MS
486 *
487 * @since CUPS 1.4@
488 */
489
5a738aea 490cups_dest_t * /* O - Destination or @code NULL@ */
568fa3fa
MS
491cupsGetNamedDest(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 */
5a738aea 493 const char *instance) /* I - Instance name or @code NULL@ */
a4924f6c
MS
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
a4924f6c
MS
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
f7deaa1a 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
5a738aea
MS
592 * @link cupsSetDests@ or @link cupsSetDests2@ functions to save the new
593 * options for the user.
f7deaa1a 594 *
426c6a59 595 * @since CUPS 1.3/Mac OS X 10.5@
f7deaa1a 596 */
597
598int /* O - New number of destinations */
599cupsRemoveDest(const char *name, /* I - Destination name */
5a738aea 600 const char *instance, /* I - Instance name or @code NULL@ */
f7deaa1a 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
2e4ff8af
MS
619 _cupsStrFree(dest->name);
620 _cupsStrFree(dest->instance);
f7deaa1a 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/*
3d052e43 639 * 'cupsSetDefaultDest()' - Set the default destination.
f7deaa1a 640 *
426c6a59 641 * @since CUPS 1.3/Mac OS X 10.5@
f7deaa1a 642 */
643
644void
645cupsSetDefaultDest(
646 const char *name, /* I - Destination name */
5a738aea 647 const char *instance, /* I - Instance name or @code NULL@ */
f7deaa1a 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
ef416fc2 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
b423cd4c 679 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 680 */
681
682void
683cupsSetDests(int num_dests, /* I - Number of destinations */
684 cups_dest_t *dests) /* I - Destinations */
685{
3d052e43 686 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
ef416fc2 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
b423cd4c 694 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 695 *
426c6a59 696 * @since CUPS 1.1.21/Mac OS X 10.4@
ef416fc2 697 */
698
699int /* O - 0 on success, -1 on error */
568fa3fa 700cupsSetDests2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 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 */
8ca02f3c 708 _ipp_option_t *match; /* Matching attribute for option */
ef416fc2 709 FILE *fp; /* File pointer */
b86bc4cf 710#ifndef WIN32
ef416fc2 711 const char *home; /* HOME environment variable */
b86bc4cf 712#endif /* WIN32 */
ef416fc2 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 */
3d052e43 718 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 719
720
721 /*
722 * Range check the input...
723 */
724
3d052e43 725 if (!num_dests || !dests)
ef416fc2 726 return (-1);
727
728 /*
729 * Get the server destinations...
730 */
731
a4924f6c 732 num_temps = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, 0, &temps);
426c6a59
MS
733
734 if (cupsLastError() >= IPP_REDIRECTION_OTHER_SITE)
735 {
736 cupsFreeDests(num_temps, temps);
737 return (-1);
738 }
ef416fc2 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
a4924f6c 753 num_temps = cups_get_dests(filename, NULL, NULL, num_temps, &temps);
ef416fc2 754
755 /*
756 * Point to user defaults...
757 */
758
759 if ((home = getenv("HOME")) != NULL)
b423cd4c 760 {
761 /*
762 * Remove the old ~/.lpoptions file...
763 */
764
ef416fc2 765 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
b423cd4c 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 }
ef416fc2 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
d6ae789d 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
ef416fc2 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 {
8ca02f3c 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
ef416fc2 835 /*
836 * See if the server/global options match these; if so, don't
837 * write 'em.
838 */
839
8ca02f3c 840 if (temp &&
841 (val = cupsGetOption(option->name, temp->num_options,
842 temp->options)) != NULL &&
843 !strcasecmp(val, option->value))
844 continue;
ef416fc2 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 {
8ca02f3c 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
ef416fc2 887 fprintf(fp, " %s=%s", option->name, option->value);
8ca02f3c 888 }
ef416fc2 889 }
890 else
891 fprintf(fp, " %s", option->name);
892 }
893
894 if (wrote)
895 fputs("\n", fp);
896 }
897
898 /*
fa73b229 899 * Free the temporary destinations and close the file...
ef416fc2 900 */
901
902 cupsFreeDests(num_temps, temps);
903
fa73b229 904 fclose(fp);
905
080811b1
MS
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
fa73b229 916#ifdef HAVE_NOTIFY_POST
ef416fc2 917 /*
fa73b229 918 * Send a notification so that MacOS X applications can know about the
919 * change, too.
ef416fc2 920 */
921
fa73b229 922 notify_post("com.apple.printerListChange");
923#endif /* HAVE_NOTIFY_POST */
ef416fc2 924
925 return (0);
926}
927
928
080811b1 929#ifdef __APPLE__
06d4e77b
MS
930/*
931 * 'appleCopyLocations()' - Copy the location history array.
932 */
933
934static CFArrayRef /* O - Location array or NULL */
935appleCopyLocations(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
749b1e90
MS
958/*
959 * 'appleCopyNetwork()' - Get the network ID for the current location.
960 */
961
962static CFStringRef /* O - Network ID */
963appleCopyNetwork(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
080811b1
MS
996/*
997 * 'appleGetDefault()' - Get the default printer for this location.
998 */
999
1000static char * /* O - Name or NULL if no default */
1001appleGetDefault(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
749b1e90 1024 if ((network = appleCopyNetwork()) == NULL)
080811b1
MS
1025 {
1026 DEBUG_puts("appleGetDefault: Unable to get current network...");
1027 return (NULL);
1028 }
1029
1030#ifdef DEBUG
1031 CFStringGetCString(network, name, namesize, kCFStringEncodingUTF8);
ae71f5de 1032 DEBUG_printf(("appleGetDefault: network=\"%s\"\n", name));
080811b1
MS
1033#endif /* DEBUG */
1034
1035 /*
1036 * Lookup the network in the preferences...
1037 */
1038
06d4e77b 1039 if ((locations = appleCopyLocations()) == NULL)
080811b1
MS
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
749b1e90
MS
1047 CFRelease(network);
1048
080811b1
MS
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
749b1e90 1060 CFRelease(network);
06d4e77b 1061 CFRelease(locations);
749b1e90 1062
080811b1
MS
1063 DEBUG_printf(("appleGetDefault: Returning \"%s\"...\n", name));
1064
1065 return (*name ? name : NULL);
1066}
1067
1068
426c6a59
MS
1069/*
1070 * 'appleGetPaperSize()' - Get the default paper size.
1071 */
1072
1073static char * /* O - Default paper size */
1074appleGetPaperSize(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 ||
557dde9f 1083 CFGetTypeID(defaultPaperID) != CFStringGetTypeID() ||
426c6a59
MS
1084 !CFStringGetCString(defaultPaperID, name, namesize,
1085 kCFStringEncodingUTF8))
1086 name[0] = '\0';
1087 else if (!strncmp(name, "na-", 3))
1088 _cups_strcpy(name, name + 3);
1089 else if (!strncmp(name, "iso-", 4))
1090 _cups_strcpy(name, name + 4);
1091
1092 if (defaultPaperID)
1093 CFRelease(defaultPaperID);
1094
1095 return (name);
1096}
1097
1098
080811b1
MS
1099/*
1100 * 'appleGetPrinter()' - Get a printer from the history array.
1101 */
1102
1103static CFStringRef /* O - Printer name or NULL */
1104appleGetPrinter(CFArrayRef locations, /* I - Location array */
1105 CFStringRef network, /* I - Network name */
1106 CFIndex *locindex) /* O - Index in array */
1107{
1108 CFIndex i, /* Looping var */
1109 count; /* Number of locations */
1110 CFDictionaryRef location; /* Current location */
1111 CFStringRef locnetwork, /* Current network */
1112 locprinter; /* Current printer */
1113
1114
1115 for (i = 0, count = CFArrayGetCount(locations); i < count; i ++)
1116 if ((location = CFArrayGetValueAtIndex(locations, i)) != NULL &&
1117 CFGetTypeID(location) == CFDictionaryGetTypeID())
1118 {
1119 if ((locnetwork = CFDictionaryGetValue(location,
1120 kLocationNetworkKey)) != NULL &&
1121 CFGetTypeID(locnetwork) == CFStringGetTypeID() &&
1122 CFStringCompare(network, locnetwork, 0) == kCFCompareEqualTo &&
1123 (locprinter = CFDictionaryGetValue(location,
1124 kLocationPrinterIDKey)) != NULL &&
1125 CFGetTypeID(locprinter) == CFStringGetTypeID())
1126 {
1127 if (locindex)
1128 *locindex = i;
1129
1130 return (locprinter);
1131 }
1132 }
1133
1134 return (NULL);
1135}
1136
1137
1138/*
1139 * 'appleSetDefault()' - Set the default printer for this location.
1140 */
1141
1142static void
1143appleSetDefault(const char *name) /* I - Default printer/class name */
1144{
1145 CFStringRef network; /* Current network */
1146 CFArrayRef locations; /* Old locations array */
1147 CFIndex locindex; /* Index in locations array */
1148 CFStringRef locprinter; /* Current printer */
1149 CFMutableArrayRef newlocations; /* New locations array */
1150 CFMutableDictionaryRef newlocation; /* New location */
1151 CFStringRef newprinter; /* New printer */
1152
1153
1154 /*
1155 * Get the current location...
1156 */
1157
749b1e90 1158 if ((network = appleCopyNetwork()) == NULL)
080811b1
MS
1159 {
1160 DEBUG_puts("appleSetDefault: Unable to get current network...");
1161 return;
1162 }
1163
080811b1
MS
1164 if ((newprinter = CFStringCreateWithCString(kCFAllocatorDefault, name,
1165 kCFStringEncodingUTF8)) == NULL)
1166 {
1167 CFRelease(network);
1168 return;
1169 }
1170
1171 /*
1172 * Lookup the network in the preferences...
1173 */
1174
06d4e77b 1175 if ((locations = appleCopyLocations()) != NULL)
080811b1
MS
1176 locprinter = appleGetPrinter(locations, network, &locindex);
1177 else
1178 {
1179 locprinter = NULL;
1180 locindex = -1;
1181 }
1182
1183 if (!locprinter ||
1184 CFStringCompare(locprinter, newprinter, 0) != kCFCompareEqualTo)
1185 {
1186 /*
1187 * Need to change the locations array...
1188 */
1189
1190 if (locations)
1191 {
1192 newlocations = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0,
1193 locations);
1194
1195 if (locprinter)
1196 CFArrayRemoveValueAtIndex(newlocations, locindex);
1197 }
1198 else
1199 newlocations = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
1200
1201 newlocation = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1202 &kCFTypeDictionaryKeyCallBacks,
1203 &kCFTypeDictionaryValueCallBacks);
1204
1205 if (newlocation && newlocations)
1206 {
1207 /*
1208 * Put the new location at the front of the array...
1209 */
1210
1211 CFDictionaryAddValue(newlocation, kLocationNetworkKey, network);
1212 CFDictionaryAddValue(newlocation, kLocationPrinterIDKey, newprinter);
1213 CFArrayInsertValueAtIndex(newlocations, 0, newlocation);
1214
1215 /*
1216 * Limit the number of locations to 10...
1217 */
1218
1219 while (CFArrayGetCount(newlocations) > 10)
1220 CFArrayRemoveValueAtIndex(newlocations, 10);
1221
1222 /*
1223 * Push the changes out...
1224 */
1225
1226 CFPreferencesSetAppValue(kLocationHistoryArrayKey, newlocations,
1227 kPMPrintingPreferences);
1228 CFPreferencesAppSynchronize(kPMPrintingPreferences);
1229 }
1230
1231 if (newlocations)
1232 CFRelease(newlocations);
1233
1234 if (newlocation)
1235 CFRelease(newlocation);
1236 }
91c84a35 1237
06d4e77b
MS
1238 if (locations)
1239 CFRelease(locations);
1240
749b1e90 1241 CFRelease(network);
080811b1
MS
1242 CFRelease(newprinter);
1243}
1244
1245
1246/*
1247 * 'appleUseLastPrinter()' - Get the default printer preference value.
1248 */
1249
1250static int /* O - 1 to use last printer, 0 otherwise */
1251appleUseLastPrinter(void)
1252{
1253 CFPropertyListRef uselast; /* Use last printer preference value */
1254
1255
1256 if ((uselast = CFPreferencesCopyAppValue(kUseLastPrinterAsCurrentPrinterKey,
1257 kPMPrintingPreferences)) != NULL)
1258 {
1259 CFRelease(uselast);
1260
1261 if (uselast == kCFBooleanFalse)
1262 return (0);
1263 }
1264
1265 return (1);
1266}
1267#endif /* __APPLE__ */
1268
1269
426c6a59
MS
1270/*
1271 * 'cups_add_dest()' - Add a destination to the array.
1272 *
1273 * Unlike cupsAddDest(), this function does not check for duplicates.
1274 */
1275
1276static cups_dest_t * /* O - New destination */
1277cups_add_dest(const char *name, /* I - Name of destination */
1278 const char *instance, /* I - Instance or NULL */
1279 int *num_dests, /* IO - Number of destinations */
1280 cups_dest_t **dests) /* IO - Destinations */
1281{
1282 int insert, /* Insertion point */
1283 diff; /* Result of comparison */
1284 cups_dest_t *dest; /* Destination pointer */
1285
1286
1287 /*
1288 * Add new destination...
1289 */
1290
1291 if (*num_dests == 0)
1292 dest = malloc(sizeof(cups_dest_t));
1293 else
1294 dest = realloc(*dests, sizeof(cups_dest_t) * (*num_dests + 1));
1295
1296 if (!dest)
1297 return (NULL);
1298
1299 *dests = dest;
1300
1301 /*
1302 * Find where to insert the destination...
1303 */
1304
1305 if (*num_dests == 0)
1306 insert = 0;
1307 else
1308 {
1309 insert = cups_find_dest(name, instance, *num_dests, *dests, *num_dests - 1,
1310 &diff);
1311
1312 if (diff > 0)
1313 insert ++;
1314 }
1315
1316 /*
1317 * Move the array elements as needed...
1318 */
1319
1320 if (insert < *num_dests)
1321 memmove(*dests + insert + 1, *dests + insert,
1322 (*num_dests - insert) * sizeof(cups_dest_t));
1323
1324 (*num_dests) ++;
1325
1326 /*
1327 * Initialize the destination...
1328 */
1329
1330 dest = *dests + insert;
1331 dest->name = _cupsStrAlloc(name);
1332 dest->instance = _cupsStrAlloc(instance);
1333 dest->is_default = 0;
1334 dest->num_options = 0;
1335 dest->options = (cups_option_t *)0;
1336
1337 return (dest);
1338}
1339
1340
1341/*
1342 * 'cups_compare_dests()' - Compare two destinations.
1343 */
1344
1345static int /* O - Result of comparison */
1346cups_compare_dests(cups_dest_t *a, /* I - First destination */
1347 cups_dest_t *b) /* I - Second destination */
1348{
1349 int diff; /* Difference */
1350
1351
1352 if ((diff = strcasecmp(a->name, b->name)) != 0)
1353 return (diff);
1354 else if (a->instance && b->instance)
1355 return (strcasecmp(a->instance, b->instance));
1356 else
1357 return ((a->instance && !b->instance) - (!a->instance && b->instance));
1358}
1359
1360
1361/*
1362 * 'cups_find_dest()' - Find a destination using a binary search.
1363 */
1364
1365static int /* O - Index of match */
1366cups_find_dest(const char *name, /* I - Destination name */
1367 const char *instance, /* I - Instance or NULL */
1368 int num_dests, /* I - Number of destinations */
1369 cups_dest_t *dests, /* I - Destinations */
1370 int prev, /* I - Previous index */
1371 int *rdiff) /* O - Difference of match */
1372{
1373 int left, /* Low mark for binary search */
1374 right, /* High mark for binary search */
1375 current, /* Current index */
1376 diff; /* Result of comparison */
1377 cups_dest_t key; /* Search key */
1378
1379
1380 key.name = (char *)name;
1381 key.instance = (char *)instance;
1382
1383 if (prev >= 0)
1384 {
1385 /*
1386 * Start search on either side of previous...
1387 */
1388
1389 if ((diff = cups_compare_dests(&key, dests + prev)) == 0 ||
1390 (diff < 0 && prev == 0) ||
1391 (diff > 0 && prev == (num_dests - 1)))
1392 {
1393 *rdiff = diff;
1394 return (prev);
1395 }
1396 else if (diff < 0)
1397 {
1398 /*
1399 * Start with previous on right side...
1400 */
1401
1402 left = 0;
1403 right = prev;
1404 }
1405 else
1406 {
1407 /*
1408 * Start wih previous on left side...
1409 */
1410
1411 left = prev;
1412 right = num_dests - 1;
1413 }
1414 }
1415 else
1416 {
1417 /*
1418 * Start search in the middle...
1419 */
1420
1421 left = 0;
1422 right = num_dests - 1;
1423 }
1424
1425 do
1426 {
1427 current = (left + right) / 2;
1428 diff = cups_compare_dests(&key, dests + current);
1429
1430 if (diff == 0)
1431 break;
1432 else if (diff < 0)
1433 right = current;
1434 else
1435 left = current;
1436 }
1437 while ((right - left) > 1);
1438
1439 if (diff != 0)
1440 {
1441 /*
1442 * Check the last 1 or 2 elements...
1443 */
1444
1445 if ((diff = cups_compare_dests(&key, dests + left)) <= 0)
1446 current = left;
1447 else
1448 {
1449 diff = cups_compare_dests(&key, dests + right);
1450 current = right;
1451 }
1452 }
1453
1454 /*
1455 * Return the closest destination and the difference...
1456 */
1457
1458 *rdiff = diff;
1459
1460 return (current);
1461}
1462
1463
a4924f6c
MS
1464/*
1465 * 'cups_get_default()' - Get the default destination from an lpoptions file.
1466 */
1467
080811b1 1468static char * /* O - Default destination or NULL */
a4924f6c
MS
1469cups_get_default(const char *filename, /* I - File to read */
1470 char *namebuf, /* I - Name buffer */
1471 size_t namesize, /* I - Size of name buffer */
1472 const char **instance) /* I - Instance */
1473{
1474 cups_file_t *fp; /* lpoptions file */
1475 char line[8192], /* Line from file */
1476 *value, /* Value for line */
1477 *nameptr; /* Pointer into name */
1478 int linenum; /* Current line */
1479
1480
1481 *namebuf = '\0';
1482
1483 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1484 {
1485 linenum = 0;
1486
1487 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1488 {
1489 if (!strcasecmp(line, "default") && value)
1490 {
1491 strlcpy(namebuf, value, namesize);
1492
1493 if ((nameptr = strchr(namebuf, ' ')) != NULL)
1494 *nameptr = '\0';
1495 if ((nameptr = strchr(namebuf, '\t')) != NULL)
1496 *nameptr = '\0';
1497
1498 if ((nameptr = strchr(namebuf, '/')) != NULL)
1499 *nameptr++ = '\0';
1500
1501 *instance = nameptr;
1502 break;
1503 }
1504 }
1505
1506 cupsFileClose(fp);
1507 }
1508
1509 return (*namebuf ? namebuf : NULL);
1510}
1511
1512
ef416fc2 1513/*
1514 * 'cups_get_dests()' - Get destinations from a file.
1515 */
1516
1517static int /* O - Number of destinations */
1518cups_get_dests(const char *filename, /* I - File to read from */
a4924f6c
MS
1519 const char *match_name, /* I - Destination name we want */
1520 const char *match_inst, /* I - Instance name we want */
ef416fc2 1521 int num_dests, /* I - Number of destinations */
1522 cups_dest_t **dests) /* IO - Destinations */
1523{
1524 int i; /* Looping var */
1525 cups_dest_t *dest; /* Current destination */
a4924f6c 1526 cups_file_t *fp; /* File pointer */
ef416fc2 1527 char line[8192], /* Line from file */
1528 *lineptr, /* Pointer into line */
1529 *name, /* Name of destination/option */
1530 *instance; /* Instance of destination */
a4924f6c 1531 int linenum; /* Current line number */
ef416fc2 1532 const char *printer; /* PRINTER or LPDEST */
1533
1534
a4924f6c
MS
1535 DEBUG_printf(("cups_get_dests(filename=\"%s\", match_name=\"%s\", "
1536 "match_inst=\"%s\", num_dests=%d, dests=%p)\n", filename,
1537 match_name ? match_name : "(null)",
1538 match_inst ? match_inst : "(null)", num_dests, dests));
1539
1540 /*
1541 * Try to open the file...
1542 */
1543
1544 if ((fp = cupsFileOpen(filename, "r")) == NULL)
1545 return (num_dests);
1546
ef416fc2 1547 /*
1548 * Check environment variables...
1549 */
1550
1551 if ((printer = getenv("LPDEST")) == NULL)
1552 if ((printer = getenv("PRINTER")) != NULL)
1553 if (strcmp(printer, "lp") == 0)
1554 printer = NULL;
1555
a4924f6c
MS
1556 DEBUG_printf(("cups_get_dests: printer=\"%s\"\n",
1557 printer ? printer : "(null)"));
ef416fc2 1558
1559 /*
1560 * Read each printer; each line looks like:
1561 *
1562 * Dest name[/instance] options
1563 * Default name[/instance] options
1564 */
1565
a4924f6c
MS
1566 linenum = 0;
1567
1568 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
ef416fc2 1569 {
1570 /*
1571 * See what type of line it is...
1572 */
1573
a4924f6c
MS
1574 DEBUG_printf(("cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"\n",
1575 linenum, line, lineptr ? lineptr : "(null)"));
ef416fc2 1576
a4924f6c
MS
1577 if ((strcasecmp(line, "dest") && strcasecmp(line, "default")) || !lineptr)
1578 {
1579 DEBUG_puts("cups_get_dests: Not a dest or default line...");
ef416fc2 1580 continue;
a4924f6c 1581 }
ef416fc2 1582
1583 name = lineptr;
1584
1585 /*
1586 * Search for an instance...
1587 */
1588
1589 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1590 lineptr ++;
1591
ef416fc2 1592 if (*lineptr == '/')
1593 {
1594 /*
1595 * Found an instance...
1596 */
1597
1598 *lineptr++ = '\0';
1599 instance = lineptr;
1600
1601 /*
1602 * Search for an instance...
1603 */
1604
1605 while (!isspace(*lineptr & 255) && *lineptr)
1606 lineptr ++;
1607 }
1608 else
1609 instance = NULL;
1610
a4924f6c
MS
1611 if (*lineptr)
1612 *lineptr++ = '\0';
1613
1614 DEBUG_printf(("cups_get_dests: name=\"%s\", instance=\"%s\"\n", name,
1615 instance));
ef416fc2 1616
1617 /*
1618 * See if the primary instance of the destination exists; if not,
1619 * ignore this entry and move on...
1620 */
1621
a4924f6c
MS
1622 if (match_name)
1623 {
1624 if (strcasecmp(name, match_name) ||
1625 (!instance && match_inst) ||
1626 (instance && !match_inst) ||
1627 (instance && strcasecmp(instance, match_inst)))
1628 continue;
ef416fc2 1629
a4924f6c
MS
1630 dest = *dests;
1631 }
1632 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
1633 {
1634 DEBUG_puts("cups_get_dests: Not found!");
1635 continue;
1636 }
1637 else
ef416fc2 1638 {
1639 /*
a4924f6c 1640 * Add the destination...
ef416fc2 1641 */
1642
a4924f6c
MS
1643 num_dests = cupsAddDest(name, instance, num_dests, dests);
1644
1645 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1646 {
1647 /*
1648 * Out of memory!
1649 */
1650
1651 DEBUG_puts("cups_get_dests: Out of memory!");
1652 break;
1653 }
ef416fc2 1654 }
1655
1656 /*
1657 * Add options until we hit the end of the line...
1658 */
1659
1660 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
1661 &(dest->options));
1662
a4924f6c
MS
1663 /*
1664 * If we found what we were looking for, stop now...
1665 */
1666
1667 if (match_name)
1668 break;
1669
ef416fc2 1670 /*
1671 * Set this as default if needed...
1672 */
1673
a4924f6c 1674 if (!printer && !strcasecmp(line, "default"))
ef416fc2 1675 {
a4924f6c
MS
1676 DEBUG_puts("cups_get_dests: Setting as default...");
1677
ef416fc2 1678 for (i = 0; i < num_dests; i ++)
1679 (*dests)[i].is_default = 0;
1680
1681 dest->is_default = 1;
1682 }
1683 }
1684
1685 /*
1686 * Close the file and return...
1687 */
1688
a4924f6c 1689 cupsFileClose(fp);
ef416fc2 1690
1691 return (num_dests);
1692}
1693
1694
1695/*
1696 * 'cups_get_sdests()' - Get destinations from a server.
1697 */
1698
1699static int /* O - Number of destinations */
568fa3fa 1700cups_get_sdests(http_t *http, /* I - Connection to server or CUPS_HTTP_DEFAULT */
a4924f6c
MS
1701 ipp_op_t op, /* I - IPP operation */
1702 const char *name, /* I - Name of destination */
ef416fc2 1703 int num_dests, /* I - Number of destinations */
1704 cups_dest_t **dests) /* IO - Destinations */
1705{
fa73b229 1706 int i; /* Looping var */
ef416fc2 1707 cups_dest_t *dest; /* Current destination */
1708 ipp_t *request, /* IPP Request */
1709 *response; /* IPP Response */
1710 ipp_attribute_t *attr; /* Current attribute */
426c6a59
MS
1711 const char *printer_name; /* printer-name attribute */
1712 char uri[1024]; /* printer-uri value */
f7deaa1a 1713 int num_options; /* Number of options */
1714 cups_option_t *options; /* Options */
426c6a59
MS
1715#ifdef __APPLE__
1716 char media_default[41]; /* Default paper size */
1717#endif /* __APPLE__ */
f7deaa1a 1718 char optname[1024], /* Option name */
1719 value[2048], /* Option value */
1720 *ptr; /* Pointer into name/value */
ef416fc2 1721 static const char * const pattrs[] = /* Attributes we're interested in */
1722 {
f42414bf 1723 "auth-info-required",
fa73b229 1724 "job-sheets-default",
426c6a59
MS
1725 "marker-change-time",
1726 "marker-colors",
1727 "marker-high-levels",
1728 "marker-levels",
1729 "marker-low-levels",
1730 "marker-message",
1731 "marker-names",
1732 "marker-types",
1733#ifdef __APPLE__
1734 "media-supported",
1735#endif /* __APPLE__ */
1736 "printer-commands",
fa73b229 1737 "printer-info",
1738 "printer-is-accepting-jobs",
1739 "printer-is-shared",
f42414bf 1740 "printer-location",
fa73b229 1741 "printer-make-and-model",
ef416fc2 1742 "printer-name",
fa73b229 1743 "printer-state",
1744 "printer-state-change-time",
1745 "printer-state-reasons",
f7deaa1a 1746 "printer-type",
1747 "printer-defaults"
ef416fc2 1748 };
1749
1750
426c6a59
MS
1751#ifdef __APPLE__
1752 /*
1753 * Get the default paper size...
1754 */
1755
1756 appleGetPaperSize(media_default, sizeof(media_default));
1757#endif /* __APPLE__ */
1758
ef416fc2 1759 /*
426c6a59
MS
1760 * Build a CUPS_GET_PRINTERS or IPP_GET_PRINTER_ATTRIBUTES request, which
1761 * require the following attributes:
ef416fc2 1762 *
1763 * attributes-charset
1764 * attributes-natural-language
fa73b229 1765 * requesting-user-name
426c6a59 1766 * printer-uri [for IPP_GET_PRINTER_ATTRIBUTES]
ef416fc2 1767 */
1768
fa73b229 1769 request = ippNewRequest(op);
ef416fc2 1770
1771 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1772 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1773 NULL, pattrs);
1774
fa73b229 1775 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1776 "requesting-user-name", NULL, cupsUser());
1777
a4924f6c
MS
1778 if (name)
1779 {
1780 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1781 "localhost", ippPort(), "/printers/%s", name);
1782 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1783 uri);
1784 }
1785
ef416fc2 1786 /*
1787 * Do the request and get back a response...
1788 */
1789
1790 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1791 {
1792 for (attr = response->attrs; attr != NULL; attr = attr->next)
1793 {
1794 /*
1795 * Skip leading attributes until we hit a printer...
1796 */
1797
1798 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1799 attr = attr->next;
1800
1801 if (attr == NULL)
1802 break;
1803
1804 /*
f7deaa1a 1805 * Pull the needed attributes from this printer...
ef416fc2 1806 */
1807
a4924f6c
MS
1808 printer_name = NULL;
1809 num_options = 0;
1810 options = NULL;
ef416fc2 1811
426c6a59 1812 for (; attr && attr->group_tag == IPP_TAG_PRINTER; attr = attr->next)
ef416fc2 1813 {
426c6a59
MS
1814 if (attr->value_tag != IPP_TAG_INTEGER &&
1815 attr->value_tag != IPP_TAG_ENUM &&
1816 attr->value_tag != IPP_TAG_BOOLEAN &&
1817 attr->value_tag != IPP_TAG_TEXT &&
1818 attr->value_tag != IPP_TAG_TEXTLANG &&
1819 attr->value_tag != IPP_TAG_NAME &&
1820 attr->value_tag != IPP_TAG_NAMELANG &&
1821 attr->value_tag != IPP_TAG_KEYWORD &&
1822 attr->value_tag != IPP_TAG_RANGE)
1823 continue;
f42414bf 1824
426c6a59
MS
1825 if (!strcmp(attr->name, "auth-info-required") ||
1826 !strcmp(attr->name, "marker-change-time") ||
1827 !strcmp(attr->name, "marker-colors") ||
1828 !strcmp(attr->name, "marker-high-levels") ||
1829 !strcmp(attr->name, "marker-levels") ||
1830 !strcmp(attr->name, "marker-low-levels") ||
1831 !strcmp(attr->name, "marker-message") ||
1832 !strcmp(attr->name, "marker-names") ||
1833 !strcmp(attr->name, "marker-types") ||
1834 !strcmp(attr->name, "printer-commands") ||
1835 !strcmp(attr->name, "printer-info") ||
1836 !strcmp(attr->name, "printer-is-shared") ||
1837 !strcmp(attr->name, "printer-make-and-model") ||
1838 !strcmp(attr->name, "printer-state") ||
1839 !strcmp(attr->name, "printer-state-change-time") ||
1840 !strcmp(attr->name, "printer-type") ||
1841 !strcmp(attr->name, "printer-is-accepting-jobs") ||
1842 !strcmp(attr->name, "printer-location") ||
1843 !strcmp(attr->name, "printer-state-reasons"))
ef416fc2 1844 {
426c6a59
MS
1845 /*
1846 * Add a printer description attribute...
1847 */
1848
1849 num_options = cupsAddOption(attr->name,
1850 cups_make_string(attr, value,
1851 sizeof(value)),
1852 num_options, &options);
1853 }
557dde9f 1854#ifdef __APPLE__
426c6a59
MS
1855 else if (!strcmp(attr->name, "media-supported"))
1856 {
1857 /*
1858 * See if we can set a default media size...
1859 */
1860
1861 for (i = 0; i < attr->num_values; i ++)
1862 if (!strcasecmp(media_default, attr->values[i].string.text))
1863 {
1864 num_options = cupsAddOption("media", media_default, num_options,
1865 &options);
1866 break;
1867 }
1868 }
557dde9f 1869#endif /* __APPLE__ */
fa73b229 1870 else if (!strcmp(attr->name, "printer-name") &&
1871 attr->value_tag == IPP_TAG_NAME)
a4924f6c 1872 printer_name = attr->values[0].string.text;
f7deaa1a 1873 else if (strncmp(attr->name, "notify-", 7) &&
1874 (attr->value_tag == IPP_TAG_BOOLEAN ||
1875 attr->value_tag == IPP_TAG_ENUM ||
1876 attr->value_tag == IPP_TAG_INTEGER ||
1877 attr->value_tag == IPP_TAG_KEYWORD ||
1878 attr->value_tag == IPP_TAG_NAME ||
1879 attr->value_tag == IPP_TAG_RANGE) &&
426c6a59 1880 (ptr = strstr(attr->name, "-default")) != NULL)
f7deaa1a 1881 {
f7deaa1a 1882 /*
1883 * Add a default option...
1884 */
1885
1886 strlcpy(optname, attr->name, sizeof(optname));
426c6a59
MS
1887 optname[ptr - attr->name] = '\0';
1888
1889 if (strcasecmp(optname, "media") ||
1890 !cupsGetOption("media", num_options, options))
1891 num_options = cupsAddOption(optname,
1892 cups_make_string(attr, value,
1893 sizeof(value)),
1894 num_options, &options);
f7deaa1a 1895 }
ef416fc2 1896 }
1897
1898 /*
1899 * See if we have everything needed...
1900 */
1901
a4924f6c 1902 if (!printer_name)
ef416fc2 1903 {
f7deaa1a 1904 cupsFreeOptions(num_options, options);
1905
ef416fc2 1906 if (attr == NULL)
1907 break;
1908 else
1909 continue;
1910 }
1911
426c6a59 1912 if ((dest = cups_add_dest(printer_name, NULL, &num_dests, dests)) != NULL)
fa73b229 1913 {
f7deaa1a 1914 dest->num_options = num_options;
1915 dest->options = options;
fa73b229 1916 }
426c6a59
MS
1917 else
1918 cupsFreeOptions(num_options, options);
f7deaa1a 1919
ef416fc2 1920 if (attr == NULL)
1921 break;
1922 }
1923
1924 ippDelete(response);
1925 }
1926
1927 /*
1928 * Return the count...
1929 */
1930
1931 return (num_dests);
1932}
1933
1934
426c6a59
MS
1935/*
1936 * 'cups_make_string()' - Make a comma-separated string of values from an IPP
1937 * attribute.
1938 */
1939
1940static char * /* O - New string */
1941cups_make_string(
1942 ipp_attribute_t *attr, /* I - Attribute to convert */
1943 char *buffer, /* I - Buffer */
1944 size_t bufsize) /* I - Size of buffer */
1945{
1946 int i; /* Looping var */
1947 char *ptr, /* Pointer into buffer */
1948 *end, /* Pointer to end of buffer */
1949 *valptr; /* Pointer into string attribute */
1950
1951
1952 /*
1953 * Return quickly if we have a single string value...
1954 */
1955
1956 if (attr->num_values == 1 &&
1957 attr->value_tag != IPP_TAG_INTEGER &&
1958 attr->value_tag != IPP_TAG_ENUM &&
1959 attr->value_tag != IPP_TAG_BOOLEAN &&
1960 attr->value_tag != IPP_TAG_RANGE)
1961 return (attr->values[0].string.text);
1962
1963 /*
1964 * Copy the values to the string, separating with commas and escaping strings
1965 * as needed...
1966 */
1967
1968 end = buffer + bufsize - 1;
1969
1970 for (i = 0, ptr = buffer; i < attr->num_values && ptr < end; i ++)
1971 {
1972 if (i)
1973 *ptr++ = ',';
1974
1975 switch (attr->value_tag)
1976 {
1977 case IPP_TAG_INTEGER :
1978 case IPP_TAG_ENUM :
1979 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].integer);
1980 break;
1981
1982 case IPP_TAG_BOOLEAN :
1983 if (attr->values[i].boolean)
1984 strlcpy(ptr, "true", end - ptr + 1);
1985 else
1986 strlcpy(ptr, "false", end - ptr + 1);
1987 break;
1988
1989 case IPP_TAG_RANGE :
1990 if (attr->values[i].range.lower == attr->values[i].range.upper)
1991 snprintf(ptr, end - ptr + 1, "%d", attr->values[i].range.lower);
1992 else
1993 snprintf(ptr, end - ptr + 1, "%d-%d", attr->values[i].range.lower,
1994 attr->values[i].range.upper);
1995 break;
1996
1997 default :
1998 for (valptr = attr->values[i].string.text;
1999 *valptr && ptr < end;)
2000 {
2001 if (strchr(" \t\n\\\'\"", *valptr))
2002 {
2003 if (ptr >= (end - 1))
2004 break;
2005
2006 *ptr++ = '\\';
2007 }
2008
2009 *ptr++ = *valptr++;
2010 }
2011
2012 *ptr = '\0';
2013 break;
2014 }
2015
2016 ptr += strlen(ptr);
2017 }
2018
2019 *ptr = '\0';
2020
2021 return (buffer);
2022}
2023
2024
ef416fc2 2025/*
b19ccc9e 2026 * End of "$Id: dest.c 7946 2008-09-16 23:27:54Z mike $".
ef416fc2 2027 */