]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest.c
Import CUPS 1.4svn-r7170.
[thirdparty/cups.git] / cups / dest.c
CommitLineData
ef416fc2 1/*
2e4ff8af 2 * "$Id: dest.c 6943 2007-09-10 23:00:33Z mike $"
ef416fc2 3 *
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
6 *
bc44d920 7 * Copyright 2007 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 *
f7deaa1a 20 * cupsAddDest() - Add a destination to the list of destinations.
21 * cupsFreeDests() - Free the memory used by the list of
22 * destinations.
23 * cupsGetDest() - Get the named destination from the list.
24 * cupsGetDests() - Get the list of destinations from the default
25 * server.
26 * cupsGetDests2() - Get the list of destinations from the
27 * specified server.
a4924f6c 28 * cupsGetNamedDest() - Get options for the named destination.
f7deaa1a 29 * cupsRemoveDest() - Remove a destination from the destination list.
30 * cupsDestSetDefaultDest() - Set the default destination.
31 * cupsSetDests() - Set the list of destinations for the default
32 * server.
33 * cupsSetDests2() - Set the list of destinations for the specified
34 * server.
35 * cups_get_dests() - Get destinations from a file.
36 * cups_get_sdests() - Get destinations from a server.
ef416fc2 37 */
38
39/*
40 * Include necessary headers...
41 */
42
a4924f6c 43#include "debug.h"
ef416fc2 44#include "globals.h"
45#include <stdlib.h>
46#include <ctype.h>
b423cd4c 47#include <sys/stat.h>
ef416fc2 48
fa73b229 49#ifdef HAVE_NOTIFY_H
50# include <notify.h>
51#endif /* HAVE_NOTIFY_H */
52
ef416fc2 53
54/*
55 * Local functions...
56 */
57
a4924f6c
MS
58static const char *cups_get_default(const char *filename, char *namebuf,
59 size_t namesize, const char **instance);
60static int cups_get_dests(const char *filename, const char *match_name,
61 const char *match_inst, int num_dests,
ef416fc2 62 cups_dest_t **dests);
a4924f6c
MS
63static int cups_get_sdests(http_t *http, ipp_op_t op, const char *name,
64 int num_dests, cups_dest_t **dests);
ef416fc2 65
66
67/*
68 * 'cupsAddDest()' - Add a destination to the list of destinations.
69 *
2abf387c 70 * This function cannot be used to add a new class or printer queue,
71 * it only adds a new container of saved options for the named
72 * destination or instance.
73 *
74 * If the named destination already exists, the destination list is
75 * returned unchanged. Adding a new instance of a destination creates
76 * a copy of that destination's options.
77 *
78 * Use the cupsSaveDests() function to save the updated list of
79 * destinations to the user's lpoptions file.
ef416fc2 80 */
81
82int /* O - New number of destinations */
2abf387c 83cupsAddDest(const char *name, /* I - Destination name */
84 const char *instance, /* I - Instance name or NULL for none/primary */
ef416fc2 85 int num_dests, /* I - Number of destinations */
86 cups_dest_t **dests) /* IO - Destinations */
87{
88 int i; /* Looping var */
89 cups_dest_t *dest; /* Destination pointer */
2abf387c 90 cups_dest_t *parent; /* Parent destination */
91 cups_option_t *option; /* Current option */
ef416fc2 92
93
2abf387c 94 if (!name || !dests)
ef416fc2 95 return (0);
96
97 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
98 return (num_dests);
99
100 /*
101 * Add new destination...
102 */
103
104 if (num_dests == 0)
105 dest = malloc(sizeof(cups_dest_t));
106 else
107 dest = realloc(*dests, sizeof(cups_dest_t) * (num_dests + 1));
108
109 if (dest == NULL)
110 return (num_dests);
111
112 *dests = dest;
113
2abf387c 114 /*
115 * Find where to insert the destination...
116 */
117
ef416fc2 118 for (i = num_dests; i > 0; i --, dest ++)
119 if (strcasecmp(name, dest->name) < 0)
120 break;
2abf387c 121 else if (!instance && dest->instance)
122 break;
b423cd4c 123 else if (!strcasecmp(name, dest->name) &&
2abf387c 124 instance && dest->instance &&
ef416fc2 125 strcasecmp(instance, dest->instance) < 0)
126 break;
127
128 if (i > 0)
129 memmove(dest + 1, dest, i * sizeof(cups_dest_t));
130
2abf387c 131 /*
132 * Initialize the destination...
133 */
134
2e4ff8af 135 dest->name = _cupsStrAlloc(name);
ef416fc2 136 dest->is_default = 0;
137 dest->num_options = 0;
138 dest->options = (cups_option_t *)0;
139
2abf387c 140 if (!instance)
ef416fc2 141 dest->instance = NULL;
142 else
2abf387c 143 {
144 /*
145 * Copy options from the primary instance...
146 */
147
2e4ff8af 148 dest->instance = _cupsStrAlloc(instance);
ef416fc2 149
2abf387c 150 if ((parent = cupsGetDest(name, NULL, num_dests + 1, *dests)) != NULL)
151 {
152 for (i = parent->num_options, option = parent->options;
153 i > 0;
154 i --, option ++)
155 dest->num_options = cupsAddOption(option->name, option->value,
156 dest->num_options,
157 &(dest->options));
158 }
159 }
160
ef416fc2 161 return (num_dests + 1);
162}
163
164
165/*
166 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
167 */
168
169void
170cupsFreeDests(int num_dests, /* I - Number of destinations */
171 cups_dest_t *dests) /* I - Destinations */
172{
173 int i; /* Looping var */
174 cups_dest_t *dest; /* Current destination */
175
176
177 if (num_dests == 0 || dests == NULL)
178 return;
179
180 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
181 {
2e4ff8af
MS
182 _cupsStrFree(dest->name);
183 _cupsStrFree(dest->instance);
ef416fc2 184
185 cupsFreeOptions(dest->num_options, dest->options);
186 }
187
188 free(dests);
189}
190
191
192/*
193 * 'cupsGetDest()' - Get the named destination from the list.
194 *
195 * Use the cupsGetDests() or cupsGetDests2() functions to get a
196 * list of supported destinations for the current user.
197 */
198
199cups_dest_t * /* O - Destination pointer or NULL */
2abf387c 200cupsGetDest(const char *name, /* I - Destination name or NULL for the default destination */
201 const char *instance, /* I - Instance name or NULL */
ef416fc2 202 int num_dests, /* I - Number of destinations */
203 cups_dest_t *dests) /* I - Destinations */
204{
205 int comp; /* Result of comparison */
206
207
2abf387c 208 if (num_dests <= 0 || !dests)
ef416fc2 209 return (NULL);
210
2abf387c 211 if (!name)
ef416fc2 212 {
213 /*
214 * NULL name for default printer.
215 */
216
217 while (num_dests > 0)
218 {
219 if (dests->is_default)
220 return (dests);
221
222 num_dests --;
223 dests ++;
224 }
225 }
226 else
227 {
228 /*
229 * Lookup name and optionally the instance...
230 */
231
232 while (num_dests > 0)
233 {
234 if ((comp = strcasecmp(name, dests->name)) < 0)
235 return (NULL);
236 else if (comp == 0)
237 {
2abf387c 238 if ((!instance && !dests->instance) ||
ef416fc2 239 (instance != NULL && dests->instance != NULL &&
2abf387c 240 !strcasecmp(instance, dests->instance)))
ef416fc2 241 return (dests);
242 }
243
244 num_dests --;
245 dests ++;
246 }
247 }
248
249 return (NULL);
250}
251
252
253/*
254 * 'cupsGetDests()' - Get the list of destinations from the default server.
ecdc0628 255 *
256 * Starting with CUPS 1.2, the returned list of destinations include the
257 * printer-info, printer-is-accepting-jobs, printer-is-shared,
258 * printer-make-and-model, printer-state, printer-state-change-time,
259 * printer-state-reasons, and printer-type attributes as options.
2abf387c 260 *
261 * Use the cupsFreeDests() function to free the destination list and
262 * the cupsGetDest() function to find a particular destination.
ef416fc2 263 */
264
265int /* O - Number of destinations */
266cupsGetDests(cups_dest_t **dests) /* O - Destinations */
267{
3d052e43 268 return (cupsGetDests2(CUPS_HTTP_DEFAULT, dests));
ef416fc2 269}
270
271
272/*
273 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
274 *
ecdc0628 275 * Starting with CUPS 1.2, the returned list of destinations include the
276 * printer-info, printer-is-accepting-jobs, printer-is-shared,
277 * printer-make-and-model, printer-state, printer-state-change-time,
278 * printer-state-reasons, and printer-type attributes as options.
279 *
2abf387c 280 * Use the cupsFreeDests() function to free the destination list and
281 * the cupsGetDest() function to find a particular destination.
282 *
ef416fc2 283 * @since CUPS 1.1.21@
284 */
285
286int /* O - Number of destinations */
3d052e43 287cupsGetDests2(http_t *http, /* I - HTTP connection or CUPS_HTTP_DEFAULT */
ef416fc2 288 cups_dest_t **dests) /* O - Destinations */
289{
290 int i; /* Looping var */
291 int num_dests; /* Number of destinations */
292 cups_dest_t *dest; /* Destination pointer */
293 const char *home; /* HOME environment variable */
b423cd4c 294 char filename[1024]; /* Local ~/.cups/lpoptions file */
ef416fc2 295 const char *defprinter; /* Default printer */
296 char name[1024], /* Copy of printer name */
297 *instance; /* Pointer to instance name */
298 int num_reals; /* Number of real queues */
299 cups_dest_t *reals; /* Real queues */
3d052e43 300 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 301
302
303 /*
304 * Range check the input...
305 */
306
3d052e43 307 if (!dests)
ef416fc2 308 return (0);
309
310 /*
311 * Initialize destination array...
312 */
313
314 num_dests = 0;
315 *dests = (cups_dest_t *)0;
316
317 /*
318 * Grab the printers and classes...
319 */
320
a4924f6c
MS
321 num_dests = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, num_dests, dests);
322 num_dests = cups_get_sdests(http, CUPS_GET_CLASSES, NULL, num_dests, dests);
ef416fc2 323
324 /*
325 * Make a copy of the "real" queues for a later sanity check...
326 */
327
328 if (num_dests > 0)
329 {
330 num_reals = num_dests;
331 reals = calloc(num_reals, sizeof(cups_dest_t));
332
333 if (reals)
334 memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
335 else
336 num_reals = 0;
337 }
338 else
339 {
340 num_reals = 0;
341 reals = NULL;
342 }
343
344 /*
345 * Grab the default destination...
346 */
347
348 if ((defprinter = cupsGetDefault2(http)) != NULL)
349 {
350 /*
351 * Grab printer and instance name...
352 */
353
354 strlcpy(name, defprinter, sizeof(name));
355
356 if ((instance = strchr(name, '/')) != NULL)
357 *instance++ = '\0';
358
359 /*
360 * Lookup the printer and instance and make it the default...
361 */
362
363 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
364 dest->is_default = 1;
365 }
366 else
367 {
368 /*
369 * This initialization of "instance" is unnecessary, but avoids a
370 * compiler warning...
371 */
372
373 instance = NULL;
374 }
375
376 /*
b423cd4c 377 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
ef416fc2 378 */
379
380 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
a4924f6c 381 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 382
383 if ((home = getenv("HOME")) != NULL)
384 {
b423cd4c 385 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
386 if (access(filename, 0))
387 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
388
a4924f6c 389 num_dests = cups_get_dests(filename, NULL, NULL, num_dests, dests);
ef416fc2 390 }
391
392 /*
393 * Validate the current default destination - this prevents old
b423cd4c 394 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
ef416fc2 395 * pointing to a non-existent printer or class...
396 */
397
398 if (num_reals)
399 {
400 /*
401 * See if we have a default printer...
402 */
403
404 if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
405 {
406 /*
407 * Have a default; see if it is real...
408 */
409
410 dest = cupsGetDest(dest->name, NULL, num_reals, reals);
411 }
412
413 /*
414 * If dest is NULL, then no default (that exists) is set, so we
415 * need to set a default if one exists...
416 */
417
418 if (dest == NULL && defprinter != NULL)
419 {
420 for (i = 0; i < num_dests; i ++)
421 (*dests)[i].is_default = 0;
422
423 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
424 dest->is_default = 1;
425 }
426
427 /*
428 * Free memory...
429 */
430
431 free(reals);
432 }
433
434 /*
435 * Return the number of destinations...
436 */
437
438 return (num_dests);
439}
440
441
a4924f6c
MS
442/*
443 * 'cupsGetNamedDest()' - Get options for the named destination.
444 *
445 * This function is optimized for retrieving a single destination and should
446 * be used instead of cupsGetDests() and cupsGetDest() when you either know
447 * the name of the destination or want to print to the default destination.
448 * If NULL is returned, the destination does not exist or there is no default
449 * destination.
450 *
3d052e43
MS
451 * If "http" is CUPS_HTTP_DEFAULT, the connection to the default print server
452 * will be used.
a4924f6c
MS
453 *
454 * If "name" is NULL, the default printer for the current user will be returned.
455 *
456 * The returned destination must be freed using cupsFreeDests() with a
457 * "num_dests" of 1.
458 *
459 * @since CUPS 1.4@
460 */
461
462cups_dest_t * /* O - Destination or NULL */
3d052e43 463cupsGetNamedDest(http_t *http, /* I - HTTP connection or CUPS_HTTP_DEFAULT */
a4924f6c
MS
464 const char *name, /* I - Destination name or NULL */
465 const char *instance) /* I - Instance name or NULL */
466{
467 cups_dest_t *dest; /* Destination */
468 char filename[1024], /* Path to lpoptions */
469 defname[256]; /* Default printer name */
470 const char *home = getenv("HOME"); /* Home directory */
471 ipp_op_t op = IPP_GET_PRINTER_ATTRIBUTES;
472 /* IPP operation to get server ops */
473 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
474
475
a4924f6c
MS
476 /*
477 * If "name" is NULL, find the default destination...
478 */
479
480 if (!name)
481 {
482 if ((name = getenv("LPDEST")) == NULL)
483 if ((name = getenv("PRINTER")) != NULL && !strcmp(name, "lp"))
484 name = NULL;
485
486 if (!name && home)
487 {
488 /*
489 * No default in the environment, try the user's lpoptions files...
490 */
491
492 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
493
494 if ((name = cups_get_default(filename, defname, sizeof(defname),
495 &instance)) == NULL)
496 {
497 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
498 name = cups_get_default(filename, defname, sizeof(defname),
499 &instance);
500 }
501 }
502
503 if (!name)
504 {
505 /*
506 * Still not there? Try the system lpoptions file...
507 */
508
509 snprintf(filename, sizeof(filename), "%s/lpoptions",
510 cg->cups_serverroot);
511 name = cups_get_default(filename, defname, sizeof(defname), &instance);
512 }
513
514 if (!name)
515 {
516 /*
517 * No locally-set default destination, ask the server...
518 */
519
520 op = CUPS_GET_DEFAULT;
521 }
522 }
523
524 /*
525 * Get the printer's attributes...
526 */
527
528 if (!cups_get_sdests(http, op, name, 0, &dest))
529 return (NULL);
530
531 if (instance)
532 dest->instance = _cupsStrAlloc(instance);
533
534 /*
535 * Then add local options...
536 */
537
538 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
539 cups_get_dests(filename, name, instance, 1, &dest);
540
541 if (home)
542 {
543 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
544
545 if (access(filename, 0))
546 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
547
548 cups_get_dests(filename, name, instance, 1, &dest);
549 }
550
551 /*
552 * Return the result...
553 */
554
555 return (dest);
556}
557
558
f7deaa1a 559/*
560 * 'cupsRemoveDest()' - Remove a destination from the destination list.
561 *
562 * Removing a destination/instance does not delete the class or printer
563 * queue, merely the lpoptions for that destination/instance. Use the
564 * cupsSetDests() or cupsSetDests2() functions to save the new options
565 * for the user.
566 *
567 * @since CUPS 1.3@
568 */
569
570int /* O - New number of destinations */
571cupsRemoveDest(const char *name, /* I - Destination name */
572 const char *instance, /* I - Instance name or NULL */
573 int num_dests, /* I - Number of destinations */
574 cups_dest_t **dests) /* IO - Destinations */
575{
576 int i; /* Index into destinations */
577 cups_dest_t *dest; /* Pointer to destination */
578
579
580 /*
581 * Find the destination...
582 */
583
584 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
585 return (num_dests);
586
587 /*
588 * Free memory...
589 */
590
2e4ff8af
MS
591 _cupsStrFree(dest->name);
592 _cupsStrFree(dest->instance);
f7deaa1a 593 cupsFreeOptions(dest->num_options, dest->options);
594
595 /*
596 * Remove the destination from the array...
597 */
598
599 num_dests --;
600
601 i = dest - *dests;
602
603 if (i < num_dests)
604 memmove(dest, dest + 1, (num_dests - i) * sizeof(cups_dest_t));
605
606 return (num_dests);
607}
608
609
610/*
3d052e43 611 * 'cupsSetDefaultDest()' - Set the default destination.
f7deaa1a 612 *
613 * @since CUPS 1.3@
614 */
615
616void
617cupsSetDefaultDest(
618 const char *name, /* I - Destination name */
619 const char *instance, /* I - Instance name or NULL */
620 int num_dests, /* I - Number of destinations */
621 cups_dest_t *dests) /* I - Destinations */
622{
623 int i; /* Looping var */
624 cups_dest_t *dest; /* Current destination */
625
626
627 /*
628 * Range check input...
629 */
630
631 if (!name || num_dests <= 0 || !dests)
632 return;
633
634 /*
635 * Loop through the array and set the "is_default" flag for the matching
636 * destination...
637 */
638
639 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
640 dest->is_default = !strcasecmp(name, dest->name) &&
641 ((!instance && !dest->instance) ||
642 (instance && dest->instance &&
643 !strcasecmp(instance, dest->instance)));
644}
645
646
ef416fc2 647/*
648 * 'cupsSetDests()' - Save the list of destinations for the default server.
649 *
650 * This function saves the destinations to /etc/cups/lpoptions when run
b423cd4c 651 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 652 */
653
654void
655cupsSetDests(int num_dests, /* I - Number of destinations */
656 cups_dest_t *dests) /* I - Destinations */
657{
3d052e43 658 cupsSetDests2(CUPS_HTTP_DEFAULT, num_dests, dests);
ef416fc2 659}
660
661
662/*
663 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
664 *
665 * This function saves the destinations to /etc/cups/lpoptions when run
b423cd4c 666 * as root and ~/.cups/lpoptions when run as a normal user.
ef416fc2 667 *
668 * @since CUPS 1.1.21@
669 */
670
671int /* O - 0 on success, -1 on error */
3d052e43 672cupsSetDests2(http_t *http, /* I - HTTP connection or CUPS_HTTP_DEFAULT */
ef416fc2 673 int num_dests, /* I - Number of destinations */
674 cups_dest_t *dests) /* I - Destinations */
675{
676 int i, j; /* Looping vars */
677 int wrote; /* Wrote definition? */
678 cups_dest_t *dest; /* Current destination */
679 cups_option_t *option; /* Current option */
8ca02f3c 680 _ipp_option_t *match; /* Matching attribute for option */
ef416fc2 681 FILE *fp; /* File pointer */
b86bc4cf 682#ifndef WIN32
ef416fc2 683 const char *home; /* HOME environment variable */
b86bc4cf 684#endif /* WIN32 */
ef416fc2 685 char filename[1024]; /* lpoptions file */
686 int num_temps; /* Number of temporary destinations */
687 cups_dest_t *temps, /* Temporary destinations */
688 *temp; /* Current temporary dest */
689 const char *val; /* Value of temporary option */
3d052e43 690 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 691
692
693 /*
694 * Range check the input...
695 */
696
3d052e43 697 if (!num_dests || !dests)
ef416fc2 698 return (-1);
699
700 /*
701 * Get the server destinations...
702 */
703
a4924f6c
MS
704 num_temps = cups_get_sdests(http, CUPS_GET_PRINTERS, NULL, 0, &temps);
705 num_temps = cups_get_sdests(http, CUPS_GET_CLASSES, NULL, num_temps, &temps);
ef416fc2 706
707 /*
708 * Figure out which file to write to...
709 */
710
711 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
712
713#ifndef WIN32
714 if (getuid())
715 {
716 /*
717 * Merge in server defaults...
718 */
719
a4924f6c 720 num_temps = cups_get_dests(filename, NULL, NULL, num_temps, &temps);
ef416fc2 721
722 /*
723 * Point to user defaults...
724 */
725
726 if ((home = getenv("HOME")) != NULL)
b423cd4c 727 {
728 /*
729 * Remove the old ~/.lpoptions file...
730 */
731
ef416fc2 732 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
b423cd4c 733 unlink(filename);
734
735 /*
736 * Create ~/.cups subdirectory...
737 */
738
739 snprintf(filename, sizeof(filename), "%s/.cups", home);
740 if (access(filename, 0))
741 mkdir(filename, 0700);
742
743 snprintf(filename, sizeof(filename), "%s/.cups/lpoptions", home);
744 }
ef416fc2 745 }
746#endif /* !WIN32 */
747
748 /*
749 * Try to open the file...
750 */
751
752 if ((fp = fopen(filename, "w")) == NULL)
753 {
754 cupsFreeDests(num_temps, temps);
755 return (-1);
756 }
757
d6ae789d 758#ifndef WIN32
759 /*
760 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
761 * file...
762 */
763
764 if (!getuid())
765 fchmod(fileno(fp), 0644);
766#endif /* !WIN32 */
767
ef416fc2 768 /*
769 * Write each printer; each line looks like:
770 *
771 * Dest name[/instance] options
772 * Default name[/instance] options
773 */
774
775 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
776 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
777 {
778 if (dest->is_default)
779 {
780 fprintf(fp, "Default %s", dest->name);
781 if (dest->instance)
782 fprintf(fp, "/%s", dest->instance);
783
784 wrote = 1;
785 }
786 else
787 wrote = 0;
788
789 if ((temp = cupsGetDest(dest->name, dest->instance, num_temps, temps)) == NULL)
790 temp = cupsGetDest(dest->name, NULL, num_temps, temps);
791
792 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
793 {
8ca02f3c 794 /*
795 * See if this option is a printer attribute; if so, skip it...
796 */
797
798 if ((match = _ippFindOption(option->name)) != NULL &&
799 match->group_tag == IPP_TAG_PRINTER)
800 continue;
801
ef416fc2 802 /*
803 * See if the server/global options match these; if so, don't
804 * write 'em.
805 */
806
8ca02f3c 807 if (temp &&
808 (val = cupsGetOption(option->name, temp->num_options,
809 temp->options)) != NULL &&
810 !strcasecmp(val, option->value))
811 continue;
ef416fc2 812
813 /*
814 * Options don't match, write to the file...
815 */
816
817 if (!wrote)
818 {
819 fprintf(fp, "Dest %s", dest->name);
820 if (dest->instance)
821 fprintf(fp, "/%s", dest->instance);
822 wrote = 1;
823 }
824
825 if (option->value[0])
826 {
8ca02f3c 827 if (strchr(option->value, ' ') ||
828 strchr(option->value, '\\') ||
829 strchr(option->value, '\"') ||
830 strchr(option->value, '\''))
831 {
832 /*
833 * Quote the value...
834 */
835
836 fprintf(fp, " %s=\"", option->name);
837
838 for (val = option->value; *val; val ++)
839 {
840 if (strchr("\"\'\\", *val))
841 putc('\\', fp);
842
843 putc(*val, fp);
844 }
845
846 putc('\"', fp);
847 }
848 else
849 {
850 /*
851 * Store the literal value...
852 */
853
ef416fc2 854 fprintf(fp, " %s=%s", option->name, option->value);
8ca02f3c 855 }
ef416fc2 856 }
857 else
858 fprintf(fp, " %s", option->name);
859 }
860
861 if (wrote)
862 fputs("\n", fp);
863 }
864
865 /*
fa73b229 866 * Free the temporary destinations and close the file...
ef416fc2 867 */
868
869 cupsFreeDests(num_temps, temps);
870
fa73b229 871 fclose(fp);
872
873#ifdef HAVE_NOTIFY_POST
ef416fc2 874 /*
fa73b229 875 * Send a notification so that MacOS X applications can know about the
876 * change, too.
ef416fc2 877 */
878
fa73b229 879 notify_post("com.apple.printerListChange");
880#endif /* HAVE_NOTIFY_POST */
ef416fc2 881
882 return (0);
883}
884
885
a4924f6c
MS
886/*
887 * 'cups_get_default()' - Get the default destination from an lpoptions file.
888 */
889
890static const char * /* O - Default destination or NULL */
891cups_get_default(const char *filename, /* I - File to read */
892 char *namebuf, /* I - Name buffer */
893 size_t namesize, /* I - Size of name buffer */
894 const char **instance) /* I - Instance */
895{
896 cups_file_t *fp; /* lpoptions file */
897 char line[8192], /* Line from file */
898 *value, /* Value for line */
899 *nameptr; /* Pointer into name */
900 int linenum; /* Current line */
901
902
903 *namebuf = '\0';
904
905 if ((fp = cupsFileOpen(filename, "r")) != NULL)
906 {
907 linenum = 0;
908
909 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
910 {
911 if (!strcasecmp(line, "default") && value)
912 {
913 strlcpy(namebuf, value, namesize);
914
915 if ((nameptr = strchr(namebuf, ' ')) != NULL)
916 *nameptr = '\0';
917 if ((nameptr = strchr(namebuf, '\t')) != NULL)
918 *nameptr = '\0';
919
920 if ((nameptr = strchr(namebuf, '/')) != NULL)
921 *nameptr++ = '\0';
922
923 *instance = nameptr;
924 break;
925 }
926 }
927
928 cupsFileClose(fp);
929 }
930
931 return (*namebuf ? namebuf : NULL);
932}
933
934
ef416fc2 935/*
936 * 'cups_get_dests()' - Get destinations from a file.
937 */
938
939static int /* O - Number of destinations */
940cups_get_dests(const char *filename, /* I - File to read from */
a4924f6c
MS
941 const char *match_name, /* I - Destination name we want */
942 const char *match_inst, /* I - Instance name we want */
ef416fc2 943 int num_dests, /* I - Number of destinations */
944 cups_dest_t **dests) /* IO - Destinations */
945{
946 int i; /* Looping var */
947 cups_dest_t *dest; /* Current destination */
a4924f6c 948 cups_file_t *fp; /* File pointer */
ef416fc2 949 char line[8192], /* Line from file */
950 *lineptr, /* Pointer into line */
951 *name, /* Name of destination/option */
952 *instance; /* Instance of destination */
a4924f6c 953 int linenum; /* Current line number */
ef416fc2 954 const char *printer; /* PRINTER or LPDEST */
955
956
a4924f6c
MS
957 DEBUG_printf(("cups_get_dests(filename=\"%s\", match_name=\"%s\", "
958 "match_inst=\"%s\", num_dests=%d, dests=%p)\n", filename,
959 match_name ? match_name : "(null)",
960 match_inst ? match_inst : "(null)", num_dests, dests));
961
962 /*
963 * Try to open the file...
964 */
965
966 if ((fp = cupsFileOpen(filename, "r")) == NULL)
967 return (num_dests);
968
ef416fc2 969 /*
970 * Check environment variables...
971 */
972
973 if ((printer = getenv("LPDEST")) == NULL)
974 if ((printer = getenv("PRINTER")) != NULL)
975 if (strcmp(printer, "lp") == 0)
976 printer = NULL;
977
a4924f6c
MS
978 DEBUG_printf(("cups_get_dests: printer=\"%s\"\n",
979 printer ? printer : "(null)"));
ef416fc2 980
981 /*
982 * Read each printer; each line looks like:
983 *
984 * Dest name[/instance] options
985 * Default name[/instance] options
986 */
987
a4924f6c
MS
988 linenum = 0;
989
990 while (cupsFileGetConf(fp, line, sizeof(line), &lineptr, &linenum))
ef416fc2 991 {
992 /*
993 * See what type of line it is...
994 */
995
a4924f6c
MS
996 DEBUG_printf(("cups_get_dests: linenum=%d line=\"%s\" lineptr=\"%s\"\n",
997 linenum, line, lineptr ? lineptr : "(null)"));
ef416fc2 998
a4924f6c
MS
999 if ((strcasecmp(line, "dest") && strcasecmp(line, "default")) || !lineptr)
1000 {
1001 DEBUG_puts("cups_get_dests: Not a dest or default line...");
ef416fc2 1002 continue;
a4924f6c 1003 }
ef416fc2 1004
1005 name = lineptr;
1006
1007 /*
1008 * Search for an instance...
1009 */
1010
1011 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
1012 lineptr ++;
1013
ef416fc2 1014 if (*lineptr == '/')
1015 {
1016 /*
1017 * Found an instance...
1018 */
1019
1020 *lineptr++ = '\0';
1021 instance = lineptr;
1022
1023 /*
1024 * Search for an instance...
1025 */
1026
1027 while (!isspace(*lineptr & 255) && *lineptr)
1028 lineptr ++;
1029 }
1030 else
1031 instance = NULL;
1032
a4924f6c
MS
1033 if (*lineptr)
1034 *lineptr++ = '\0';
1035
1036 DEBUG_printf(("cups_get_dests: name=\"%s\", instance=\"%s\"\n", name,
1037 instance));
ef416fc2 1038
1039 /*
1040 * See if the primary instance of the destination exists; if not,
1041 * ignore this entry and move on...
1042 */
1043
a4924f6c
MS
1044 if (match_name)
1045 {
1046 if (strcasecmp(name, match_name) ||
1047 (!instance && match_inst) ||
1048 (instance && !match_inst) ||
1049 (instance && strcasecmp(instance, match_inst)))
1050 continue;
ef416fc2 1051
a4924f6c
MS
1052 dest = *dests;
1053 }
1054 else if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
1055 {
1056 DEBUG_puts("cups_get_dests: Not found!");
1057 continue;
1058 }
1059 else
ef416fc2 1060 {
1061 /*
a4924f6c 1062 * Add the destination...
ef416fc2 1063 */
1064
a4924f6c
MS
1065 num_dests = cupsAddDest(name, instance, num_dests, dests);
1066
1067 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
1068 {
1069 /*
1070 * Out of memory!
1071 */
1072
1073 DEBUG_puts("cups_get_dests: Out of memory!");
1074 break;
1075 }
ef416fc2 1076 }
1077
1078 /*
1079 * Add options until we hit the end of the line...
1080 */
1081
1082 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
1083 &(dest->options));
1084
a4924f6c
MS
1085 /*
1086 * If we found what we were looking for, stop now...
1087 */
1088
1089 if (match_name)
1090 break;
1091
ef416fc2 1092 /*
1093 * Set this as default if needed...
1094 */
1095
a4924f6c 1096 if (!printer && !strcasecmp(line, "default"))
ef416fc2 1097 {
a4924f6c
MS
1098 DEBUG_puts("cups_get_dests: Setting as default...");
1099
ef416fc2 1100 for (i = 0; i < num_dests; i ++)
1101 (*dests)[i].is_default = 0;
1102
1103 dest->is_default = 1;
1104 }
1105 }
1106
1107 /*
1108 * Close the file and return...
1109 */
1110
a4924f6c 1111 cupsFileClose(fp);
ef416fc2 1112
1113 return (num_dests);
1114}
1115
1116
1117/*
1118 * 'cups_get_sdests()' - Get destinations from a server.
1119 */
1120
1121static int /* O - Number of destinations */
3d052e43 1122cups_get_sdests(http_t *http, /* I - HTTP connection or CUPS_HTTP_DEFAULT */
a4924f6c
MS
1123 ipp_op_t op, /* I - IPP operation */
1124 const char *name, /* I - Name of destination */
ef416fc2 1125 int num_dests, /* I - Number of destinations */
1126 cups_dest_t **dests) /* IO - Destinations */
1127{
fa73b229 1128 int i; /* Looping var */
ef416fc2 1129 cups_dest_t *dest; /* Current destination */
1130 ipp_t *request, /* IPP Request */
1131 *response; /* IPP Response */
1132 ipp_attribute_t *attr; /* Current attribute */
fa73b229 1133 int accepting, /* printer-is-accepting-jobs attribute */
1134 shared, /* printer-is-shared attribute */
1135 state, /* printer-state attribute */
1136 change_time, /* printer-state-change-time attribute */
1137 type; /* printer-type attribute */
1138 const char *info, /* printer-info attribute */
f42414bf 1139 *location, /* printer-location attribute */
fa73b229 1140 *make_model, /* printer-make-and-model attribute */
a4924f6c
MS
1141 *printer_name; /* printer-name attribute */
1142 char uri[1024], /* printer-uri value */
1143 job_sheets[1024], /* job-sheets-default attribute */
f42414bf 1144 auth_info_req[1024], /* auth-info-required attribute */
f7deaa1a 1145 reasons[1024]; /* printer-state-reasons attribute */
1146 int num_options; /* Number of options */
1147 cups_option_t *options; /* Options */
1148 char optname[1024], /* Option name */
1149 value[2048], /* Option value */
1150 *ptr; /* Pointer into name/value */
ef416fc2 1151 static const char * const pattrs[] = /* Attributes we're interested in */
1152 {
f42414bf 1153 "auth-info-required",
fa73b229 1154 "job-sheets-default",
1155 "printer-info",
1156 "printer-is-accepting-jobs",
1157 "printer-is-shared",
f42414bf 1158 "printer-location",
fa73b229 1159 "printer-make-and-model",
ef416fc2 1160 "printer-name",
fa73b229 1161 "printer-state",
1162 "printer-state-change-time",
1163 "printer-state-reasons",
f7deaa1a 1164 "printer-type",
1165 "printer-defaults"
ef416fc2 1166 };
1167
1168
1169 /*
1170 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
1171 * the following attributes:
1172 *
1173 * attributes-charset
1174 * attributes-natural-language
fa73b229 1175 * requesting-user-name
ef416fc2 1176 */
1177
fa73b229 1178 request = ippNewRequest(op);
ef416fc2 1179
1180 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1181 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1182 NULL, pattrs);
1183
fa73b229 1184 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1185 "requesting-user-name", NULL, cupsUser());
1186
a4924f6c
MS
1187 if (name)
1188 {
1189 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1190 "localhost", ippPort(), "/printers/%s", name);
1191 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
1192 uri);
1193 }
1194
ef416fc2 1195 /*
1196 * Do the request and get back a response...
1197 */
1198
1199 if ((response = cupsDoRequest(http, request, "/")) != NULL)
1200 {
1201 for (attr = response->attrs; attr != NULL; attr = attr->next)
1202 {
1203 /*
1204 * Skip leading attributes until we hit a printer...
1205 */
1206
1207 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1208 attr = attr->next;
1209
1210 if (attr == NULL)
1211 break;
1212
1213 /*
f7deaa1a 1214 * Pull the needed attributes from this printer...
ef416fc2 1215 */
1216
a4924f6c
MS
1217 accepting = 0;
1218 change_time = 0;
1219 info = NULL;
1220 location = NULL;
1221 make_model = NULL;
1222 printer_name = NULL;
1223 num_options = 0;
1224 options = NULL;
1225 shared = 1;
1226 state = IPP_PRINTER_IDLE;
1227 type = CUPS_PRINTER_LOCAL;
ef416fc2 1228
f42414bf 1229 auth_info_req[0] = '\0';
1230 job_sheets[0] = '\0';
1231 reasons[0] = '\0';
ef416fc2 1232
1233 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1234 {
f42414bf 1235 if (!strcmp(attr->name, "auth-info-required") &&
1236 attr->value_tag == IPP_TAG_KEYWORD)
1237 {
1238 strlcpy(auth_info_req, attr->values[0].string.text,
1239 sizeof(auth_info_req));
1240
1241 for (i = 1, ptr = auth_info_req + strlen(auth_info_req);
1242 i < attr->num_values;
1243 i ++)
1244 {
1245 snprintf(ptr, sizeof(auth_info_req) - (ptr - auth_info_req), ",%s",
1246 attr->values[i].string.text);
1247 ptr += strlen(ptr);
1248 }
1249 }
1250 else if (!strcmp(attr->name, "job-sheets-default") &&
1251 (attr->value_tag == IPP_TAG_KEYWORD ||
1252 attr->value_tag == IPP_TAG_NAME))
ef416fc2 1253 {
1254 if (attr->num_values == 2)
1255 snprintf(job_sheets, sizeof(job_sheets), "%s,%s",
1256 attr->values[0].string.text, attr->values[1].string.text);
1257 else
fa73b229 1258 strlcpy(job_sheets, attr->values[0].string.text,
1259 sizeof(job_sheets));
ef416fc2 1260 }
fa73b229 1261 else if (!strcmp(attr->name, "printer-info") &&
1262 attr->value_tag == IPP_TAG_TEXT)
1263 info = attr->values[0].string.text;
1264 else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
1265 attr->value_tag == IPP_TAG_BOOLEAN)
1266 accepting = attr->values[0].boolean;
1267 else if (!strcmp(attr->name, "printer-is-shared") &&
1268 attr->value_tag == IPP_TAG_BOOLEAN)
1269 shared = attr->values[0].boolean;
f42414bf 1270 else if (!strcmp(attr->name, "printer-location") &&
1271 attr->value_tag == IPP_TAG_TEXT)
1272 location = attr->values[0].string.text;
fa73b229 1273 else if (!strcmp(attr->name, "printer-make-and-model") &&
1274 attr->value_tag == IPP_TAG_TEXT)
1275 make_model = attr->values[0].string.text;
1276 else if (!strcmp(attr->name, "printer-name") &&
1277 attr->value_tag == IPP_TAG_NAME)
a4924f6c 1278 printer_name = attr->values[0].string.text;
fa73b229 1279 else if (!strcmp(attr->name, "printer-state") &&
1280 attr->value_tag == IPP_TAG_ENUM)
1281 state = attr->values[0].integer;
1282 else if (!strcmp(attr->name, "printer-state-change-time") &&
1283 attr->value_tag == IPP_TAG_INTEGER)
1284 change_time = attr->values[0].integer;
1285 else if (!strcmp(attr->name, "printer-state-reasons") &&
1286 attr->value_tag == IPP_TAG_KEYWORD)
1287 {
1288 strlcpy(reasons, attr->values[0].string.text, sizeof(reasons));
f7deaa1a 1289 for (i = 1, ptr = reasons + strlen(reasons);
fa73b229 1290 i < attr->num_values;
1291 i ++)
1292 {
f7deaa1a 1293 snprintf(ptr, sizeof(reasons) - (ptr - reasons), ",%s",
fa73b229 1294 attr->values[i].string.text);
f7deaa1a 1295 ptr += strlen(ptr);
fa73b229 1296 }
1297 }
1298 else if (!strcmp(attr->name, "printer-type") &&
1299 attr->value_tag == IPP_TAG_ENUM)
1300 type = attr->values[0].integer;
f7deaa1a 1301 else if (strncmp(attr->name, "notify-", 7) &&
1302 (attr->value_tag == IPP_TAG_BOOLEAN ||
1303 attr->value_tag == IPP_TAG_ENUM ||
1304 attr->value_tag == IPP_TAG_INTEGER ||
1305 attr->value_tag == IPP_TAG_KEYWORD ||
1306 attr->value_tag == IPP_TAG_NAME ||
1307 attr->value_tag == IPP_TAG_RANGE) &&
1308 strstr(attr->name, "-default"))
1309 {
1310 char *valptr; /* Pointer into attribute value */
1311
1312
1313 /*
1314 * Add a default option...
1315 */
1316
1317 strlcpy(optname, attr->name, sizeof(optname));
1318 if ((ptr = strstr(optname, "-default")) != NULL)
1319 *ptr = '\0';
1320
1321 value[0] = '\0';
1322 for (i = 0, ptr = value; i < attr->num_values; i ++)
1323 {
1324 if (ptr >= (value + sizeof(value) - 1))
1325 break;
1326
1327 if (i)
1328 *ptr++ = ',';
1329
1330 switch (attr->value_tag)
1331 {
1332 case IPP_TAG_INTEGER :
1333 case IPP_TAG_ENUM :
1334 snprintf(ptr, sizeof(value) - (ptr - value), "%d",
1335 attr->values[i].integer);
1336 break;
1337
1338 case IPP_TAG_BOOLEAN :
1339 if (attr->values[i].boolean)
1340 strlcpy(ptr, "true", sizeof(value) - (ptr - value));
1341 else
1342 strlcpy(ptr, "false", sizeof(value) - (ptr - value));
1343 break;
1344
1345 case IPP_TAG_RANGE :
1346 if (attr->values[i].range.lower ==
1347 attr->values[i].range.upper)
1348 snprintf(ptr, sizeof(value) - (ptr - value), "%d",
1349 attr->values[i].range.lower);
1350 else
1351 snprintf(ptr, sizeof(value) - (ptr - value), "%d-%d",
1352 attr->values[i].range.lower,
1353 attr->values[i].range.upper);
1354 break;
1355
1356 default :
1357 for (valptr = attr->values[i].string.text;
1358 *valptr && ptr < (value + sizeof(value) - 2);)
1359 {
1360 if (strchr(" \t\n\\\'\"", *valptr))
1361 *ptr++ = '\\';
1362
1363 *ptr++ = *valptr++;
1364 }
1365
1366 *ptr = '\0';
1367 break;
1368 }
1369
1370 ptr += strlen(ptr);
1371 }
1372
1373 num_options = cupsAddOption(optname, value, num_options, &options);
1374 }
ef416fc2 1375
1376 attr = attr->next;
1377 }
1378
1379 /*
1380 * See if we have everything needed...
1381 */
1382
a4924f6c 1383 if (!printer_name)
ef416fc2 1384 {
f7deaa1a 1385 cupsFreeOptions(num_options, options);
1386
ef416fc2 1387 if (attr == NULL)
1388 break;
1389 else
1390 continue;
1391 }
1392
a4924f6c 1393 num_dests = cupsAddDest(printer_name, NULL, num_dests, dests);
ef416fc2 1394
a4924f6c 1395 if ((dest = cupsGetDest(printer_name, NULL, num_dests, *dests)) != NULL)
fa73b229 1396 {
f7deaa1a 1397 dest->num_options = num_options;
1398 dest->options = options;
1399
1400 num_options = 0;
1401 options = NULL;
1402
f42414bf 1403 if (auth_info_req[0])
1404 dest->num_options = cupsAddOption("auth-info-required", auth_info_req,
1405 dest->num_options,
1406 &(dest->options));
1407
ef416fc2 1408 if (job_sheets[0])
fa73b229 1409 dest->num_options = cupsAddOption("job-sheets", job_sheets,
1410 dest->num_options,
1411 &(dest->options));
1412
1413 if (info)
1414 dest->num_options = cupsAddOption("printer-info", info,
1415 dest->num_options,
1416 &(dest->options));
1417
f7deaa1a 1418 sprintf(value, "%d", accepting);
1419 dest->num_options = cupsAddOption("printer-is-accepting-jobs", value,
fa73b229 1420 dest->num_options,
1421 &(dest->options));
1422
f7deaa1a 1423 sprintf(value, "%d", shared);
1424 dest->num_options = cupsAddOption("printer-is-shared", value,
fa73b229 1425 dest->num_options,
1426 &(dest->options));
1427
f42414bf 1428 if (location)
1429 dest->num_options = cupsAddOption("printer-location",
1430 location, dest->num_options,
1431 &(dest->options));
1432
fa73b229 1433 if (make_model)
1434 dest->num_options = cupsAddOption("printer-make-and-model",
1435 make_model, dest->num_options,
ef416fc2 1436 &(dest->options));
1437
f7deaa1a 1438 sprintf(value, "%d", state);
1439 dest->num_options = cupsAddOption("printer-state", value,
fa73b229 1440 dest->num_options,
1441 &(dest->options));
1442
1443 if (change_time)
1444 {
f7deaa1a 1445 sprintf(value, "%d", change_time);
1446 dest->num_options = cupsAddOption("printer-state-change-time", value,
fa73b229 1447 dest->num_options,
1448 &(dest->options));
1449 }
1450
1451 if (reasons[0])
1452 dest->num_options = cupsAddOption("printer-state-reasons", reasons,
1453 dest->num_options,
1454 &(dest->options));
1455
f7deaa1a 1456 sprintf(value, "%d", type);
1457 dest->num_options = cupsAddOption("printer-type", value,
fa73b229 1458 dest->num_options,
1459 &(dest->options));
1460 }
1461
f7deaa1a 1462 cupsFreeOptions(num_options, options);
1463
ef416fc2 1464 if (attr == NULL)
1465 break;
1466 }
1467
1468 ippDelete(response);
1469 }
1470
1471 /*
1472 * Return the count...
1473 */
1474
1475 return (num_dests);
1476}
1477
1478
1479/*
2e4ff8af 1480 * End of "$Id: dest.c 6943 2007-09-10 23:00:33Z mike $".
ef416fc2 1481 */