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