1 /*
2 * USB printer backend for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2012 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers.
14 */
15
16 #ifdef __APPLE__
17 /* A header order dependency requires this be first */
18 # include <ApplicationServices/ApplicationServices.h>
19 #endif /* __APPLE__ */
20
21 #include "backend-private.h"
22
23 #ifdef _WIN32
24 # include <io.h>
25 #else
26 # include <unistd.h>
27 # include <fcntl.h>
28 # include <termios.h>
29 #endif /* _WIN32 */
30
31
32 /*
33 * Local functions...
34 */
35
36 void list_devices(void);
37 int print_device(const char *uri, const char *hostname,
38 const char *resource, char *options,
39 int print_fd, int copies, int argc, char *argv[]);
40
41
42 /*
43 * Include the vendor-specific USB implementation...
44 */
45
46 #ifdef HAVE_LIBUSB
47 # include "usb-libusb.c"
48 #elif defined(__APPLE__)
49 # include "usb-darwin.c"
50 #elif defined(__linux) || defined(__sun) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
51 # include "usb-unix.c"
52 #else
53 /*
54 * Use dummy functions that do nothing on unsupported platforms...
55 * These can be used as templates for implementing USB printing on new
56 * platforms...
57 */
58
59 /*
60 * 'list_devices()' - List all available USB devices to stdout.
61 */
62
63 void
list_devices(void)64 list_devices(void)
65 {
66 /*
67 * Don't have any devices to list... Use output of the form:
68 *
69 * direct usb:/make/model?serial=foo "Make Model" "USB Printer"
70 *
71 * Note that "Hewlett Packard" or any other variation MUST be mapped to
72 * "HP" for compatibility with the PPD and ICC specs.
73 */
74 }
75
76
77 /*
78 * 'print_device()' - Print a file to a USB device.
79 */
80
81 int /* O - Exit status */
print_device(const char * uri,const char * hostname,const char * resource,char * options,int print_fd,int copies,int argc,char * argv[])82 print_device(const char *uri, /* I - Device URI */
83 const char *hostname, /* I - Hostname/manufacturer */
84 const char *resource, /* I - Resource/modelname */
85 char *options, /* I - Device options/serial number */
86 int print_fd, /* I - File descriptor to print */
87 int copies, /* I - Copies to print */
88 int argc, /* I - Number of command-line arguments (6 or 7) */
89 char *argv[]) /* I - Command-line arguments */
90 {
91 /*
92 * Can't print, so just reference the arguments to eliminate compiler
93 * warnings and return and exit status of 1. Normally you would use the
94 * arguments to send a file to the printer and return 0 if everything
95 * worked OK and non-zero if there was an error.
96 */
97
98 (void)uri;
99 (void)hostname;
100 (void)resource;
101 (void)options;
102 (void)print_fd;
103 (void)copies;
104 (void)argc;
105 (void)argv;
106
107 return (CUPS_BACKEND_FAILED);
108 }
109 #endif /* HAVE_LIBUSB */
110
111
112 /*
113 * 'main()' - Send a file to the specified USB port.
114 *
115 * Usage:
116 *
117 * printer-uri job-id user title copies options [file]
118 */
119
120 int /* O - Exit status */
main(int argc,char * argv[])121 main(int argc, /* I - Number of command-line arguments (6 or 7) */
122 char *argv[]) /* I - Command-line arguments */
123 {
124 int print_fd; /* Print file */
125 int copies; /* Number of copies to print */
126 int status; /* Exit status */
127 int port; /* Port number (not used) */
128 const char *uri; /* Device URI */
129 char method[255], /* Method in URI */
130 hostname[1024], /* Hostname */
131 username[255], /* Username info (not used) */
132 resource[1024], /* Resource info (device and options) */
133 *options; /* Pointer to options */
134 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
135 struct sigaction action; /* Actions for POSIX signals */
136 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
137
138
139 /*
140 * Make sure status messages are not buffered...
141 */
142
143 setbuf(stderr, NULL);
144
145 /*
146 * Ignore SIGPIPE signals...
147 */
148
149 #ifdef HAVE_SIGSET
150 sigset(SIGPIPE, SIG_IGN);
151 #elif defined(HAVE_SIGACTION)
152 memset(&action, 0, sizeof(action));
153 action.sa_handler = SIG_IGN;
154 sigaction(SIGPIPE, &action, NULL);
155 #else
156 signal(SIGPIPE, SIG_IGN);
157 #endif /* HAVE_SIGSET */
158
159 /*
160 * Check command-line...
161 */
162
163 if (argc == 1)
164 {
165 list_devices();
166 return (CUPS_BACKEND_OK);
167 }
168 else if (argc != 6 && argc != 7)
169 {
170 _cupsLangPrintf(stderr,
171 _("Usage: %s job-id user title copies options [file]"),
172 argv[0]);
173 return (CUPS_BACKEND_FAILED);
174 }
175
176 /*
177 * Extract the device name and options from the URI...
178 */
179
180 uri = cupsBackendDeviceURI(argv);
181
182 if (httpSeparateURI(HTTP_URI_CODING_ALL, uri,
183 method, sizeof(method), username, sizeof(username),
184 hostname, sizeof(hostname), &port,
185 resource, sizeof(resource)) < HTTP_URI_OK)
186 {
187 _cupsLangPrintFilter(stderr, "ERROR",
188 _("No device URI found in argv[0] or in DEVICE_URI "
189 "environment variable."));
190 return (1);
191 }
192
193 /*
194 * See if there are any options...
195 */
196
197 if ((options = strchr(resource, '?')) != NULL)
198 {
199 /*
200 * Yup, terminate the device name string and move to the first
201 * character of the options...
202 */
203
204 *options++ = '\0';
205 }
206
207 /*
208 * If we have 7 arguments, print the file named on the command-line.
209 * Otherwise, send stdin instead...
210 */
211
212 if (argc == 6)
213 {
214 print_fd = 0;
215 copies = 1;
216 }
217 else
218 {
219 /*
220 * Try to open the print file...
221 */
222
223 if ((print_fd = open(argv[6], O_RDONLY)) < 0)
224 {
225 _cupsLangPrintError("ERROR", _("Unable to open print file"));
226 return (CUPS_BACKEND_FAILED);
227 }
228
229 copies = atoi(argv[4]);
230 }
231
232 /*
233 * Finally, send the print file...
234 */
235
236 status = print_device(uri, hostname, resource, options, print_fd, copies,
237 argc, argv);
238
239 /*
240 * Close the input file and return...
241 */
242
243 if (print_fd != 0)
244 close(print_fd);
245
246 return (status);
247 }
248