]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / dest.c
1 /*
2 * "$Id: dest.c 4918 2006-01-12 05:14:40Z mike $"
3 *
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
6 *
7 * Copyright 1997-2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Easy Software Products and are protected by Federal
11 * copyright law. Distribution and use rights are outlined in the file
12 * "LICENSE.txt" which should have been included with this file. If this
13 * file is missing or damaged please contact Easy Software Products
14 * at:
15 *
16 * Attn: CUPS Licensing Information
17 * Easy Software Products
18 * 44141 Airport View Drive, Suite 204
19 * Hollywood, Maryland 20636 USA
20 *
21 * Voice: (301) 373-9600
22 * EMail: cups-info@cups.org
23 * WWW: http://www.cups.org
24 *
25 * This file is subject to the Apple OS-Developed Software exception.
26 *
27 * Contents:
28 *
29 * cupsAddDest() - Add a destination to the list of destinations.
30 * cupsFreeDests() - Free the memory used by the list of destinations.
31 * cupsGetDest() - Get the named destination from the list.
32 * cupsGetDests() - Get the list of destinations from the default server.
33 * cupsGetDests2() - Get the list of destinations from the specified server.
34 * cupsSetDests() - Set the list of destinations for the default server.
35 * cupsSetDests2() - Set the list of destinations for the specified server.
36 * cups_get_dests() - Get destinations from a file.
37 * cups_get_sdests() - Get destinations from a server.
38 */
39
40 /*
41 * Include necessary headers...
42 */
43
44 #include "globals.h"
45 #include <stdlib.h>
46 #include <ctype.h>
47
48
49 /*
50 * Local functions...
51 */
52
53 static int cups_get_dests(const char *filename, int num_dests,
54 cups_dest_t **dests);
55 static int cups_get_sdests(http_t *http, ipp_op_t op, int num_dests,
56 cups_dest_t **dests);
57
58
59 /*
60 * 'cupsAddDest()' - Add a destination to the list of destinations.
61 *
62 * Use the cupsSaveDests() function to save the updated list of destinations
63 * to the user's lpoptions file.
64 */
65
66 int /* O - New number of destinations */
67 cupsAddDest(const char *name, /* I - Name of destination */
68 const char *instance, /* I - Instance of destination or NULL for none/primary */
69 int num_dests, /* I - Number of destinations */
70 cups_dest_t **dests) /* IO - Destinations */
71 {
72 int i; /* Looping var */
73 cups_dest_t *dest; /* Destination pointer */
74
75
76 if (name == NULL || dests == NULL)
77 return (0);
78
79 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
80 return (num_dests);
81
82 /*
83 * Add new destination...
84 */
85
86 if (num_dests == 0)
87 dest = malloc(sizeof(cups_dest_t));
88 else
89 dest = realloc(*dests, sizeof(cups_dest_t) * (num_dests + 1));
90
91 if (dest == NULL)
92 return (num_dests);
93
94 *dests = dest;
95
96 for (i = num_dests; i > 0; i --, dest ++)
97 if (strcasecmp(name, dest->name) < 0)
98 break;
99 else if (strcasecmp(name, dest->name) == 0 &&
100 instance != NULL && dest->instance != NULL &&
101 strcasecmp(instance, dest->instance) < 0)
102 break;
103
104 if (i > 0)
105 memmove(dest + 1, dest, i * sizeof(cups_dest_t));
106
107 dest->name = strdup(name);
108 dest->is_default = 0;
109 dest->num_options = 0;
110 dest->options = (cups_option_t *)0;
111
112 if (instance == NULL)
113 dest->instance = NULL;
114 else
115 dest->instance = strdup(instance);
116
117 return (num_dests + 1);
118 }
119
120
121 /*
122 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
123 */
124
125 void
126 cupsFreeDests(int num_dests, /* I - Number of destinations */
127 cups_dest_t *dests) /* I - Destinations */
128 {
129 int i; /* Looping var */
130 cups_dest_t *dest; /* Current destination */
131
132
133 if (num_dests == 0 || dests == NULL)
134 return;
135
136 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
137 {
138 free(dest->name);
139
140 if (dest->instance)
141 free(dest->instance);
142
143 cupsFreeOptions(dest->num_options, dest->options);
144 }
145
146 free(dests);
147 }
148
149
150 /*
151 * 'cupsGetDest()' - Get the named destination from the list.
152 *
153 * Use the cupsGetDests() or cupsGetDests2() functions to get a
154 * list of supported destinations for the current user.
155 */
156
157 cups_dest_t * /* O - Destination pointer or NULL */
158 cupsGetDest(const char *name, /* I - Name of destination */
159 const char *instance, /* I - Instance of destination */
160 int num_dests, /* I - Number of destinations */
161 cups_dest_t *dests) /* I - Destinations */
162 {
163 int comp; /* Result of comparison */
164
165
166 if (num_dests == 0 || dests == NULL)
167 return (NULL);
168
169 if (name == NULL)
170 {
171 /*
172 * NULL name for default printer.
173 */
174
175 while (num_dests > 0)
176 {
177 if (dests->is_default)
178 return (dests);
179
180 num_dests --;
181 dests ++;
182 }
183 }
184 else
185 {
186 /*
187 * Lookup name and optionally the instance...
188 */
189
190 while (num_dests > 0)
191 {
192 if ((comp = strcasecmp(name, dests->name)) < 0)
193 return (NULL);
194 else if (comp == 0)
195 {
196 if ((instance == NULL && dests->instance == NULL) ||
197 (instance != NULL && dests->instance != NULL &&
198 strcasecmp(instance, dests->instance) == 0))
199 return (dests);
200 }
201
202 num_dests --;
203 dests ++;
204 }
205 }
206
207 return (NULL);
208 }
209
210
211 /*
212 * 'cupsGetDests()' - Get the list of destinations from the default server.
213 */
214
215 int /* O - Number of destinations */
216 cupsGetDests(cups_dest_t **dests) /* O - Destinations */
217 {
218 int num_dests; /* Number of destinations */
219 http_t *http; /* HTTP connection */
220
221
222 /*
223 * Connect to the CUPS server and get the destination list and options...
224 */
225
226 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
227
228 num_dests = cupsGetDests2(http, dests);
229
230 if (http)
231 httpClose(http);
232
233 return (num_dests);
234 }
235
236
237 /*
238 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
239 *
240 * @since CUPS 1.1.21@
241 */
242
243 int /* O - Number of destinations */
244 cupsGetDests2(http_t *http, /* I - HTTP connection */
245 cups_dest_t **dests) /* O - Destinations */
246 {
247 int i; /* Looping var */
248 int num_dests; /* Number of destinations */
249 cups_dest_t *dest; /* Destination pointer */
250 const char *home; /* HOME environment variable */
251 char filename[1024]; /* Local ~/.lpoptions file */
252 const char *defprinter; /* Default printer */
253 char name[1024], /* Copy of printer name */
254 *instance; /* Pointer to instance name */
255 int num_reals; /* Number of real queues */
256 cups_dest_t *reals; /* Real queues */
257 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
258
259
260 /*
261 * Range check the input...
262 */
263
264 if (!http || !dests)
265 return (0);
266
267 /*
268 * Initialize destination array...
269 */
270
271 num_dests = 0;
272 *dests = (cups_dest_t *)0;
273
274 /*
275 * Grab the printers and classes...
276 */
277
278 num_dests = cups_get_sdests(http, CUPS_GET_PRINTERS, num_dests, dests);
279 num_dests = cups_get_sdests(http, CUPS_GET_CLASSES, num_dests, dests);
280
281 /*
282 * Make a copy of the "real" queues for a later sanity check...
283 */
284
285 if (num_dests > 0)
286 {
287 num_reals = num_dests;
288 reals = calloc(num_reals, sizeof(cups_dest_t));
289
290 if (reals)
291 memcpy(reals, *dests, num_reals * sizeof(cups_dest_t));
292 else
293 num_reals = 0;
294 }
295 else
296 {
297 num_reals = 0;
298 reals = NULL;
299 }
300
301 /*
302 * Grab the default destination...
303 */
304
305 if ((defprinter = cupsGetDefault2(http)) != NULL)
306 {
307 /*
308 * Grab printer and instance name...
309 */
310
311 strlcpy(name, defprinter, sizeof(name));
312
313 if ((instance = strchr(name, '/')) != NULL)
314 *instance++ = '\0';
315
316 /*
317 * Lookup the printer and instance and make it the default...
318 */
319
320 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
321 dest->is_default = 1;
322 }
323 else
324 {
325 /*
326 * This initialization of "instance" is unnecessary, but avoids a
327 * compiler warning...
328 */
329
330 instance = NULL;
331 }
332
333 /*
334 * Load the /etc/cups/lpoptions and ~/.lpoptions files...
335 */
336
337 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
338 num_dests = cups_get_dests(filename, num_dests, dests);
339
340 if ((home = getenv("HOME")) != NULL)
341 {
342 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
343 num_dests = cups_get_dests(filename, num_dests, dests);
344 }
345
346 /*
347 * Validate the current default destination - this prevents old
348 * Default lines in /etc/cups/lpoptions and ~/.lpoptions from
349 * pointing to a non-existent printer or class...
350 */
351
352 if (num_reals)
353 {
354 /*
355 * See if we have a default printer...
356 */
357
358 if ((dest = cupsGetDest(NULL, NULL, num_dests, *dests)) != NULL)
359 {
360 /*
361 * Have a default; see if it is real...
362 */
363
364 dest = cupsGetDest(dest->name, NULL, num_reals, reals);
365 }
366
367 /*
368 * If dest is NULL, then no default (that exists) is set, so we
369 * need to set a default if one exists...
370 */
371
372 if (dest == NULL && defprinter != NULL)
373 {
374 for (i = 0; i < num_dests; i ++)
375 (*dests)[i].is_default = 0;
376
377 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) != NULL)
378 dest->is_default = 1;
379 }
380
381 /*
382 * Free memory...
383 */
384
385 free(reals);
386 }
387
388 /*
389 * Return the number of destinations...
390 */
391
392 return (num_dests);
393 }
394
395
396 /*
397 * 'cupsSetDests()' - Save the list of destinations for the default server.
398 *
399 * This function saves the destinations to /etc/cups/lpoptions when run
400 * as root and ~/.lpoptions when run as a normal user.
401 */
402
403 void
404 cupsSetDests(int num_dests, /* I - Number of destinations */
405 cups_dest_t *dests) /* I - Destinations */
406 {
407 http_t *http; /* HTTP connection */
408
409
410 /*
411 * Connect to the CUPS server and save the destination list and options...
412 */
413
414 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
415
416 cupsSetDests2(http, num_dests, dests);
417
418 if (http)
419 httpClose(http);
420 }
421
422
423 /*
424 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
425 *
426 * This function saves the destinations to /etc/cups/lpoptions when run
427 * as root and ~/.lpoptions when run as a normal user.
428 *
429 * @since CUPS 1.1.21@
430 */
431
432 int /* O - 0 on success, -1 on error */
433 cupsSetDests2(http_t *http, /* I - HTTP connection */
434 int num_dests, /* I - Number of destinations */
435 cups_dest_t *dests) /* I - Destinations */
436 {
437 int i, j; /* Looping vars */
438 int wrote; /* Wrote definition? */
439 cups_dest_t *dest; /* Current destination */
440 cups_option_t *option; /* Current option */
441 FILE *fp; /* File pointer */
442 const char *home; /* HOME environment variable */
443 char filename[1024]; /* lpoptions file */
444 int num_temps; /* Number of temporary destinations */
445 cups_dest_t *temps, /* Temporary destinations */
446 *temp; /* Current temporary dest */
447 const char *val; /* Value of temporary option */
448 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
449
450
451 /*
452 * Range check the input...
453 */
454
455 if (!http || !num_dests || !dests)
456 return (-1);
457
458 /*
459 * Get the server destinations...
460 */
461
462 num_temps = cups_get_sdests(http, CUPS_GET_PRINTERS, 0, &temps);
463 num_temps = cups_get_sdests(http, CUPS_GET_CLASSES, num_temps, &temps);
464
465 /*
466 * Figure out which file to write to...
467 */
468
469 snprintf(filename, sizeof(filename), "%s/lpoptions", cg->cups_serverroot);
470
471 #ifndef WIN32
472 if (getuid())
473 {
474 /*
475 * Merge in server defaults...
476 */
477
478 num_temps = cups_get_dests(filename, num_temps, &temps);
479
480 /*
481 * Point to user defaults...
482 */
483
484 if ((home = getenv("HOME")) != NULL)
485 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
486 }
487 #endif /* !WIN32 */
488
489 /*
490 * Try to open the file...
491 */
492
493 if ((fp = fopen(filename, "w")) == NULL)
494 {
495 cupsFreeDests(num_temps, temps);
496 return (-1);
497 }
498
499 /*
500 * Write each printer; each line looks like:
501 *
502 * Dest name[/instance] options
503 * Default name[/instance] options
504 */
505
506 for (i = num_dests, dest = dests; i > 0; i --, dest ++)
507 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
508 {
509 if (dest->is_default)
510 {
511 fprintf(fp, "Default %s", dest->name);
512 if (dest->instance)
513 fprintf(fp, "/%s", dest->instance);
514
515 wrote = 1;
516 }
517 else
518 wrote = 0;
519
520 if ((temp = cupsGetDest(dest->name, dest->instance, num_temps, temps)) == NULL)
521 temp = cupsGetDest(dest->name, NULL, num_temps, temps);
522
523 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
524 {
525 /*
526 * See if the server/global options match these; if so, don't
527 * write 'em.
528 */
529
530 if (temp && (val = cupsGetOption(option->name, temp->num_options,
531 temp->options)) != NULL)
532 {
533 if (strcasecmp(val, option->value) == 0)
534 continue;
535 }
536
537 /*
538 * Options don't match, write to the file...
539 */
540
541 if (!wrote)
542 {
543 fprintf(fp, "Dest %s", dest->name);
544 if (dest->instance)
545 fprintf(fp, "/%s", dest->instance);
546 wrote = 1;
547 }
548
549 if (option->value[0])
550 {
551 if (strchr(option->value, ' ') != NULL)
552 fprintf(fp, " %s=\"%s\"", option->name, option->value);
553 else
554 fprintf(fp, " %s=%s", option->name, option->value);
555 }
556 else
557 fprintf(fp, " %s", option->name);
558 }
559
560 if (wrote)
561 fputs("\n", fp);
562 }
563
564 /*
565 * Free the temporary destinations...
566 */
567
568 cupsFreeDests(num_temps, temps);
569
570 /*
571 * Close the file and return...
572 */
573
574 fclose(fp);
575
576 return (0);
577 }
578
579
580 /*
581 * 'cups_get_dests()' - Get destinations from a file.
582 */
583
584 static int /* O - Number of destinations */
585 cups_get_dests(const char *filename, /* I - File to read from */
586 int num_dests, /* I - Number of destinations */
587 cups_dest_t **dests) /* IO - Destinations */
588 {
589 int i; /* Looping var */
590 cups_dest_t *dest; /* Current destination */
591 FILE *fp; /* File pointer */
592 char line[8192], /* Line from file */
593 *lineptr, /* Pointer into line */
594 *name, /* Name of destination/option */
595 *instance; /* Instance of destination */
596 const char *printer; /* PRINTER or LPDEST */
597
598
599 /*
600 * Check environment variables...
601 */
602
603 if ((printer = getenv("LPDEST")) == NULL)
604 if ((printer = getenv("PRINTER")) != NULL)
605 if (strcmp(printer, "lp") == 0)
606 printer = NULL;
607
608 /*
609 * Try to open the file...
610 */
611
612 if ((fp = fopen(filename, "r")) == NULL)
613 return (num_dests);
614
615 /*
616 * Read each printer; each line looks like:
617 *
618 * Dest name[/instance] options
619 * Default name[/instance] options
620 */
621
622 while (fgets(line, sizeof(line), fp) != NULL)
623 {
624 /*
625 * See what type of line it is...
626 */
627
628 if (strncasecmp(line, "dest", 4) == 0 && isspace(line[4] & 255))
629 lineptr = line + 4;
630 else if (strncasecmp(line, "default", 7) == 0 && isspace(line[7] & 255))
631 lineptr = line + 7;
632 else
633 continue;
634
635 /*
636 * Skip leading whitespace...
637 */
638
639 while (isspace(*lineptr & 255))
640 lineptr ++;
641
642 if (!*lineptr)
643 continue;
644
645 name = lineptr;
646
647 /*
648 * Search for an instance...
649 */
650
651 while (!isspace(*lineptr & 255) && *lineptr && *lineptr != '/')
652 lineptr ++;
653
654 if (!*lineptr)
655 continue;
656
657 if (*lineptr == '/')
658 {
659 /*
660 * Found an instance...
661 */
662
663 *lineptr++ = '\0';
664 instance = lineptr;
665
666 /*
667 * Search for an instance...
668 */
669
670 while (!isspace(*lineptr & 255) && *lineptr)
671 lineptr ++;
672 }
673 else
674 instance = NULL;
675
676 *lineptr++ = '\0';
677
678 /*
679 * See if the primary instance of the destination exists; if not,
680 * ignore this entry and move on...
681 */
682
683 if (cupsGetDest(name, NULL, num_dests, *dests) == NULL)
684 continue;
685
686 /*
687 * Add the destination...
688 */
689
690 num_dests = cupsAddDest(name, instance, num_dests, dests);
691
692 if ((dest = cupsGetDest(name, instance, num_dests, *dests)) == NULL)
693 {
694 /*
695 * Out of memory!
696 */
697
698 fclose(fp);
699 return (num_dests);
700 }
701
702 /*
703 * Add options until we hit the end of the line...
704 */
705
706 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
707 &(dest->options));
708
709 /*
710 * Set this as default if needed...
711 */
712
713 if (strncasecmp(line, "default", 7) == 0 && printer == NULL)
714 {
715 for (i = 0; i < num_dests; i ++)
716 (*dests)[i].is_default = 0;
717
718 dest->is_default = 1;
719 }
720 }
721
722 /*
723 * Close the file and return...
724 */
725
726 fclose(fp);
727
728 return (num_dests);
729 }
730
731
732 /*
733 * 'cups_get_sdests()' - Get destinations from a server.
734 */
735
736 static int /* O - Number of destinations */
737 cups_get_sdests(http_t *http, /* I - HTTP connection */
738 ipp_op_t op, /* I - get-printers or get-classes */
739 int num_dests, /* I - Number of destinations */
740 cups_dest_t **dests) /* IO - Destinations */
741 {
742 cups_dest_t *dest; /* Current destination */
743 ipp_t *request, /* IPP Request */
744 *response; /* IPP Response */
745 ipp_attribute_t *attr; /* Current attribute */
746 cups_lang_t *language; /* Default language */
747 const char *name; /* printer-name attribute */
748 char job_sheets[1024]; /* job-sheets option */
749 static const char * const pattrs[] = /* Attributes we're interested in */
750 {
751 "printer-name",
752 "job-sheets-default"
753 };
754
755
756 /*
757 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
758 * the following attributes:
759 *
760 * attributes-charset
761 * attributes-natural-language
762 */
763
764 request = ippNew();
765
766 request->request.op.operation_id = op;
767 request->request.op.request_id = 1;
768
769 language = cupsLangDefault();
770
771 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
772 "attributes-charset", NULL, cupsLangEncoding(language));
773
774 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
775 "attributes-natural-language", NULL, language->language);
776
777 cupsLangFree(language);
778
779 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
780 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
781 NULL, pattrs);
782
783 /*
784 * Do the request and get back a response...
785 */
786
787 if ((response = cupsDoRequest(http, request, "/")) != NULL)
788 {
789 for (attr = response->attrs; attr != NULL; attr = attr->next)
790 {
791 /*
792 * Skip leading attributes until we hit a printer...
793 */
794
795 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
796 attr = attr->next;
797
798 if (attr == NULL)
799 break;
800
801 /*
802 * Pull the needed attributes from this job...
803 */
804
805 name = NULL;
806
807 strcpy(job_sheets, "");
808
809 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
810 {
811 if (strcmp(attr->name, "printer-name") == 0 &&
812 attr->value_tag == IPP_TAG_NAME)
813 name = attr->values[0].string.text;
814
815 if (strcmp(attr->name, "job-sheets-default") == 0 &&
816 (attr->value_tag == IPP_TAG_KEYWORD ||
817 attr->value_tag == IPP_TAG_NAME))
818 {
819 if (attr->num_values == 2)
820 snprintf(job_sheets, sizeof(job_sheets), "%s,%s",
821 attr->values[0].string.text, attr->values[1].string.text);
822 else
823 strcpy(job_sheets, attr->values[0].string.text);
824 }
825
826 attr = attr->next;
827 }
828
829 /*
830 * See if we have everything needed...
831 */
832
833 if (!name)
834 {
835 if (attr == NULL)
836 break;
837 else
838 continue;
839 }
840
841 num_dests = cupsAddDest(name, NULL, num_dests, dests);
842
843 if ((dest = cupsGetDest(name, NULL, num_dests, *dests)) != NULL)
844 if (job_sheets[0])
845 dest->num_options = cupsAddOption("job-sheets", job_sheets, 0,
846 &(dest->options));
847
848 if (attr == NULL)
849 break;
850 }
851
852 ippDelete(response);
853 }
854
855 /*
856 * Return the count...
857 */
858
859 return (num_dests);
860 }
861
862
863 /*
864 * End of "$Id: dest.c 4918 2006-01-12 05:14:40Z mike $".
865 */