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