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