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