2 * Simulated client test program for CUPS.
4 * Copyright 2017 by Apple Inc.
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/".
12 * This file is subject to the Apple OS-Developed Software exception.
16 * Include necessary headers...
21 #include "thread-private.h"
29 typedef struct _client_monitor_s
31 const char *uri
, /* Printer URI */
32 *hostname
, /* Hostname */
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 */
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);
58 * 'main()' - Main entry.
61 int /* O - Exit status */
62 main(int argc
, /* I - Number of command-line arguments */
63 char *argv
[]) /* I - Command-line arguments */
65 int i
; /* Looping var */
66 const char *opt
, /* Current option */
67 *uri
= NULL
, /* Printer URI */
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 */
89 "printer-description",
96 * Parse command-line options...
99 for (i
= 1; i
< argc
; i
++)
101 if (argv
[i
][0] == '-')
103 for (opt
= argv
[i
] + 1; *opt
; opt
++)
107 case 'f' : /* -f print-file */
110 puts("Print file can only be specified once.");
118 puts("Expected print file after '-f'.");
127 printf("Unknown option '-%c'.\n", *opt
);
133 else if (uri
|| (strncmp(argv
[i
], "ipp://", 6) && strncmp(argv
[i
], "ipps://", 7)))
135 printf("Unknown command-line argument '%s'.\n", argv
[i
]);
144 * Make sure we have everything we need.
149 puts("Expected printer URI.");
155 * Connect to the printer...
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
)
160 printf("Bad printer URI '%s'.\n", uri
);
167 if (!strcmp(scheme
, "https") || !strcmp(scheme
, "ipps"))
168 encryption
= HTTP_ENCRYPTION_ALWAYS
;
170 encryption
= HTTP_ENCRYPTION_IF_REQUESTED
;
172 if ((http
= httpConnect2(hostname
, port
, NULL
, AF_UNSPEC
, encryption
, 1, 0, NULL
)) == NULL
)
174 printf("Unable to connect to '%s' on port %d: %s\n", hostname
, port
, cupsLastErrorString());
179 * Query printer status and capabilities...
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
);
187 response
= cupsDoRequest(http
, request
, resource
);
189 show_capabilities(response
);
192 * Now figure out what we will be printing...
198 * User specified a print file, figure out the format...
201 if ((opt
= strrchr(printfile
, '.')) != NULL
)
204 * Guess the format from the extension...
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";
218 printformat
= "application/octet-stream";
223 * Tell the printer to auto-detect...
226 printformat
= "application/octet-stream";
232 * No file specified, make something to test with...
235 if ((printfile
= make_raster_file(response
, tempfile
, sizeof(tempfile
), &printformat
)) == NULL
)
242 * Start monitoring the printer in the background...
245 memset(&monitor
, 0, sizeof(monitor
));
248 monitor
.hostname
= hostname
;
249 monitor
.resource
= resource
;
251 monitor
.encryption
= encryption
;
253 _cupsThreadCreate((_cups_thread_func_t
)monitor_printer
, &monitor
);
256 * Create the job and wait for completion...
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());
263 if ((opt
= strrchr(printfile
, '/')) != NULL
)
268 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_name
, "job-name", NULL
, opt
);
270 response
= cupsDoRequest(http
, request
, resource
);
272 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
274 printf("Unable to create print job: %s\n", cupsLastErrorString());
276 monitor
.job_state
= IPP_JSTATE_ABORTED
;
281 if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
283 puts("No job-id returned in Create-Job request.");
285 monitor
.job_state
= IPP_JSTATE_ABORTED
;
290 monitor
.job_id
= ippGetInteger(attr
, 0);
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
);
300 response
= cupsDoFileRequest(http
, request
, resource
, printfile
);
302 if (cupsLastError() >= IPP_STATUS_REDIRECTION_OTHER_SITE
)
304 printf("Unable to print file: %s\n", cupsLastErrorString());
306 monitor
.job_state
= IPP_JSTATE_ABORTED
;
311 while (monitor
.job_state
< IPP_JSTATE_CANCELED
)
315 * Cleanup after ourselves...
325 return (monitor
.job_state
== IPP_JSTATE_COMPLETED
);
330 * 'make_raster_file()' - Create a temporary raster file.
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 */
343 * 'monitor_printer()' - Monitor the job and printer states.
346 static void * /* O - Thread exit code */
348 _client_monitor_t
*monitor
) /* I - Monitoring data */
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 */
365 static const char * const pattrs
[] = /* Printer attributes we want */
368 "printer-state-reasons"
373 * Open a connection to the printer...
376 http
= httpConnect2(monitor
->hostname
, monitor
->port
, NULL
, AF_UNSPEC
,
377 monitor
->encryption
, 1, 0, NULL
);
378 httpSetTimeout(http
, 30.0, timeout_cb
, NULL
);
381 * Loop until the job is canceled, aborted, or completed.
384 printer_state
= (ipp_pstate_t
)0;
385 printer_state_reasons
[0] = '\0';
387 job_state
= (ipp_jstate_t
)0;
388 job_state_reasons
[0] = '\0';
390 while (monitor
->job_state
< IPP_JSTATE_CANCELED
)
393 * Reconnect to the printer as needed...
396 if (httpGetFd(http
) < 0)
399 if (httpGetFd(http
) >= 0)
402 * Connected, so check on the printer state...
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
);
410 response
= cupsDoRequest(http
, request
, monitor
->resource
);
412 if ((attr
= ippFindAttribute(response
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
413 printer_state
= (ipp_pstate_t
)ippGetInteger(attr
, 0);
415 if ((attr
= ippFindAttribute(response
, "printer-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
416 ippAttributeString(attr
, printer_state_reasons
, sizeof(printer_state_reasons
));
418 if (printer_state
!= monitor
->printer_state
|| strcmp(printer_state_reasons
, monitor
->printer_state_reasons
))
420 printf("PRINTER: %s (%s)\n", ippEnumString("printer-state", printer_state
), printer_state_reasons
);
422 monitor
->printer_state
= printer_state
;
423 strlcpy(monitor
->printer_state_reasons
, printer_state_reasons
, sizeof(monitor
->printer_state_reasons
));
428 if (monitor
->job_id
> 0)
431 * Check the status of the job itself...
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
);
440 response
= cupsDoRequest(http
, request
, monitor
->resource
);
442 if ((attr
= ippFindAttribute(response
, "job-state", IPP_TAG_ENUM
)) != NULL
)
443 job_state
= (ipp_jstate_t
)ippGetInteger(attr
, 0);
445 if ((attr
= ippFindAttribute(response
, "job-state-reasons", IPP_TAG_KEYWORD
)) != NULL
)
446 ippAttributeString(attr
, job_state_reasons
, sizeof(job_state_reasons
));
448 if (job_state
!= monitor
->job_state
|| strcmp(job_state_reasons
, monitor
->job_state_reasons
))
450 printf("JOB %d: %s (%s)\n", monitor
->job_id
, ippEnumString("job-state", job_state
), job_state_reasons
);
452 monitor
->job_state
= job_state
;
453 strlcpy(monitor
->job_state_reasons
, job_state_reasons
, sizeof(monitor
->job_state_reasons
));
460 if (monitor
.job_state
< IPP_JSTATE_CANCELED
)
463 * Sleep for 5 seconds...
471 * Cleanup and return...
481 * 'show_capabilities()' - Show printer capabilities.
485 show_capabilities(ipp_t
*response
) /* I - Printer attributes */
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 */
494 "finishings-default",
496 "finishings-supported",
500 "output-bin-default",
501 "output-bin-supported",
502 "print-color-mode-default",
503 "print-color-mode-supported",
506 "document-format-default",
507 "document-format-supported",
508 "pwg-raster-document-resolution-supported",
509 "pwg-raster-document-type-supported",
513 puts("CAPABILITIES:");
514 for (i
= 0; i
< (int)(sizeof(pattrs
) / sizeof(pattrs
[0])); i
++)
516 if ((attr
= ippFindAttribute(response
, pattrs
[i
], IPP_TAG_ZERO
)) != NULL
)
518 ippAttributeString(attr
, buffer
, sizeof(buffer
));
519 printf(" %s=%s\n", pattrs
[i
], buffer
);
526 * 'usage()' - Show program usage...
532 puts("Usage: ./testclient printer-uri [-f print-file]");