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