]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/testclient.c
Save work on client code.
[thirdparty/cups.git] / cups / testclient.c
1 /*
2 * Simulated client test program for CUPS.
3 *
4 * Copyright 2017 by Apple Inc.
5 *
6 * These coded instructions, statements, and computer programs are the
7 * property of Apple Inc. and are protected by Federal copyright
8 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
9 * which should have been included with this file. If this file is
10 * missing or damaged, see the license at "http://www.cups.org/".
11 *
12 * This file is subject to the Apple OS-Developed Software exception.
13 */
14
15 /*
16 * Include necessary headers...
17 */
18
19 #include "cups.h"
20 #include "raster.h"
21 #include "thread-private.h"
22 #include <stdlib.h>
23
24
25 /*
26 * Local types...
27 */
28
29 typedef struct _client_monitor_s
30 {
31 const char *uri, /* Printer URI */
32 *hostname, /* Hostname */
33 *user, /* Username */
34 *resource; /* Resource path */
35 int port; /* Port number */
36 http_encryption_t encryption; /* Use encryption? */
37 ipp_pstate_t printer_state; /* Current printer state */
38 char printer_state_reasons[1024];
39 /* Current printer-state-reasons */
40 int job_id; /* Job ID for submitted job */
41 ipp_jstate_t job_state; /* Current job state */
42 char job_state_reasons[1024];
43 /* Current job-state-reasons */
44 } _client_monitor_t;
45
46
47 /*
48 * Local functions...
49 */
50
51 static const char *make_raster_file(ipp_t *response, char *tempname, size_t tempsize, const char **format);
52 static void *monitor_printer(_client_monitor_t *monitor);
53 static void show_capabilities(ipp_t *response);
54 static void usage(void);
55
56
57 /*
58 * 'main()' - Main entry.
59 */
60
61 int /* O - Exit status */
62 main(int argc, /* I - Number of command-line arguments */
63 char *argv[]) /* I - Command-line arguments */
64 {
65 int i; /* Looping var */
66 const char *opt, /* Current option */
67 *uri = NULL, /* Printer URI */
68 *printfile = NULL,
69 /* Print file */
70 *printformat = NULL;
71 /* Print format */
72 char tempfile[1024] = "",
73 /* Temporary file (if any) */
74 scheme[32], /* URI scheme */
75 userpass[256], /* Username:password */
76 hostname[256], /* Hostname */
77 resource[256]; /* Resource path */
78 int port; /* Port number */
79 http_encryption_t encryption; /* Encryption mode */
80 _client_monitor_t monitor; /* Monitoring data */
81 http_t *http; /* HTTP connection */
82 ipp_t *request, /* IPP request */
83 *response; /* IPP response */
84 ipp_attribute_t *attr; /* IPP attribute */
85 static const char * const pattrs[] = /* Printer attributes we are interested in */
86 {
87 "job-template",
88 "printer-defaults",
89 "printer-description",
90 "media-col-database",
91 "media-col-ready"
92 };
93
94
95 /*
96 * Parse command-line options...
97 */
98
99 for (i = 1; i < argc; i ++)
100 {
101 if (argv[i][0] == '-')
102 {
103 for (opt = argv[i] + 1; *opt; opt ++)
104 {
105 switch (*opt)
106 {
107 case 'f' : /* -f print-file */
108 if (printfile)
109 {
110 puts("Print file can only be specified once.");
111 usage();
112 return (1);
113 }
114
115 i ++;
116 if (i >= argc)
117 {
118 puts("Expected print file after '-f'.");
119 usage();
120 return (1);
121 }
122
123 printfile = argv[i];
124 break;
125
126 default :
127 printf("Unknown option '-%c'.\n", *opt);
128 usage();
129 return (1);
130 }
131 }
132 }
133 else if (uri || (strncmp(argv[i], "ipp://", 6) && strncmp(argv[i], "ipps://", 7)))
134 {
135 printf("Unknown command-line argument '%s'.\n", argv[i]);
136 usage();
137 return (1);
138 }
139 else
140 uri = argv[i];
141 }
142
143 /*
144 * Make sure we have everything we need.
145 */
146
147 if (!uri)
148 {
149 puts("Expected printer URI.");
150 usage();
151 return (1);
152 }
153
154 /*
155 * Connect to the printer...
156 */
157
158 if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), username, sizeof(username), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
159 {
160 printf("Bad printer URI '%s'.\n", uri);
161 return (1);
162 }
163
164 if (!port)
165 port = IPP_PORT;
166
167 if (!strcmp(scheme, "https") || !strcmp(scheme, "ipps"))
168 encryption = HTTP_ENCRYPTION_ALWAYS;
169 else
170 encryption = HTTP_ENCRYPTION_IF_REQUESTED;
171
172 if ((http = httpConnect2(hostname, port, NULL, AF_UNSPEC, encryption, 1, 0, NULL)) == NULL)
173 {
174 printf("Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
175 return (1);
176 }
177
178 /*
179 * Query printer status and capabilities...
180 */
181
182 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
183 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
184 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
185 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
186
187 response = cupsDoRequest(http, request, resource);
188
189 show_capabilities(response);
190
191 /*
192 * Now figure out what we will be printing...
193 */
194
195 if (printfile)
196 {
197 /*
198 * User specified a print file, figure out the format...
199 */
200
201 if ((opt = strrchr(printfile, '.')) != NULL)
202 {
203 /*
204 * Guess the format from the extension...
205 */
206
207 if (!strcmp(opt, ".jpg"))
208 printformat = "image/jpeg";
209 else if (!strcmp(opt, ".pdf"))
210 printformat = "application/pdf";
211 else if (!strcmp(opt, ".ps"))
212 printformat = "application/postscript";
213 else if (!strcmp(opt, ".pwg"))
214 printformat = "image/pwg-raster";
215 else if (!strcmp(opt, ".urf"))
216 printformat = "image/urf";
217 else
218 printformat = "application/octet-stream";
219 }
220 else
221 {
222 /*
223 * Tell the printer to auto-detect...
224 */
225
226 printformat = "application/octet-stream";
227 }
228 }
229 else
230 {
231 /*
232 * No file specified, make something to test with...
233 */
234
235 if ((printfile = make_raster_file(response, tempfile, sizeof(tempfile), &printformat)) == NULL)
236 return (1);
237 }
238
239 ippDelete(response);
240
241 /*
242 * Start monitoring the printer in the background...
243 */
244
245 memset(&monitor, 0, sizeof(monitor));
246
247 monitor.uri = uri;
248 monitor.hostname = hostname;
249 monitor.resource = resource;
250 monitor.port = port;
251 monitor.encryption = encryption;
252
253 _cupsThreadCreate((_cups_thread_func_t)monitor_printer, &monitor);
254
255 /*
256 * Create the job and wait for completion...
257 */
258
259 request = ippNewRequest(IPP_OP_CREATE_JOB);
260 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
261 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
262
263 if ((opt = strrchr(printfile, '/')) != NULL)
264 opt ++;
265 else
266 opt = printfile;
267
268 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_name, "job-name", NULL, opt);
269
270 response = cupsDoRequest(http, request, resource);
271
272 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
273 {
274 printf("Unable to create print job: %s\n", cupsLastErrorString());
275
276 monitor.job_state = IPP_JSTATE_ABORTED;
277
278 goto cleanup;
279 }
280
281 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
282 {
283 puts("No job-id returned in Create-Job request.");
284
285 monitor.job_state = IPP_JSTATE_ABORTED;
286
287 goto cleanup;
288 }
289
290 monitor.job_id = ippGetInteger(attr, 0);
291
292 ippDelete(response);
293
294 request = ippNewRequest(IPP_OP_CREATE_JOB);
295 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
296 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor.job_id);
297 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
298 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, "document-format", NULL, printformat);
299
300 response = cupsDoFileRequest(http, request, resource, printfile);
301
302 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE)
303 {
304 printf("Unable to print file: %s\n", cupsLastErrorString());
305
306 monitor.job_state = IPP_JSTATE_ABORTED;
307
308 goto cleanup;
309 }
310
311 while (monitor.job_state < IPP_JSTATE_CANCELED)
312 sleep(1);
313
314 /*
315 * Cleanup after ourselves...
316 */
317
318 cleanup:
319
320 httpClose(http);
321
322 if (tempfile[0])
323 unlink(tempfile);
324
325 return (monitor.job_state == IPP_JSTATE_COMPLETED);
326 }
327
328
329 /*
330 * 'make_raster_file()' - Create a temporary raster file.
331 */
332
333 static const char * /* O - Print filename */
334 make_raster_file(ipp_t *response, /* I - Printer attributes */
335 char *tempname, /* I - Temporary filename buffer */
336 size_t tempsize, /* I - Size of temp file buffer */
337 const char **format) /* O - Print format */
338 {
339 }
340
341
342 /*
343 * 'monitor_printer()' - Monitor the job and printer states.
344 */
345
346 static void * /* O - Thread exit code */
347 monitor_printer(
348 _client_monitor_t *monitor) /* I - Monitoring data */
349 {
350 http_t *http; /* Connection to printer */
351 ipp_t *request, /* IPP request */
352 *response; /* IPP response */
353 ipp_attribute_t *attr; /* Attribute in response */
354 ipp_pstate_t printer_state; /* Printer state */
355 char printer_state_reasons[1024];
356 /* Printer state reasons */
357 int job_id; /* Job ID */
358 ipp_jstate_t job_state; /* Job state */
359 char job_state_reasons[1024];/* Printer state reasons */
360 static const char * const jattrs[] = /* Job attributes we want */
361 {
362 "job-state",
363 "job-state-reasons"
364 };
365 static const char * const pattrs[] = /* Printer attributes we want */
366 {
367 "printer-state",
368 "printer-state-reasons"
369 };
370
371
372 /*
373 * Open a connection to the printer...
374 */
375
376 http = httpConnect2(monitor->hostname, monitor->port, NULL, AF_UNSPEC,
377 monitor->encryption, 1, 0, NULL);
378 httpSetTimeout(http, 30.0, timeout_cb, NULL);
379
380 /*
381 * Loop until the job is canceled, aborted, or completed.
382 */
383
384 printer_state = (ipp_pstate_t)0;
385 printer_state_reasons[0] = '\0';
386
387 job_state = (ipp_jstate_t)0;
388 job_state_reasons[0] = '\0';
389
390 while (monitor->job_state < IPP_JSTATE_CANCELED)
391 {
392 /*
393 * Reconnect to the printer as needed...
394 */
395
396 if (httpGetFd(http) < 0)
397 httpReconnect(http);
398
399 if (httpGetFd(http) >= 0)
400 {
401 /*
402 * Connected, so check on the printer state...
403 */
404
405 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
406 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
407 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
408 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(pattrs) / sizeof(pattrs[0])), NULL, pattrs);
409
410 response = cupsDoRequest(http, request, monitor->resource);
411
412 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
413 printer_state = (ipp_pstate_t)ippGetInteger(attr, 0);
414
415 if ((attr = ippFindAttribute(response, "printer-state-reasons", IPP_TAG_KEYWORD)) != NULL)
416 ippAttributeString(attr, printer_state_reasons, sizeof(printer_state_reasons));
417
418 if (printer_state != monitor->printer_state || strcmp(printer_state_reasons, monitor->printer_state_reasons))
419 {
420 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state), printer_state_reasons);
421
422 monitor->printer_state = printer_state;
423 strlcpy(monitor->printer_state_reasons, printer_state_reasons, sizeof(monitor->printer_state_reasons));
424 }
425
426 ippDelete(response);
427
428 if (monitor->job_id > 0)
429 {
430 /*
431 * Check the status of the job itself...
432 */
433
434 request = ippNewRequest(IPP_OP_GET_JOB_ATTRIBUTES);
435 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, monitor->uri);
436 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", monitor->job_id);
437 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser());
438 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", (int)(sizeof(jattrs) / sizeof(jattrs[0])), NULL, jattrs);
439
440 response = cupsDoRequest(http, request, monitor->resource);
441
442 if ((attr = ippFindAttribute(response, "job-state", IPP_TAG_ENUM)) != NULL)
443 job_state = (ipp_jstate_t)ippGetInteger(attr, 0);
444
445 if ((attr = ippFindAttribute(response, "job-state-reasons", IPP_TAG_KEYWORD)) != NULL)
446 ippAttributeString(attr, job_state_reasons, sizeof(job_state_reasons));
447
448 if (job_state != monitor->job_state || strcmp(job_state_reasons, monitor->job_state_reasons))
449 {
450 printf("JOB %d: %s (%s)\n", monitor->job_id, ippEnumString("job-state", job_state), job_state_reasons);
451
452 monitor->job_state = job_state;
453 strlcpy(monitor->job_state_reasons, job_state_reasons, sizeof(monitor->job_state_reasons));
454 }
455
456 ippDelete(response);
457 }
458 }
459
460 if (monitor.job_state < IPP_JSTATE_CANCELED)
461 {
462 /*
463 * Sleep for 5 seconds...
464 */
465
466 sleep(5);
467 }
468 }
469
470 /*
471 * Cleanup and return...
472 */
473
474 httpClose(http);
475
476 return (NULL);
477 }
478
479
480 /*
481 * 'show_capabilities()' - Show printer capabilities.
482 */
483
484 static void
485 show_capabilities(ipp_t *response) /* I - Printer attributes */
486 {
487 int i; /* Looping var */
488 ipp_attribute_t *attr; /* Attribute */
489 char buffer[1024]; /* Attribute value buffer */
490 static const char * const * pattrs[] =/* Attributes we want to show */
491 {
492 "copies-default",
493 "copies-supported",
494 "finishings-default",
495 "finishings-ready",
496 "finishings-supported",
497 "media-default",
498 "media-ready",
499 "media-supported",
500 "output-bin-default",
501 "output-bin-supported",
502 "print-color-mode-default",
503 "print-color-mode-supported",
504 "sides-default",
505 "sides-supported",
506 "document-format-default",
507 "document-format-supported",
508 "pwg-raster-document-resolution-supported",
509 "pwg-raster-document-type-supported",
510 "urf-supported"
511 }
512
513 puts("CAPABILITIES:");
514 for (i = 0; i < (int)(sizeof(pattrs) / sizeof(pattrs[0])); i ++)
515 {
516 if ((attr = ippFindAttribute(response, pattrs[i], IPP_TAG_ZERO)) != NULL)
517 {
518 ippAttributeString(attr, buffer, sizeof(buffer));
519 printf(" %s=%s\n", pattrs[i], buffer);
520 }
521 }
522 }
523
524
525 /*
526 * 'usage()' - Show program usage...
527 */
528
529 static void
530 usage(void)
531 {
532 puts("Usage: ./testclient printer-uri [-f print-file]");
533 }