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