]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
f9cd96e5c0065908ece63e093ca6f5cc41c04172
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
7 * Copyright 1997-2006 by Easy Software Products.
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
16 * Attn: CUPS Licensing Information
17 * Easy Software Products
18 * 44141 Airport View Drive, Suite 204
19 * Hollywood, Maryland 20636 USA
21 * Voice: (301) 373-9600
22 * EMail: cups-info@cups.org
23 * WWW: http://www.cups.org
25 * This file is subject to the Apple OS-Developed Software exception.
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.
41 * Include necessary headers...
50 #endif /* HAVE_NOTIFY_H */
57 static int cups_get_dests(const char *filename
, int num_dests
,
59 static int cups_get_sdests(http_t
*http
, ipp_op_t op
, int num_dests
,
64 * 'cupsAddDest()' - Add a destination to the list of destinations.
66 * Use the cupsSaveDests() function to save the updated list of destinations
67 * to the user's lpoptions file.
70 int /* O - New number of destinations */
71 cupsAddDest(const char *name
, /* I - Name of destination */
72 const char *instance
, /* I - Instance of destination or NULL for none/primary */
73 int num_dests
, /* I - Number of destinations */
74 cups_dest_t
**dests
) /* IO - Destinations */
76 int i
; /* Looping var */
77 cups_dest_t
*dest
; /* Destination pointer */
80 if (name
== NULL
|| dests
== NULL
)
83 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
87 * Add new destination...
91 dest
= malloc(sizeof(cups_dest_t
));
93 dest
= realloc(*dests
, sizeof(cups_dest_t
) * (num_dests
+ 1));
100 for (i
= num_dests
; i
> 0; i
--, dest
++)
101 if (strcasecmp(name
, dest
->name
) < 0)
103 else if (strcasecmp(name
, dest
->name
) == 0 &&
104 instance
!= NULL
&& dest
->instance
!= NULL
&&
105 strcasecmp(instance
, dest
->instance
) < 0)
109 memmove(dest
+ 1, dest
, i
* sizeof(cups_dest_t
));
111 dest
->name
= strdup(name
);
112 dest
->is_default
= 0;
113 dest
->num_options
= 0;
114 dest
->options
= (cups_option_t
*)0;
116 if (instance
== NULL
)
117 dest
->instance
= NULL
;
119 dest
->instance
= strdup(instance
);
121 return (num_dests
+ 1);
126 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
130 cupsFreeDests(int num_dests
, /* I - Number of destinations */
131 cups_dest_t
*dests
) /* I - Destinations */
133 int i
; /* Looping var */
134 cups_dest_t
*dest
; /* Current destination */
137 if (num_dests
== 0 || dests
== NULL
)
140 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
145 free(dest
->instance
);
147 cupsFreeOptions(dest
->num_options
, dest
->options
);
155 * 'cupsGetDest()' - Get the named destination from the list.
157 * Use the cupsGetDests() or cupsGetDests2() functions to get a
158 * list of supported destinations for the current user.
161 cups_dest_t
* /* O - Destination pointer or NULL */
162 cupsGetDest(const char *name
, /* I - Name of destination */
163 const char *instance
, /* I - Instance of destination */
164 int num_dests
, /* I - Number of destinations */
165 cups_dest_t
*dests
) /* I - Destinations */
167 int comp
; /* Result of comparison */
170 if (num_dests
== 0 || dests
== NULL
)
176 * NULL name for default printer.
179 while (num_dests
> 0)
181 if (dests
->is_default
)
191 * Lookup name and optionally the instance...
194 while (num_dests
> 0)
196 if ((comp
= strcasecmp(name
, dests
->name
)) < 0)
200 if ((instance
== NULL
&& dests
->instance
== NULL
) ||
201 (instance
!= NULL
&& dests
->instance
!= NULL
&&
202 strcasecmp(instance
, dests
->instance
) == 0))
216 * 'cupsGetDests()' - Get the list of destinations from the default server.
219 int /* O - Number of destinations */
220 cupsGetDests(cups_dest_t
**dests
) /* O - Destinations */
222 int num_dests
; /* Number of destinations */
223 http_t
*http
; /* HTTP connection */
227 * Connect to the CUPS server and get the destination list and options...
230 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
232 num_dests
= cupsGetDests2(http
, dests
);
242 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
244 * @since CUPS 1.1.21@
247 int /* O - Number of destinations */
248 cupsGetDests2(http_t
*http
, /* I - HTTP connection */
249 cups_dest_t
**dests
) /* O - Destinations */
251 int i
; /* Looping var */
252 int num_dests
; /* Number of destinations */
253 cups_dest_t
*dest
; /* Destination pointer */
254 const char *home
; /* HOME environment variable */
255 char filename
[1024]; /* Local ~/.lpoptions file */
256 const char *defprinter
; /* Default printer */
257 char name
[1024], /* Copy of printer name */
258 *instance
; /* Pointer to instance name */
259 int num_reals
; /* Number of real queues */
260 cups_dest_t
*reals
; /* Real queues */
261 _cups_globals_t
*cg
= _cupsGlobals(); /* Global data */
265 * Range check the input...
272 * Initialize destination array...
276 *dests
= (cups_dest_t
*)0;
279 * Grab the printers and classes...
282 num_dests
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, num_dests
, dests
);
283 num_dests
= cups_get_sdests(http
, CUPS_GET_CLASSES
, num_dests
, dests
);
286 * Make a copy of the "real" queues for a later sanity check...
291 num_reals
= num_dests
;
292 reals
= calloc(num_reals
, sizeof(cups_dest_t
));
295 memcpy(reals
, *dests
, num_reals
* sizeof(cups_dest_t
));
306 * Grab the default destination...
309 if ((defprinter
= cupsGetDefault2(http
)) != NULL
)
312 * Grab printer and instance name...
315 strlcpy(name
, defprinter
, sizeof(name
));
317 if ((instance
= strchr(name
, '/')) != NULL
)
321 * Lookup the printer and instance and make it the default...
324 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
325 dest
->is_default
= 1;
330 * This initialization of "instance" is unnecessary, but avoids a
331 * compiler warning...
338 * Load the /etc/cups/lpoptions and ~/.lpoptions files...
341 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
342 num_dests
= cups_get_dests(filename
, num_dests
, dests
);
344 if ((home
= getenv("HOME")) != NULL
)
346 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
347 num_dests
= cups_get_dests(filename
, num_dests
, dests
);
351 * Validate the current default destination - this prevents old
352 * Default lines in /etc/cups/lpoptions and ~/.lpoptions from
353 * pointing to a non-existent printer or class...
359 * See if we have a default printer...
362 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, *dests
)) != NULL
)
365 * Have a default; see if it is real...
368 dest
= cupsGetDest(dest
->name
, NULL
, num_reals
, reals
);
372 * If dest is NULL, then no default (that exists) is set, so we
373 * need to set a default if one exists...
376 if (dest
== NULL
&& defprinter
!= NULL
)
378 for (i
= 0; i
< num_dests
; i
++)
379 (*dests
)[i
].is_default
= 0;
381 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
382 dest
->is_default
= 1;
393 * Return the number of destinations...
401 * 'cupsSetDests()' - Save the list of destinations for the default server.
403 * This function saves the destinations to /etc/cups/lpoptions when run
404 * as root and ~/.lpoptions when run as a normal user.
408 cupsSetDests(int num_dests
, /* I - Number of destinations */
409 cups_dest_t
*dests
) /* I - Destinations */
411 http_t
*http
; /* HTTP connection */
415 * Connect to the CUPS server and save the destination list and options...
418 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
420 cupsSetDests2(http
, num_dests
, dests
);
428 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
430 * This function saves the destinations to /etc/cups/lpoptions when run
431 * as root and ~/.lpoptions when run as a normal user.
433 * @since CUPS 1.1.21@
436 int /* O - 0 on success, -1 on error */
437 cupsSetDests2(http_t
*http
, /* I - HTTP connection */
438 int num_dests
, /* I - Number of destinations */
439 cups_dest_t
*dests
) /* I - Destinations */
441 int i
, j
; /* Looping vars */
442 int wrote
; /* Wrote definition? */
443 cups_dest_t
*dest
; /* Current destination */
444 cups_option_t
*option
; /* Current option */
445 FILE *fp
; /* File pointer */
446 const char *home
; /* HOME environment variable */
447 char filename
[1024]; /* lpoptions file */
448 int num_temps
; /* Number of temporary destinations */
449 cups_dest_t
*temps
, /* Temporary destinations */
450 *temp
; /* Current temporary dest */
451 const char *val
; /* Value of temporary option */
452 _cups_globals_t
*cg
= _cupsGlobals(); /* Global data */
456 * Range check the input...
459 if (!http
|| !num_dests
|| !dests
)
463 * Get the server destinations...
466 num_temps
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, 0, &temps
);
467 num_temps
= cups_get_sdests(http
, CUPS_GET_CLASSES
, num_temps
, &temps
);
470 * Figure out which file to write to...
473 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
479 * Merge in server defaults...
482 num_temps
= cups_get_dests(filename
, num_temps
, &temps
);
485 * Point to user defaults...
488 if ((home
= getenv("HOME")) != NULL
)
489 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
494 * Try to open the file...
497 if ((fp
= fopen(filename
, "w")) == NULL
)
499 cupsFreeDests(num_temps
, temps
);
504 * Write each printer; each line looks like:
506 * Dest name[/instance] options
507 * Default name[/instance] options
510 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
511 if (dest
->instance
!= NULL
|| dest
->num_options
!= 0 || dest
->is_default
)
513 if (dest
->is_default
)
515 fprintf(fp
, "Default %s", dest
->name
);
517 fprintf(fp
, "/%s", dest
->instance
);
524 if ((temp
= cupsGetDest(dest
->name
, dest
->instance
, num_temps
, temps
)) == NULL
)
525 temp
= cupsGetDest(dest
->name
, NULL
, num_temps
, temps
);
527 for (j
= dest
->num_options
, option
= dest
->options
; j
> 0; j
--, option
++)
530 * See if the server/global options match these; if so, don't
534 if (temp
&& (val
= cupsGetOption(option
->name
, temp
->num_options
,
535 temp
->options
)) != NULL
)
537 if (strcasecmp(val
, option
->value
) == 0)
542 * Options don't match, write to the file...
547 fprintf(fp
, "Dest %s", dest
->name
);
549 fprintf(fp
, "/%s", dest
->instance
);
553 if (option
->value
[0])
555 if (strchr(option
->value
, ' ') != NULL
)
556 fprintf(fp
, " %s=\"%s\"", option
->name
, option
->value
);
558 fprintf(fp
, " %s=%s", option
->name
, option
->value
);
561 fprintf(fp
, " %s", option
->name
);
569 * Free the temporary destinations and close the file...
572 cupsFreeDests(num_temps
, temps
);
576 #ifdef HAVE_NOTIFY_POST
578 * Send a notification so that MacOS X applications can know about the
582 notify_post("com.apple.printerListChange");
583 #endif /* HAVE_NOTIFY_POST */
590 * 'cups_get_dests()' - Get destinations from a file.
593 static int /* O - Number of destinations */
594 cups_get_dests(const char *filename
, /* I - File to read from */
595 int num_dests
, /* I - Number of destinations */
596 cups_dest_t
**dests
) /* IO - Destinations */
598 int i
; /* Looping var */
599 cups_dest_t
*dest
; /* Current destination */
600 FILE *fp
; /* File pointer */
601 char line
[8192], /* Line from file */
602 *lineptr
, /* Pointer into line */
603 *name
, /* Name of destination/option */
604 *instance
; /* Instance of destination */
605 const char *printer
; /* PRINTER or LPDEST */
609 * Check environment variables...
612 if ((printer
= getenv("LPDEST")) == NULL
)
613 if ((printer
= getenv("PRINTER")) != NULL
)
614 if (strcmp(printer
, "lp") == 0)
618 * Try to open the file...
621 if ((fp
= fopen(filename
, "r")) == NULL
)
625 * Read each printer; each line looks like:
627 * Dest name[/instance] options
628 * Default name[/instance] options
631 while (fgets(line
, sizeof(line
), fp
) != NULL
)
634 * See what type of line it is...
637 if (strncasecmp(line
, "dest", 4) == 0 && isspace(line
[4] & 255))
639 else if (strncasecmp(line
, "default", 7) == 0 && isspace(line
[7] & 255))
645 * Skip leading whitespace...
648 while (isspace(*lineptr
& 255))
657 * Search for an instance...
660 while (!isspace(*lineptr
& 255) && *lineptr
&& *lineptr
!= '/')
669 * Found an instance...
676 * Search for an instance...
679 while (!isspace(*lineptr
& 255) && *lineptr
)
688 * See if the primary instance of the destination exists; if not,
689 * ignore this entry and move on...
692 if (cupsGetDest(name
, NULL
, num_dests
, *dests
) == NULL
)
696 * Add the destination...
699 num_dests
= cupsAddDest(name
, instance
, num_dests
, dests
);
701 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
712 * Add options until we hit the end of the line...
715 dest
->num_options
= cupsParseOptions(lineptr
, dest
->num_options
,
719 * Set this as default if needed...
722 if (strncasecmp(line
, "default", 7) == 0 && printer
== NULL
)
724 for (i
= 0; i
< num_dests
; i
++)
725 (*dests
)[i
].is_default
= 0;
727 dest
->is_default
= 1;
732 * Close the file and return...
742 * 'cups_get_sdests()' - Get destinations from a server.
745 static int /* O - Number of destinations */
746 cups_get_sdests(http_t
*http
, /* I - HTTP connection */
747 ipp_op_t op
, /* I - get-printers or get-classes */
748 int num_dests
, /* I - Number of destinations */
749 cups_dest_t
**dests
) /* IO - Destinations */
751 int i
; /* Looping var */
752 cups_dest_t
*dest
; /* Current destination */
753 ipp_t
*request
, /* IPP Request */
754 *response
; /* IPP Response */
755 ipp_attribute_t
*attr
; /* Current attribute */
756 int accepting
, /* printer-is-accepting-jobs attribute */
757 shared
, /* printer-is-shared attribute */
758 state
, /* printer-state attribute */
759 change_time
, /* printer-state-change-time attribute */
760 type
; /* printer-type attribute */
761 const char *info
, /* printer-info attribute */
762 *make_model
, /* printer-make-and-model attribute */
763 *name
; /* printer-name attribute */
764 char job_sheets
[1024], /* job-sheets option */
765 reasons
[1024], /* printer-state-reasons attribute */
766 *rptr
, /* Pointer into reasons string */
767 temp
[255]; /* Temporary string for numbers */
768 static const char * const pattrs
[] = /* Attributes we're interested in */
770 "job-sheets-default",
772 "printer-is-accepting-jobs",
774 "printer-make-and-model",
777 "printer-state-change-time",
778 "printer-state-reasons",
784 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
785 * the following attributes:
788 * attributes-natural-language
789 * requesting-user-name
792 request
= ippNewRequest(op
);
794 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
795 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
798 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
799 "requesting-user-name", NULL
, cupsUser());
802 * Do the request and get back a response...
805 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
807 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
810 * Skip leading attributes until we hit a printer...
813 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
820 * Pull the needed attributes from this job...
829 state
= IPP_PRINTER_IDLE
;
830 type
= CUPS_PRINTER_LOCAL
;
832 strcpy(job_sheets
, "");
835 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_PRINTER
)
837 if (!strcmp(attr
->name
, "job-sheets-default") &&
838 (attr
->value_tag
== IPP_TAG_KEYWORD
||
839 attr
->value_tag
== IPP_TAG_NAME
))
841 if (attr
->num_values
== 2)
842 snprintf(job_sheets
, sizeof(job_sheets
), "%s,%s",
843 attr
->values
[0].string
.text
, attr
->values
[1].string
.text
);
845 strlcpy(job_sheets
, attr
->values
[0].string
.text
,
848 else if (!strcmp(attr
->name
, "printer-info") &&
849 attr
->value_tag
== IPP_TAG_TEXT
)
850 info
= attr
->values
[0].string
.text
;
851 else if (!strcmp(attr
->name
, "printer-is-accepting-jobs") &&
852 attr
->value_tag
== IPP_TAG_BOOLEAN
)
853 accepting
= attr
->values
[0].boolean
;
854 else if (!strcmp(attr
->name
, "printer-is-shared") &&
855 attr
->value_tag
== IPP_TAG_BOOLEAN
)
856 shared
= attr
->values
[0].boolean
;
857 else if (!strcmp(attr
->name
, "printer-make-and-model") &&
858 attr
->value_tag
== IPP_TAG_TEXT
)
859 make_model
= attr
->values
[0].string
.text
;
860 else if (!strcmp(attr
->name
, "printer-name") &&
861 attr
->value_tag
== IPP_TAG_NAME
)
862 name
= attr
->values
[0].string
.text
;
863 else if (!strcmp(attr
->name
, "printer-state") &&
864 attr
->value_tag
== IPP_TAG_ENUM
)
865 state
= attr
->values
[0].integer
;
866 else if (!strcmp(attr
->name
, "printer-state-change-time") &&
867 attr
->value_tag
== IPP_TAG_INTEGER
)
868 change_time
= attr
->values
[0].integer
;
869 else if (!strcmp(attr
->name
, "printer-state-reasons") &&
870 attr
->value_tag
== IPP_TAG_KEYWORD
)
872 strlcpy(reasons
, attr
->values
[0].string
.text
, sizeof(reasons
));
873 for (i
= 1, rptr
= reasons
+ strlen(reasons
);
874 i
< attr
->num_values
;
877 snprintf(rptr
, sizeof(reasons
) - (rptr
- reasons
), ",%s",
878 attr
->values
[i
].string
.text
);
879 rptr
+= strlen(rptr
);
882 else if (!strcmp(attr
->name
, "printer-type") &&
883 attr
->value_tag
== IPP_TAG_ENUM
)
884 type
= attr
->values
[0].integer
;
890 * See if we have everything needed...
901 num_dests
= cupsAddDest(name
, NULL
, num_dests
, dests
);
903 if ((dest
= cupsGetDest(name
, NULL
, num_dests
, *dests
)) != NULL
)
906 dest
->num_options
= cupsAddOption("job-sheets", job_sheets
,
911 dest
->num_options
= cupsAddOption("printer-info", info
,
915 sprintf(temp
, "%d", accepting
);
916 dest
->num_options
= cupsAddOption("printer-is-accepting-jobs", temp
,
920 sprintf(temp
, "%d", shared
);
921 dest
->num_options
= cupsAddOption("printer-is-shared", temp
,
926 dest
->num_options
= cupsAddOption("printer-make-and-model",
927 make_model
, dest
->num_options
,
930 sprintf(temp
, "%d", state
);
931 dest
->num_options
= cupsAddOption("printer-state", temp
,
937 sprintf(temp
, "%d", change_time
);
938 dest
->num_options
= cupsAddOption("printer-state-change-time", temp
,
944 dest
->num_options
= cupsAddOption("printer-state-reasons", reasons
,
948 sprintf(temp
, "%d", type
);
949 dest
->num_options
= cupsAddOption("printer-type", temp
,
962 * Return the count...