]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/dest.c
Mirror 1.1.x changes...
[thirdparty/cups.git] / cups / dest.c
CommitLineData
bf23e338 1/*
6db7190f 2 * "$Id: dest.c,v 1.18.2.12 2003/01/24 20:45:10 mike Exp $"
bf23e338 3 *
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
6 *
1d9595ab 7 * Copyright 1997-2003 by Easy Software Products.
bf23e338 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 *
dab1a4d8 25 * This file is subject to the Apple OS-Developed Software exception.
26 *
bf23e338 27 * Contents:
28 *
07c5adda 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.
bf23e338 36 */
37
38/*
39 * Include necessary headers...
40 */
41
42#include "cups.h"
07c5adda 43#include "language.h"
f4671380 44#include "string.h"
45#include <stdlib.h>
8a2c2126 46#include <ctype.h>
bf23e338 47
48
49/*
50 * Local functions...
51 */
52
53static int cups_get_dests(const char *filename, int num_dests,
54 cups_dest_t **dests);
07c5adda 55static int cups_get_sdests(ipp_op_t op, int num_dests,
56 cups_dest_t **dests);
bf23e338 57
58
59/*
60 * 'cupsAddDest()' - Add a destination to the list of destinations.
61 */
62
f4671380 63int /* O - New number of destinations */
64cupsAddDest(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 */
bf23e338 68{
f4671380 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;
29134920 96 else if (strcasecmp(name, dest->name) == 0 &&
97 instance != NULL && dest->instance != NULL &&
f4671380 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);
bf23e338 115}
116
117
118/*
119 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
120 */
121
122void
f4671380 123cupsFreeDests(int num_dests, /* I - Number of destinations */
124 cups_dest_t *dests) /* I - Destinations */
bf23e338 125{
f4671380 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);
bf23e338 144}
145
146
147/*
148 * 'cupsGetDest()' - Get the named destination from the list.
149 */
150
f4671380 151cups_dest_t * /* O - Destination pointer or NULL */
152cupsGetDest(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 */
bf23e338 156{
f4671380 157 int comp; /* Result of comparison */
158
159
7bfde0bb 160 if (num_dests == 0 || dests == NULL)
f4671380 161 return (NULL);
162
7bfde0bb 163 if (name == NULL)
f4671380 164 {
7bfde0bb 165 /*
166 * NULL name for default printer.
167 */
168
169 while (num_dests > 0)
f4671380 170 {
7bfde0bb 171 if (dests->is_default)
172 return (dests);
173
174 num_dests --;
175 dests ++;
f4671380 176 }
7bfde0bb 177 }
178 else
179 {
180 /*
181 * Lookup name and optionally the instance...
182 */
f4671380 183
7bfde0bb 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 }
f4671380 199 }
200
201 return (NULL);
bf23e338 202}
203
204
205/*
206 * 'cupsGetDests()' - Get the list of destinations.
207 */
208
209int /* O - Number of destinations */
210cupsGetDests(cups_dest_t **dests) /* O - Destinations */
211{
9ae34eb7 212 int i; /* Looping var */
bf23e338 213 int num_dests; /* Number of destinations */
bf23e338 214 cups_dest_t *dest; /* Destination pointer */
f4671380 215 const char *home; /* HOME environment variable */
216 char filename[1024]; /* Local ~/.lpoptions file */
37ac5c5e 217 const char *defprinter; /* Default printer */
218 char name[1024], /* Copy of printer name */
219 *instance; /* Pointer to instance name */
9ae34eb7 220 int num_reals; /* Number of real queues */
221 cups_dest_t *reals; /* Real queues */
bf23e338 222
223
224 /*
225 * Initialize destination array...
226 */
227
228 num_dests = 0;
229 *dests = (cups_dest_t *)0;
230
231 /*
07c5adda 232 * Grab the printers and classes...
bf23e338 233 */
234
07c5adda 235 num_dests = cups_get_sdests(CUPS_GET_PRINTERS, num_dests, dests);
236 num_dests = cups_get_sdests(CUPS_GET_CLASSES, num_dests, dests);
bf23e338 237
9ae34eb7 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
bf23e338 258 /*
259 * Grab the default destination...
260 */
261
37ac5c5e 262 if ((defprinter = cupsGetDefault()) != NULL)
263 {
264 /*
265 * Grab printer and instance name...
266 */
267
def978d5 268 strlcpy(name, defprinter, sizeof(name));
37ac5c5e 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 }
753453e4 280 else
281 {
282 /*
283 * This initialization of "instance" is unnecessary, but avoids a
284 * compiler warning...
285 */
286
287 instance = NULL;
288 }
bf23e338 289
290 /*
291 * Load the /etc/cups/lpoptions and ~/.lpoptions files...
292 */
293
3e3712a4 294 if ((home = getenv("CUPS_SERVERROOT")) != NULL)
295 {
04de52f8 296 snprintf(filename, sizeof(filename), "%s/lpoptions", home);
3e3712a4 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);
f4671380 301
302 if ((home = getenv("HOME")) != NULL)
303 {
04de52f8 304 snprintf(filename, sizeof(filename), "%s/.lpoptions", home);
f4671380 305 num_dests = cups_get_dests(filename, num_dests, dests);
306 }
bf23e338 307
753453e4 308 /*
9ae34eb7 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...
753453e4 312 */
313
9ae34eb7 314 if (num_reals)
753453e4 315 {
316 /*
9ae34eb7 317 * See if we have a default printer...
753453e4 318 */
319
9ae34eb7 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);
753453e4 348 }
349
f4671380 350 /*
351 * Return the number of destinations...
352 */
353
354 return (num_dests);
bf23e338 355}
356
357
358/*
359 * 'cupsSetDests()' - Set the list of destinations.
360 */
361
362void
f4671380 363cupsSetDests(int num_dests, /* I - Number of destinations */
364 cups_dest_t *dests) /* I - Destinations */
bf23e338 365{
f4671380 366 int i, j; /* Looping vars */
07c5adda 367 int wrote; /* Wrote definition? */
f4671380 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 */
07c5adda 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 */
f4671380 382
07c5adda 383 num_temps = cups_get_sdests(CUPS_GET_PRINTERS, 0, &temps);
384 num_temps = cups_get_sdests(CUPS_GET_CLASSES, num_temps, &temps);
f4671380 385
386 /*
387 * Figure out which file to write to...
388 */
389
07c5adda 390 if ((home = getenv("CUPS_SERVERROOT")) != NULL)
391 snprintf(filename, sizeof(filename), "%s/lpoptions", home);
392 else
393 strcpy(filename, CUPS_SERVERROOT "/lpoptions");
7ad5f9ca 394
07c5adda 395#ifndef WIN32
396 if (getuid())
3e3712a4 397 {
07c5adda 398 /*
399 * Merge in server defaults...
400 */
a6988fb1 401
07c5adda 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);
3e3712a4 410 }
07c5adda 411#endif /* !WIN32 */
f4671380 412
413 /*
414 * Try to open the file...
415 */
416
417 if ((fp = fopen(filename, "w")) == NULL)
07c5adda 418 {
419 cupsFreeDests(num_temps, temps);
f4671380 420 return;
07c5adda 421 }
f4671380 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 ++)
db504ff1 431 if (dest->instance != NULL || dest->num_options != 0 || dest->is_default)
f4671380 432 {
07c5adda 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);
f4671380 446
447 for (j = dest->num_options, option = dest->options; j > 0; j --, option ++)
07c5adda 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
9495a093 473 if (option->value[0])
f12f2b91 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 }
9495a093 480 else
481 fprintf(fp, " %s", option->name);
07c5adda 482 }
f4671380 483
07c5adda 484 if (wrote)
485 fputs("\n", fp);
f4671380 486 }
487
07c5adda 488 /*
489 * Free the temporary destinations...
490 */
491
492 cupsFreeDests(num_temps, temps);
493
f4671380 494 /*
495 * Close the file and return...
496 */
497
498 fclose(fp);
bf23e338 499}
500
501
502/*
503 * 'cups_get_dests()' - Get destinations from a file.
504 */
505
506static int /* O - Number of destinations */
507cups_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{
db504ff1 511 int i; /* Looping var */
f4671380 512 cups_dest_t *dest; /* Current destination */
f4671380 513 FILE *fp; /* File pointer */
514 char line[8192], /* Line from file */
515 *lineptr, /* Pointer into line */
516 *name, /* Name of destination/option */
db504ff1 517 *instance; /* Instance of destination */
97aa42aa 518 const char *printer; /* PRINTER or LPDEST */
f4671380 519
520
97aa42aa 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
f4671380 530 /*
531 * Try to open the file...
532 */
533
534 if ((fp = fopen(filename, "r")) == NULL)
db504ff1 535 return (num_dests);
f4671380 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;
db504ff1 552 else if (strncasecmp(line, "default", 7) == 0 && isspace(line[7]))
f4671380 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
c672a858 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
f4671380 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);
db504ff1 621 return (num_dests);
f4671380 622 }
623
624 /*
625 * Add options until we hit the end of the line...
626 */
627
db504ff1 628 dest->num_options = cupsParseOptions(lineptr, dest->num_options,
629 &(dest->options));
f4671380 630
db504ff1 631 /*
632 * Set this as default if needed...
633 */
f4671380 634
97aa42aa 635 if (strncasecmp(line, "default", 7) == 0 && printer == NULL)
db504ff1 636 {
637 for (i = 0; i < num_dests; i ++)
638 (*dests)[i].is_default = 0;
f4671380 639
db504ff1 640 dest->is_default = 1;
f4671380 641 }
642 }
643
644 /*
645 * Close the file and return...
646 */
647
648 fclose(fp);
1e907b5a 649
650 return (num_dests);
bf23e338 651}
652
653
654/*
753453e4 655 * 'cups_get_sdests()' - Get destinations from a server.
656 */
657
658static int /* O - Number of destinations */
659cups_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 */
6db7190f 671 static const char * const pattrs[] = /* Attributes we're interested in */
753453e4 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
0a3ac972 695 request->request.op.operation_id = op;
696 request->request.op.request_id = 1;
753453e4 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/*
6db7190f 797 * End of "$Id: dest.c,v 1.18.2.12 2003/01/24 20:45:10 mike Exp $".
bf23e338 798 */