]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/dest.c
2 * "$Id: dest.c 6191 2007-01-10 16:48:37Z mike $"
4 * User-defined destination (and option) support for the Common UNIX
5 * Printing System (CUPS).
7 * Copyright 1997-2007 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...
51 #endif /* HAVE_NOTIFY_H */
58 static int cups_get_dests(const char *filename
, int num_dests
,
60 static int cups_get_sdests(http_t
*http
, ipp_op_t op
, int num_dests
,
65 * 'cupsAddDest()' - Add a destination to the list of destinations.
67 * This function cannot be used to add a new class or printer queue,
68 * it only adds a new container of saved options for the named
69 * destination or instance.
71 * If the named destination already exists, the destination list is
72 * returned unchanged. Adding a new instance of a destination creates
73 * a copy of that destination's options.
75 * Use the cupsSaveDests() function to save the updated list of
76 * destinations to the user's lpoptions file.
79 int /* O - New number of destinations */
80 cupsAddDest(const char *name
, /* I - Destination name */
81 const char *instance
, /* I - Instance name or NULL for none/primary */
82 int num_dests
, /* I - Number of destinations */
83 cups_dest_t
**dests
) /* IO - Destinations */
85 int i
; /* Looping var */
86 cups_dest_t
*dest
; /* Destination pointer */
87 cups_dest_t
*parent
; /* Parent destination */
88 cups_option_t
*option
; /* Current option */
94 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
98 * Add new destination...
102 dest
= malloc(sizeof(cups_dest_t
));
104 dest
= realloc(*dests
, sizeof(cups_dest_t
) * (num_dests
+ 1));
112 * Find where to insert the destination...
115 for (i
= num_dests
; i
> 0; i
--, dest
++)
116 if (strcasecmp(name
, dest
->name
) < 0)
118 else if (!instance
&& dest
->instance
)
120 else if (!strcasecmp(name
, dest
->name
) &&
121 instance
&& dest
->instance
&&
122 strcasecmp(instance
, dest
->instance
) < 0)
126 memmove(dest
+ 1, dest
, i
* sizeof(cups_dest_t
));
129 * Initialize the destination...
132 dest
->name
= strdup(name
);
133 dest
->is_default
= 0;
134 dest
->num_options
= 0;
135 dest
->options
= (cups_option_t
*)0;
138 dest
->instance
= NULL
;
142 * Copy options from the primary instance...
145 dest
->instance
= strdup(instance
);
147 if ((parent
= cupsGetDest(name
, NULL
, num_dests
+ 1, *dests
)) != NULL
)
149 for (i
= parent
->num_options
, option
= parent
->options
;
152 dest
->num_options
= cupsAddOption(option
->name
, option
->value
,
158 return (num_dests
+ 1);
163 * 'cupsFreeDests()' - Free the memory used by the list of destinations.
167 cupsFreeDests(int num_dests
, /* I - Number of destinations */
168 cups_dest_t
*dests
) /* I - Destinations */
170 int i
; /* Looping var */
171 cups_dest_t
*dest
; /* Current destination */
174 if (num_dests
== 0 || dests
== NULL
)
177 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
182 free(dest
->instance
);
184 cupsFreeOptions(dest
->num_options
, dest
->options
);
192 * 'cupsGetDest()' - Get the named destination from the list.
194 * Use the cupsGetDests() or cupsGetDests2() functions to get a
195 * list of supported destinations for the current user.
198 cups_dest_t
* /* O - Destination pointer or NULL */
199 cupsGetDest(const char *name
, /* I - Destination name or NULL for the default destination */
200 const char *instance
, /* I - Instance name or NULL */
201 int num_dests
, /* I - Number of destinations */
202 cups_dest_t
*dests
) /* I - Destinations */
204 int comp
; /* Result of comparison */
207 if (num_dests
<= 0 || !dests
)
213 * NULL name for default printer.
216 while (num_dests
> 0)
218 if (dests
->is_default
)
228 * Lookup name and optionally the instance...
231 while (num_dests
> 0)
233 if ((comp
= strcasecmp(name
, dests
->name
)) < 0)
237 if ((!instance
&& !dests
->instance
) ||
238 (instance
!= NULL
&& dests
->instance
!= NULL
&&
239 !strcasecmp(instance
, dests
->instance
)))
253 * 'cupsGetDests()' - Get the list of destinations from the default server.
255 * Starting with CUPS 1.2, the returned list of destinations include the
256 * printer-info, printer-is-accepting-jobs, printer-is-shared,
257 * printer-make-and-model, printer-state, printer-state-change-time,
258 * printer-state-reasons, and printer-type attributes as options.
260 * Use the cupsFreeDests() function to free the destination list and
261 * the cupsGetDest() function to find a particular destination.
264 int /* O - Number of destinations */
265 cupsGetDests(cups_dest_t
**dests
) /* O - Destinations */
267 int num_dests
; /* Number of destinations */
268 http_t
*http
; /* HTTP connection */
272 * Connect to the CUPS server and get the destination list and options...
275 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
277 num_dests
= cupsGetDests2(http
, dests
);
287 * 'cupsGetDests2()' - Get the list of destinations from the specified server.
289 * Starting with CUPS 1.2, the returned list of destinations include the
290 * printer-info, printer-is-accepting-jobs, printer-is-shared,
291 * printer-make-and-model, printer-state, printer-state-change-time,
292 * printer-state-reasons, and printer-type attributes as options.
294 * Use the cupsFreeDests() function to free the destination list and
295 * the cupsGetDest() function to find a particular destination.
297 * @since CUPS 1.1.21@
300 int /* O - Number of destinations */
301 cupsGetDests2(http_t
*http
, /* I - HTTP connection */
302 cups_dest_t
**dests
) /* O - Destinations */
304 int i
; /* Looping var */
305 int num_dests
; /* Number of destinations */
306 cups_dest_t
*dest
; /* Destination pointer */
307 const char *home
; /* HOME environment variable */
308 char filename
[1024]; /* Local ~/.cups/lpoptions file */
309 const char *defprinter
; /* Default printer */
310 char name
[1024], /* Copy of printer name */
311 *instance
; /* Pointer to instance name */
312 int num_reals
; /* Number of real queues */
313 cups_dest_t
*reals
; /* Real queues */
314 _cups_globals_t
*cg
= _cupsGlobals(); /* Global data */
318 * Range check the input...
325 * Initialize destination array...
329 *dests
= (cups_dest_t
*)0;
332 * Grab the printers and classes...
335 num_dests
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, num_dests
, dests
);
336 num_dests
= cups_get_sdests(http
, CUPS_GET_CLASSES
, num_dests
, dests
);
339 * Make a copy of the "real" queues for a later sanity check...
344 num_reals
= num_dests
;
345 reals
= calloc(num_reals
, sizeof(cups_dest_t
));
348 memcpy(reals
, *dests
, num_reals
* sizeof(cups_dest_t
));
359 * Grab the default destination...
362 if ((defprinter
= cupsGetDefault2(http
)) != NULL
)
365 * Grab printer and instance name...
368 strlcpy(name
, defprinter
, sizeof(name
));
370 if ((instance
= strchr(name
, '/')) != NULL
)
374 * Lookup the printer and instance and make it the default...
377 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
378 dest
->is_default
= 1;
383 * This initialization of "instance" is unnecessary, but avoids a
384 * compiler warning...
391 * Load the /etc/cups/lpoptions and ~/.cups/lpoptions files...
394 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
395 num_dests
= cups_get_dests(filename
, num_dests
, dests
);
397 if ((home
= getenv("HOME")) != NULL
)
399 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
400 if (access(filename
, 0))
401 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
403 num_dests
= cups_get_dests(filename
, num_dests
, dests
);
407 * Validate the current default destination - this prevents old
408 * Default lines in /etc/cups/lpoptions and ~/.cups/lpoptions from
409 * pointing to a non-existent printer or class...
415 * See if we have a default printer...
418 if ((dest
= cupsGetDest(NULL
, NULL
, num_dests
, *dests
)) != NULL
)
421 * Have a default; see if it is real...
424 dest
= cupsGetDest(dest
->name
, NULL
, num_reals
, reals
);
428 * If dest is NULL, then no default (that exists) is set, so we
429 * need to set a default if one exists...
432 if (dest
== NULL
&& defprinter
!= NULL
)
434 for (i
= 0; i
< num_dests
; i
++)
435 (*dests
)[i
].is_default
= 0;
437 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) != NULL
)
438 dest
->is_default
= 1;
449 * Return the number of destinations...
457 * 'cupsSetDests()' - Save the list of destinations for the default server.
459 * This function saves the destinations to /etc/cups/lpoptions when run
460 * as root and ~/.cups/lpoptions when run as a normal user.
464 cupsSetDests(int num_dests
, /* I - Number of destinations */
465 cups_dest_t
*dests
) /* I - Destinations */
467 http_t
*http
; /* HTTP connection */
471 * Connect to the CUPS server and save the destination list and options...
474 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
476 cupsSetDests2(http
, num_dests
, dests
);
484 * 'cupsSetDests2()' - Save the list of destinations for the specified server.
486 * This function saves the destinations to /etc/cups/lpoptions when run
487 * as root and ~/.cups/lpoptions when run as a normal user.
489 * @since CUPS 1.1.21@
492 int /* O - 0 on success, -1 on error */
493 cupsSetDests2(http_t
*http
, /* I - HTTP connection */
494 int num_dests
, /* I - Number of destinations */
495 cups_dest_t
*dests
) /* I - Destinations */
497 int i
, j
; /* Looping vars */
498 int wrote
; /* Wrote definition? */
499 cups_dest_t
*dest
; /* Current destination */
500 cups_option_t
*option
; /* Current option */
501 _ipp_option_t
*match
; /* Matching attribute for option */
502 FILE *fp
; /* File pointer */
504 const char *home
; /* HOME environment variable */
506 char filename
[1024]; /* lpoptions file */
507 int num_temps
; /* Number of temporary destinations */
508 cups_dest_t
*temps
, /* Temporary destinations */
509 *temp
; /* Current temporary dest */
510 const char *val
; /* Value of temporary option */
511 _cups_globals_t
*cg
= _cupsGlobals(); /* Global data */
515 * Range check the input...
518 if (!http
|| !num_dests
|| !dests
)
522 * Get the server destinations...
525 num_temps
= cups_get_sdests(http
, CUPS_GET_PRINTERS
, 0, &temps
);
526 num_temps
= cups_get_sdests(http
, CUPS_GET_CLASSES
, num_temps
, &temps
);
529 * Figure out which file to write to...
532 snprintf(filename
, sizeof(filename
), "%s/lpoptions", cg
->cups_serverroot
);
538 * Merge in server defaults...
541 num_temps
= cups_get_dests(filename
, num_temps
, &temps
);
544 * Point to user defaults...
547 if ((home
= getenv("HOME")) != NULL
)
550 * Remove the old ~/.lpoptions file...
553 snprintf(filename
, sizeof(filename
), "%s/.lpoptions", home
);
557 * Create ~/.cups subdirectory...
560 snprintf(filename
, sizeof(filename
), "%s/.cups", home
);
561 if (access(filename
, 0))
562 mkdir(filename
, 0700);
564 snprintf(filename
, sizeof(filename
), "%s/.cups/lpoptions", home
);
570 * Try to open the file...
573 if ((fp
= fopen(filename
, "w")) == NULL
)
575 cupsFreeDests(num_temps
, temps
);
581 * Set the permissions to 0644 when saving to the /etc/cups/lpoptions
586 fchmod(fileno(fp
), 0644);
590 * Write each printer; each line looks like:
592 * Dest name[/instance] options
593 * Default name[/instance] options
596 for (i
= num_dests
, dest
= dests
; i
> 0; i
--, dest
++)
597 if (dest
->instance
!= NULL
|| dest
->num_options
!= 0 || dest
->is_default
)
599 if (dest
->is_default
)
601 fprintf(fp
, "Default %s", dest
->name
);
603 fprintf(fp
, "/%s", dest
->instance
);
610 if ((temp
= cupsGetDest(dest
->name
, dest
->instance
, num_temps
, temps
)) == NULL
)
611 temp
= cupsGetDest(dest
->name
, NULL
, num_temps
, temps
);
613 for (j
= dest
->num_options
, option
= dest
->options
; j
> 0; j
--, option
++)
616 * See if this option is a printer attribute; if so, skip it...
619 if ((match
= _ippFindOption(option
->name
)) != NULL
&&
620 match
->group_tag
== IPP_TAG_PRINTER
)
624 * See if the server/global options match these; if so, don't
629 (val
= cupsGetOption(option
->name
, temp
->num_options
,
630 temp
->options
)) != NULL
&&
631 !strcasecmp(val
, option
->value
))
635 * Options don't match, write to the file...
640 fprintf(fp
, "Dest %s", dest
->name
);
642 fprintf(fp
, "/%s", dest
->instance
);
646 if (option
->value
[0])
648 if (strchr(option
->value
, ' ') ||
649 strchr(option
->value
, '\\') ||
650 strchr(option
->value
, '\"') ||
651 strchr(option
->value
, '\''))
657 fprintf(fp
, " %s=\"", option
->name
);
659 for (val
= option
->value
; *val
; val
++)
661 if (strchr("\"\'\\", *val
))
672 * Store the literal value...
675 fprintf(fp
, " %s=%s", option
->name
, option
->value
);
679 fprintf(fp
, " %s", option
->name
);
687 * Free the temporary destinations and close the file...
690 cupsFreeDests(num_temps
, temps
);
694 #ifdef HAVE_NOTIFY_POST
696 * Send a notification so that MacOS X applications can know about the
700 notify_post("com.apple.printerListChange");
701 #endif /* HAVE_NOTIFY_POST */
708 * 'cups_get_dests()' - Get destinations from a file.
711 static int /* O - Number of destinations */
712 cups_get_dests(const char *filename
, /* I - File to read from */
713 int num_dests
, /* I - Number of destinations */
714 cups_dest_t
**dests
) /* IO - Destinations */
716 int i
; /* Looping var */
717 cups_dest_t
*dest
; /* Current destination */
718 FILE *fp
; /* File pointer */
719 char line
[8192], /* Line from file */
720 *lineptr
, /* Pointer into line */
721 *name
, /* Name of destination/option */
722 *instance
; /* Instance of destination */
723 const char *printer
; /* PRINTER or LPDEST */
727 * Check environment variables...
730 if ((printer
= getenv("LPDEST")) == NULL
)
731 if ((printer
= getenv("PRINTER")) != NULL
)
732 if (strcmp(printer
, "lp") == 0)
736 * Try to open the file...
739 if ((fp
= fopen(filename
, "r")) == NULL
)
743 * Read each printer; each line looks like:
745 * Dest name[/instance] options
746 * Default name[/instance] options
749 while (fgets(line
, sizeof(line
), fp
) != NULL
)
752 * See what type of line it is...
755 if (strncasecmp(line
, "dest", 4) == 0 && isspace(line
[4] & 255))
757 else if (strncasecmp(line
, "default", 7) == 0 && isspace(line
[7] & 255))
763 * Skip leading whitespace...
766 while (isspace(*lineptr
& 255))
775 * Search for an instance...
778 while (!isspace(*lineptr
& 255) && *lineptr
&& *lineptr
!= '/')
787 * Found an instance...
794 * Search for an instance...
797 while (!isspace(*lineptr
& 255) && *lineptr
)
806 * See if the primary instance of the destination exists; if not,
807 * ignore this entry and move on...
810 if (cupsGetDest(name
, NULL
, num_dests
, *dests
) == NULL
)
814 * Add the destination...
817 num_dests
= cupsAddDest(name
, instance
, num_dests
, dests
);
819 if ((dest
= cupsGetDest(name
, instance
, num_dests
, *dests
)) == NULL
)
830 * Add options until we hit the end of the line...
833 dest
->num_options
= cupsParseOptions(lineptr
, dest
->num_options
,
837 * Set this as default if needed...
840 if (strncasecmp(line
, "default", 7) == 0 && printer
== NULL
)
842 for (i
= 0; i
< num_dests
; i
++)
843 (*dests
)[i
].is_default
= 0;
845 dest
->is_default
= 1;
850 * Close the file and return...
860 * 'cups_get_sdests()' - Get destinations from a server.
863 static int /* O - Number of destinations */
864 cups_get_sdests(http_t
*http
, /* I - HTTP connection */
865 ipp_op_t op
, /* I - get-printers or get-classes */
866 int num_dests
, /* I - Number of destinations */
867 cups_dest_t
**dests
) /* IO - Destinations */
869 int i
; /* Looping var */
870 cups_dest_t
*dest
; /* Current destination */
871 ipp_t
*request
, /* IPP Request */
872 *response
; /* IPP Response */
873 ipp_attribute_t
*attr
; /* Current attribute */
874 int accepting
, /* printer-is-accepting-jobs attribute */
875 shared
, /* printer-is-shared attribute */
876 state
, /* printer-state attribute */
877 change_time
, /* printer-state-change-time attribute */
878 type
; /* printer-type attribute */
879 const char *info
, /* printer-info attribute */
880 *make_model
, /* printer-make-and-model attribute */
881 *name
; /* printer-name attribute */
882 char job_sheets
[1024], /* job-sheets option */
883 reasons
[1024], /* printer-state-reasons attribute */
884 *rptr
, /* Pointer into reasons string */
885 temp
[255]; /* Temporary string for numbers */
886 static const char * const pattrs
[] = /* Attributes we're interested in */
888 "job-sheets-default",
890 "printer-is-accepting-jobs",
892 "printer-make-and-model",
895 "printer-state-change-time",
896 "printer-state-reasons",
902 * Build a CUPS_GET_PRINTERS or CUPS_GET_CLASSES request, which require
903 * the following attributes:
906 * attributes-natural-language
907 * requesting-user-name
910 request
= ippNewRequest(op
);
912 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
913 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
916 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
917 "requesting-user-name", NULL
, cupsUser());
920 * Do the request and get back a response...
923 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
925 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
928 * Skip leading attributes until we hit a printer...
931 while (attr
!= NULL
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
938 * Pull the needed attributes from this job...
947 state
= IPP_PRINTER_IDLE
;
948 type
= CUPS_PRINTER_LOCAL
;
950 strcpy(job_sheets
, "");
953 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_PRINTER
)
955 if (!strcmp(attr
->name
, "job-sheets-default") &&
956 (attr
->value_tag
== IPP_TAG_KEYWORD
||
957 attr
->value_tag
== IPP_TAG_NAME
))
959 if (attr
->num_values
== 2)
960 snprintf(job_sheets
, sizeof(job_sheets
), "%s,%s",
961 attr
->values
[0].string
.text
, attr
->values
[1].string
.text
);
963 strlcpy(job_sheets
, attr
->values
[0].string
.text
,
966 else if (!strcmp(attr
->name
, "printer-info") &&
967 attr
->value_tag
== IPP_TAG_TEXT
)
968 info
= attr
->values
[0].string
.text
;
969 else if (!strcmp(attr
->name
, "printer-is-accepting-jobs") &&
970 attr
->value_tag
== IPP_TAG_BOOLEAN
)
971 accepting
= attr
->values
[0].boolean
;
972 else if (!strcmp(attr
->name
, "printer-is-shared") &&
973 attr
->value_tag
== IPP_TAG_BOOLEAN
)
974 shared
= attr
->values
[0].boolean
;
975 else if (!strcmp(attr
->name
, "printer-make-and-model") &&
976 attr
->value_tag
== IPP_TAG_TEXT
)
977 make_model
= attr
->values
[0].string
.text
;
978 else if (!strcmp(attr
->name
, "printer-name") &&
979 attr
->value_tag
== IPP_TAG_NAME
)
980 name
= attr
->values
[0].string
.text
;
981 else if (!strcmp(attr
->name
, "printer-state") &&
982 attr
->value_tag
== IPP_TAG_ENUM
)
983 state
= attr
->values
[0].integer
;
984 else if (!strcmp(attr
->name
, "printer-state-change-time") &&
985 attr
->value_tag
== IPP_TAG_INTEGER
)
986 change_time
= attr
->values
[0].integer
;
987 else if (!strcmp(attr
->name
, "printer-state-reasons") &&
988 attr
->value_tag
== IPP_TAG_KEYWORD
)
990 strlcpy(reasons
, attr
->values
[0].string
.text
, sizeof(reasons
));
991 for (i
= 1, rptr
= reasons
+ strlen(reasons
);
992 i
< attr
->num_values
;
995 snprintf(rptr
, sizeof(reasons
) - (rptr
- reasons
), ",%s",
996 attr
->values
[i
].string
.text
);
997 rptr
+= strlen(rptr
);
1000 else if (!strcmp(attr
->name
, "printer-type") &&
1001 attr
->value_tag
== IPP_TAG_ENUM
)
1002 type
= attr
->values
[0].integer
;
1008 * See if we have everything needed...
1019 num_dests
= cupsAddDest(name
, NULL
, num_dests
, dests
);
1021 if ((dest
= cupsGetDest(name
, NULL
, num_dests
, *dests
)) != NULL
)
1024 dest
->num_options
= cupsAddOption("job-sheets", job_sheets
,
1029 dest
->num_options
= cupsAddOption("printer-info", info
,
1033 sprintf(temp
, "%d", accepting
);
1034 dest
->num_options
= cupsAddOption("printer-is-accepting-jobs", temp
,
1038 sprintf(temp
, "%d", shared
);
1039 dest
->num_options
= cupsAddOption("printer-is-shared", temp
,
1044 dest
->num_options
= cupsAddOption("printer-make-and-model",
1045 make_model
, dest
->num_options
,
1048 sprintf(temp
, "%d", state
);
1049 dest
->num_options
= cupsAddOption("printer-state", temp
,
1055 sprintf(temp
, "%d", change_time
);
1056 dest
->num_options
= cupsAddOption("printer-state-change-time", temp
,
1062 dest
->num_options
= cupsAddOption("printer-state-reasons", reasons
,
1066 sprintf(temp
, "%d", type
);
1067 dest
->num_options
= cupsAddOption("printer-type", temp
,
1076 ippDelete(response
);
1080 * Return the count...
1088 * End of "$Id: dest.c 6191 2007-01-10 16:48:37Z mike $".