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