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