]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/util.c
No more "what's new" file.
[thirdparty/cups.git] / cups / util.c
CommitLineData
ef416fc2 1/*
f2d18633 2 * "$Id$"
ef416fc2 3 *
7e86f2f6 4 * Printing utilities for CUPS.
ef416fc2 5 *
7e86f2f6
MS
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products.
ef416fc2 8 *
7e86f2f6
MS
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
7e86f2f6 15 * This file is subject to the Apple OS-Developed Software exception.
ef416fc2 16 */
17
18/*
19 * Include necessary headers...
20 */
21
71e16022 22#include "cups-private.h"
ef416fc2 23#include <fcntl.h>
24#include <sys/stat.h>
25#if defined(WIN32) || defined(__EMX__)
26# include <io.h>
27#else
28# include <unistd.h>
29#endif /* WIN32 || __EMX__ */
30
31
32/*
33 * Local functions...
34 */
35
fa73b229 36static int cups_get_printer_uri(http_t *http, const char *name,
37 char *host, int hostsize, int *port,
38 char *resource, int resourcesize,
39 int depth);
ef416fc2 40
41
42/*
43 * 'cupsCancelJob()' - Cancel a print job on the default server.
44 *
5a738aea
MS
45 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
46 * to cancel the current job on the named destination.
47 *
48 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
ef416fc2 49 * the cause of any failure.
50 */
51
52int /* O - 1 on success, 0 on failure */
53cupsCancelJob(const char *name, /* I - Name of printer or class */
568fa3fa 54 int job_id) /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
ef416fc2 55{
5a738aea 56 return (cupsCancelJob2(CUPS_HTTP_DEFAULT, name, job_id, 0)
cb7f98ee 57 < IPP_STATUS_REDIRECTION_OTHER_SITE);
3d052e43
MS
58}
59
60
61/*
62 * 'cupsCancelJob2()' - Cancel or purge a print job.
63 *
64 * Canceled jobs remain in the job history while purged jobs are removed
65 * from the job history.
66 *
5a738aea
MS
67 * Pass @code CUPS_JOBID_ALL@ to cancel all jobs or @code CUPS_JOBID_CURRENT@
68 * to cancel the current job on the named destination.
69 *
70 * Use the @link cupsLastError@ and @link cupsLastErrorString@ functions to get
3d052e43
MS
71 * the cause of any failure.
72 *
f3c17241 73 * @since CUPS 1.4/OS X 10.6@
3d052e43
MS
74 */
75
76ipp_status_t /* O - IPP status */
568fa3fa 77cupsCancelJob2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
5a738aea 78 const char *name, /* I - Name of printer or class */
568fa3fa 79 int job_id, /* I - Job ID, @code CUPS_JOBID_CURRENT@ for the current job, or @code CUPS_JOBID_ALL@ for all jobs */
5a738aea 80 int purge) /* I - 1 to purge, 0 to cancel */
3d052e43 81{
5a738aea 82 char uri[HTTP_MAX_URI]; /* Job/printer URI */
3d052e43 83 ipp_t *request; /* IPP request */
ef416fc2 84
85
86 /*
3d052e43 87 * Range check input...
ef416fc2 88 */
89
5a738aea 90 if (job_id < -1 || (!name && job_id == 0))
ef416fc2 91 {
cb7f98ee 92 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 93 return (0);
94 }
95
96 /*
3d052e43 97 * Connect to the default server as needed...
ef416fc2 98 */
99
3d052e43
MS
100 if (!http)
101 if ((http = _cupsConnect()) == NULL)
cb7f98ee 102 return (IPP_STATUS_ERROR_SERVICE_UNAVAILABLE);
ef416fc2 103
104 /*
5a738aea 105 * Build an IPP_CANCEL_JOB or IPP_PURGE_JOBS request, which requires the following
ef416fc2 106 * attributes:
107 *
108 * attributes-charset
109 * attributes-natural-language
5a738aea 110 * job-uri or printer-uri + job-id
3d052e43 111 * requesting-user-name
5a738aea 112 * [purge-job] or [purge-jobs]
ef416fc2 113 */
114
cb7f98ee 115 request = ippNewRequest(job_id < 0 ? IPP_OP_PURGE_JOBS : IPP_OP_CANCEL_JOB);
ef416fc2 116
5a738aea
MS
117 if (name)
118 {
119 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
120 "localhost", ippPort(), "/printers/%s", name);
121
122 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL,
123 uri);
124 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
125 job_id);
126 }
127 else if (job_id > 0)
128 {
129 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
130
131 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
132 }
ef416fc2 133
134 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
135 NULL, cupsUser());
5a738aea
MS
136
137 if (purge && job_id >= 0)
3d052e43 138 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
5a738aea
MS
139 else if (!purge && job_id < 0)
140 ippAddBoolean(request, IPP_TAG_OPERATION, "purge-jobs", 0);
ef416fc2 141
142 /*
143 * Do the request...
144 */
145
3d052e43
MS
146 ippDelete(cupsDoRequest(http, request, "/jobs/"));
147
148 return (cupsLastError());
149}
150
151
152/*
568fa3fa 153 * 'cupsCreateJob()' - Create an empty job for streaming.
3d052e43 154 *
568fa3fa
MS
155 * Use this function when you want to stream print data using the
156 * @link cupsStartDocument@, @link cupsWriteRequestData@, and
157 * @link cupsFinishDocument@ functions. If you have one or more files to
158 * print, use the @link cupsPrintFile2@ or @link cupsPrintFiles2@ function
159 * instead.
3d052e43 160 *
f3c17241 161 * @since CUPS 1.4/OS X 10.6@
3d052e43
MS
162 */
163
164int /* O - Job ID or 0 on error */
165cupsCreateJob(
568fa3fa
MS
166 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
167 const char *name, /* I - Destination name */
3d052e43
MS
168 const char *title, /* I - Title of job */
169 int num_options, /* I - Number of options */
170 cups_option_t *options) /* I - Options */
171{
172 char printer_uri[1024], /* Printer URI */
173 resource[1024]; /* Printer resource */
174 ipp_t *request, /* Create-Job request */
175 *response; /* Create-Job response */
176 ipp_attribute_t *attr; /* job-id attribute */
177 int job_id = 0; /* job-id value */
178
179
180 DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", "
e07d4801 181 "num_options=%d, options=%p)",
3d052e43
MS
182 http, name, title, num_options, options));
183
184 /*
185 * Range check input...
186 */
187
188 if (!name)
189 {
cb7f98ee 190 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
3d052e43
MS
191 return (0);
192 }
193
194 /*
195 * Build a Create-Job request...
196 */
197
cb7f98ee 198 if ((request = ippNewRequest(IPP_OP_CREATE_JOB)) == NULL)
3d052e43 199 {
cb7f98ee 200 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
3d052e43
MS
201 return (0);
202 }
203
204 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
205 NULL, "localhost", ippPort(), "/printers/%s", name);
206 snprintf(resource, sizeof(resource), "/printers/%s", name);
207
208 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
209 NULL, printer_uri);
210 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
211 NULL, cupsUser());
212 if (title)
213 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
214 title);
a469f8a5 215 cupsEncodeOptions2(request, num_options, options, IPP_TAG_OPERATION);
a29fd7dd
MS
216 cupsEncodeOptions2(request, num_options, options, IPP_TAG_JOB);
217 cupsEncodeOptions2(request, num_options, options, IPP_TAG_SUBSCRIPTION);
3d052e43
MS
218
219 /*
220 * Send the request and get the job-id...
221 */
222
223 response = cupsDoRequest(http, request, resource);
224
225 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
226 job_id = attr->values[0].integer;
227
228 ippDelete(response);
229
230 /*
231 * Return it...
232 */
233
234 return (job_id);
235}
236
237
238/*
239 * 'cupsFinishDocument()' - Finish sending a document.
240 *
5a738aea
MS
241 * The document must have been started using @link cupsStartDocument@.
242 *
f3c17241 243 * @since CUPS 1.4/OS X 10.6@
3d052e43
MS
244 */
245
246ipp_status_t /* O - Status of document submission */
568fa3fa
MS
247cupsFinishDocument(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
248 const char *name) /* I - Destination name */
3d052e43
MS
249{
250 char resource[1024]; /* Printer resource */
251
252
253 snprintf(resource, sizeof(resource), "/printers/%s", name);
ef416fc2 254
3d052e43
MS
255 ippDelete(cupsGetResponse(http, resource));
256
257 return (cupsLastError());
ef416fc2 258}
259
260
ef416fc2 261/*
262 * 'cupsFreeJobs()' - Free memory used by job data.
263 */
264
265void
266cupsFreeJobs(int num_jobs, /* I - Number of jobs */
267 cups_job_t *jobs) /* I - Jobs */
268{
3d052e43
MS
269 int i; /* Looping var */
270 cups_job_t *job; /* Current job */
ef416fc2 271
272
3d052e43 273 if (num_jobs <= 0 || !jobs)
ef416fc2 274 return;
275
3d052e43 276 for (i = num_jobs, job = jobs; i > 0; i --, job ++)
ef416fc2 277 {
3d052e43
MS
278 _cupsStrFree(job->dest);
279 _cupsStrFree(job->user);
280 _cupsStrFree(job->format);
281 _cupsStrFree(job->title);
ef416fc2 282 }
283
284 free(jobs);
285}
286
287
288/*
289 * 'cupsGetClasses()' - Get a list of printer classes from the default server.
290 *
5a738aea 291 * This function is deprecated - use @link cupsGetDests@ instead.
ef416fc2 292 *
293 * @deprecated@
294 */
295
296int /* O - Number of classes */
297cupsGetClasses(char ***classes) /* O - Classes */
298{
299 int n; /* Number of classes */
300 ipp_t *request, /* IPP Request */
301 *response; /* IPP Response */
302 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 303 char **temp; /* Temporary pointer */
3d052e43 304 http_t *http; /* Connection to server */
ef416fc2 305
306
3d052e43 307 if (!classes)
ef416fc2 308 {
cb7f98ee 309 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 310
311 return (0);
312 }
313
3d052e43 314 *classes = NULL;
ef416fc2 315
3d052e43 316 if ((http = _cupsConnect()) == NULL)
ef416fc2 317 return (0);
ef416fc2 318
319 /*
320 * Build a CUPS_GET_CLASSES request, which requires the following
321 * attributes:
322 *
323 * attributes-charset
324 * attributes-natural-language
325 * requested-attributes
326 */
327
cb7f98ee 328 request = ippNewRequest(IPP_OP_CUPS_GET_CLASSES);
ef416fc2 329
330 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
331 "requested-attributes", NULL, "printer-name");
332
333 /*
334 * Do the request and get back a response...
335 */
336
3d052e43 337 n = 0;
ef416fc2 338
3d052e43 339 if ((response = cupsDoRequest(http, request, "/")) != NULL)
ef416fc2 340 {
341 for (attr = response->attrs; attr != NULL; attr = attr->next)
342 if (attr->name != NULL &&
88f9aafc 343 _cups_strcasecmp(attr->name, "printer-name") == 0 &&
ef416fc2 344 attr->value_tag == IPP_TAG_NAME)
345 {
346 if (n == 0)
347 temp = malloc(sizeof(char *));
348 else
7e86f2f6 349 temp = realloc(*classes, sizeof(char *) * (size_t)(n + 1));
ef416fc2 350
351 if (temp == NULL)
352 {
353 /*
354 * Ran out of memory!
355 */
356
357 while (n > 0)
358 {
359 n --;
360 free((*classes)[n]);
361 }
362
363 free(*classes);
364 ippDelete(response);
365 return (0);
366 }
367
368 *classes = temp;
369 temp[n] = strdup(attr->values[0].string.text);
370 n ++;
371 }
372
373 ippDelete(response);
374 }
375
376 return (n);
377}
378
379
380/*
381 * 'cupsGetDefault()' - Get the default printer or class for the default server.
382 *
383 * This function returns the default printer or class as defined by
384 * the LPDEST or PRINTER environment variables. If these environment
385 * variables are not set, the server default destination is returned.
5a738aea
MS
386 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
387 * functions to get the user-defined default printer, as this function does
388 * not support the lpoptions-defined default printer.
ef416fc2 389 */
390
5a738aea 391const char * /* O - Default printer or @code NULL@ */
ef416fc2 392cupsGetDefault(void)
393{
ef416fc2 394 /*
395 * Return the default printer...
396 */
397
3d052e43 398 return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
ef416fc2 399}
400
401
402/*
403 * 'cupsGetDefault2()' - Get the default printer or class for the specified server.
404 *
405 * This function returns the default printer or class as defined by
406 * the LPDEST or PRINTER environment variables. If these environment
407 * variables are not set, the server default destination is returned.
5a738aea
MS
408 * Applications should use the @link cupsGetDests@ and @link cupsGetDest@
409 * functions to get the user-defined default printer, as this function does
410 * not support the lpoptions-defined default printer.
ef416fc2 411 *
f3c17241 412 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 413 */
414
5a738aea 415const char * /* O - Default printer or @code NULL@ */
568fa3fa 416cupsGetDefault2(http_t *http) /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 417{
418 ipp_t *request, /* IPP Request */
419 *response; /* IPP Response */
420 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 421 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
422
423
424 /*
38e73f87 425 * See if we have a user default printer set...
ef416fc2 426 */
427
38e73f87
MS
428 if (_cupsUserDefault(cg->def_printer, sizeof(cg->def_printer)))
429 return (cg->def_printer);
ef416fc2 430
431 /*
3d052e43 432 * Connect to the server as needed...
ef416fc2 433 */
434
435 if (!http)
3d052e43
MS
436 if ((http = _cupsConnect()) == NULL)
437 return (NULL);
ef416fc2 438
439 /*
440 * Build a CUPS_GET_DEFAULT request, which requires the following
441 * attributes:
442 *
443 * attributes-charset
444 * attributes-natural-language
445 */
446
cb7f98ee 447 request = ippNewRequest(IPP_OP_CUPS_GET_DEFAULT);
ef416fc2 448
449 /*
450 * Do the request and get back a response...
451 */
452
453 if ((response = cupsDoRequest(http, request, "/")) != NULL)
454 {
3d052e43
MS
455 if ((attr = ippFindAttribute(response, "printer-name",
456 IPP_TAG_NAME)) != NULL)
ef416fc2 457 {
3d052e43
MS
458 strlcpy(cg->def_printer, attr->values[0].string.text,
459 sizeof(cg->def_printer));
ef416fc2 460 ippDelete(response);
461 return (cg->def_printer);
462 }
463
464 ippDelete(response);
465 }
466
467 return (NULL);
468}
469
470
471/*
472 * 'cupsGetJobs()' - Get the jobs from the default server.
5a738aea
MS
473 *
474 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
475 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
476 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
477 * jobs that are stopped, canceled, aborted, or completed.
ef416fc2 478 */
479
480int /* O - Number of jobs */
481cupsGetJobs(cups_job_t **jobs, /* O - Job data */
568fa3fa 482 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
ecdc0628 483 int myjobs, /* I - 0 = all users, 1 = mine */
5a738aea 484 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
ef416fc2 485{
ef416fc2 486 /*
487 * Return the jobs...
488 */
489
5a738aea 490 return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, name, myjobs, whichjobs));
ef416fc2 491}
492
493
494
495/*
496 * 'cupsGetJobs2()' - Get the jobs from the specified server.
497 *
5a738aea
MS
498 * A "whichjobs" value of @code CUPS_WHICHJOBS_ALL@ returns all jobs regardless
499 * of state, while @code CUPS_WHICHJOBS_ACTIVE@ returns jobs that are
500 * pending, processing, or held and @code CUPS_WHICHJOBS_COMPLETED@ returns
501 * jobs that are stopped, canceled, aborted, or completed.
502 *
f3c17241 503 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 504 */
505
506int /* O - Number of jobs */
568fa3fa 507cupsGetJobs2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
ef416fc2 508 cups_job_t **jobs, /* O - Job data */
568fa3fa 509 const char *name, /* I - @code NULL@ = all destinations, otherwise show jobs for named destination */
ecdc0628 510 int myjobs, /* I - 0 = all users, 1 = mine */
5a738aea 511 int whichjobs) /* I - @code CUPS_WHICHJOBS_ALL@, @code CUPS_WHICHJOBS_ACTIVE@, or @code CUPS_WHICHJOBS_COMPLETED@ */
ef416fc2 512{
513 int n; /* Number of jobs */
514 ipp_t *request, /* IPP Request */
515 *response; /* IPP Response */
516 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 517 cups_job_t *temp; /* Temporary pointer */
518 int id, /* job-id */
519 priority, /* job-priority */
520 size; /* job-k-octets */
521 ipp_jstate_t state; /* job-state */
522 time_t completed_time, /* time-at-completed */
523 creation_time, /* time-at-creation */
524 processing_time; /* time-at-processing */
525 const char *dest, /* job-printer-uri */
526 *format, /* document-format */
527 *title, /* job-name */
528 *user; /* job-originating-user-name */
529 char uri[HTTP_MAX_URI]; /* URI for jobs */
530 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
531 static const char * const attrs[] = /* Requested attributes */
532 {
5a662dc0 533 "document-format",
ef416fc2 534 "job-id",
ef416fc2 535 "job-k-octets",
5a662dc0
MS
536 "job-name",
537 "job-originating-user-name",
538 "job-printer-uri",
539 "job-priority",
ef416fc2 540 "job-state",
541 "time-at-completed",
542 "time-at-creation",
5a662dc0 543 "time-at-processing"
ef416fc2 544 };
545
546
547 /*
548 * Range check input...
549 */
550
3d052e43 551 if (!jobs)
ef416fc2 552 {
cb7f98ee 553 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 554
555 return (-1);
556 }
557
558 /*
559 * Get the right URI...
560 */
561
5a738aea 562 if (name)
ef416fc2 563 {
a4d04587 564 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
cb7f98ee
MS
565 "localhost", 0, "/printers/%s",
566 name) < HTTP_URI_STATUS_OK)
ef416fc2 567 {
cb7f98ee
MS
568 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
569 _("Unable to create printer-uri"), 1);
ef416fc2 570
571 return (-1);
572 }
573 }
574 else
5a9febac 575 strlcpy(uri, "ipp://localhost/", sizeof(uri));
ef416fc2 576
3d052e43
MS
577 if (!http)
578 if ((http = _cupsConnect()) == NULL)
579 return (-1);
ef416fc2 580
581 /*
582 * Build an IPP_GET_JOBS request, which requires the following
583 * attributes:
584 *
585 * attributes-charset
586 * attributes-natural-language
587 * printer-uri
588 * requesting-user-name
589 * which-jobs
590 * my-jobs
591 * requested-attributes
592 */
593
cb7f98ee 594 request = ippNewRequest(IPP_OP_GET_JOBS);
ef416fc2 595
596 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
597 "printer-uri", NULL, uri);
598
599 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
600 "requesting-user-name", NULL, cupsUser());
601
602 if (myjobs)
603 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
604
5a738aea 605 if (whichjobs == CUPS_WHICHJOBS_COMPLETED)
ef416fc2 606 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
607 "which-jobs", NULL, "completed");
5a738aea 608 else if (whichjobs == CUPS_WHICHJOBS_ALL)
ecdc0628 609 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
610 "which-jobs", NULL, "all");
ef416fc2 611
612 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
613 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
614 NULL, attrs);
615
616 /*
617 * Do the request and get back a response...
618 */
619
620 n = 0;
621 *jobs = NULL;
622
623 if ((response = cupsDoRequest(http, request, "/")) != NULL)
624 {
3d052e43 625 for (attr = response->attrs; attr; attr = attr->next)
ef416fc2 626 {
627 /*
628 * Skip leading attributes until we hit a job...
629 */
630
3d052e43 631 while (attr && attr->group_tag != IPP_TAG_JOB)
ef416fc2 632 attr = attr->next;
633
3d052e43 634 if (!attr)
ef416fc2 635 break;
636
637 /*
638 * Pull the needed attributes from this job...
639 */
640
641 id = 0;
642 size = 0;
643 priority = 50;
cb7f98ee 644 state = IPP_JSTATE_PENDING;
ef416fc2 645 user = "unknown";
646 dest = NULL;
647 format = "application/octet-stream";
648 title = "untitled";
649 creation_time = 0;
650 completed_time = 0;
651 processing_time = 0;
652
3d052e43 653 while (attr && attr->group_tag == IPP_TAG_JOB)
ef416fc2 654 {
3d052e43 655 if (!strcmp(attr->name, "job-id") &&
ef416fc2 656 attr->value_tag == IPP_TAG_INTEGER)
657 id = attr->values[0].integer;
3d052e43 658 else if (!strcmp(attr->name, "job-state") &&
ef416fc2 659 attr->value_tag == IPP_TAG_ENUM)
660 state = (ipp_jstate_t)attr->values[0].integer;
3d052e43 661 else if (!strcmp(attr->name, "job-priority") &&
ef416fc2 662 attr->value_tag == IPP_TAG_INTEGER)
663 priority = attr->values[0].integer;
3d052e43 664 else if (!strcmp(attr->name, "job-k-octets") &&
ef416fc2 665 attr->value_tag == IPP_TAG_INTEGER)
666 size = attr->values[0].integer;
3d052e43 667 else if (!strcmp(attr->name, "time-at-completed") &&
ef416fc2 668 attr->value_tag == IPP_TAG_INTEGER)
669 completed_time = attr->values[0].integer;
3d052e43 670 else if (!strcmp(attr->name, "time-at-creation") &&
ef416fc2 671 attr->value_tag == IPP_TAG_INTEGER)
672 creation_time = attr->values[0].integer;
3d052e43 673 else if (!strcmp(attr->name, "time-at-processing") &&
ef416fc2 674 attr->value_tag == IPP_TAG_INTEGER)
675 processing_time = attr->values[0].integer;
3d052e43 676 else if (!strcmp(attr->name, "job-printer-uri") &&
ef416fc2 677 attr->value_tag == IPP_TAG_URI)
678 {
679 if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
680 dest ++;
681 }
3d052e43 682 else if (!strcmp(attr->name, "job-originating-user-name") &&
ef416fc2 683 attr->value_tag == IPP_TAG_NAME)
684 user = attr->values[0].string.text;
3d052e43 685 else if (!strcmp(attr->name, "document-format") &&
ef416fc2 686 attr->value_tag == IPP_TAG_MIMETYPE)
687 format = attr->values[0].string.text;
3d052e43 688 else if (!strcmp(attr->name, "job-name") &&
ef416fc2 689 (attr->value_tag == IPP_TAG_TEXT ||
690 attr->value_tag == IPP_TAG_NAME))
691 title = attr->values[0].string.text;
692
693 attr = attr->next;
694 }
695
696 /*
697 * See if we have everything needed...
698 */
699
3d052e43 700 if (!dest || !id)
ef416fc2 701 {
3d052e43 702 if (!attr)
ef416fc2 703 break;
704 else
705 continue;
706 }
707
708 /*
709 * Allocate memory for the job...
710 */
711
712 if (n == 0)
713 temp = malloc(sizeof(cups_job_t));
714 else
7e86f2f6 715 temp = realloc(*jobs, sizeof(cups_job_t) * (size_t)(n + 1));
ef416fc2 716
3d052e43 717 if (!temp)
ef416fc2 718 {
719 /*
720 * Ran out of memory!
721 */
722
cb7f98ee 723 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
3d052e43 724
ef416fc2 725 cupsFreeJobs(n, *jobs);
726 *jobs = NULL;
727
728 ippDelete(response);
3d052e43
MS
729
730 return (-1);
ef416fc2 731 }
732
733 *jobs = temp;
734 temp += n;
735 n ++;
736
737 /*
738 * Copy the data over...
739 */
740
3d052e43
MS
741 temp->dest = _cupsStrAlloc(dest);
742 temp->user = _cupsStrAlloc(user);
743 temp->format = _cupsStrAlloc(format);
744 temp->title = _cupsStrAlloc(title);
ef416fc2 745 temp->id = id;
746 temp->priority = priority;
747 temp->state = state;
748 temp->size = size;
749 temp->completed_time = completed_time;
750 temp->creation_time = creation_time;
751 temp->processing_time = processing_time;
752
3d052e43 753 if (!attr)
ef416fc2 754 break;
755 }
756
757 ippDelete(response);
758 }
759
cb7f98ee 760 if (n == 0 && cg->last_error >= IPP_STATUS_ERROR_BAD_REQUEST)
ef416fc2 761 return (-1);
762 else
763 return (n);
764}
765
766
767/*
768 * 'cupsGetPPD()' - Get the PPD file for a printer on the default server.
769 *
5a738aea 770 * For classes, @code cupsGetPPD@ returns the PPD file for the first printer
ef416fc2 771 * in the class.
568fa3fa
MS
772 *
773 * The returned filename is stored in a static buffer and is overwritten with
4e6f60f0
MS
774 * each call to @code cupsGetPPD@ or @link cupsGetPPD2@. The caller "owns" the
775 * file that is created and must @code unlink@ the returned filename.
ef416fc2 776 */
777
778const char * /* O - Filename for PPD file */
568fa3fa 779cupsGetPPD(const char *name) /* I - Destination name */
ef416fc2 780{
781 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
2e4ff8af
MS
782 time_t modtime = 0; /* Modification time */
783
ef416fc2 784
ef416fc2 785 /*
786 * Return the PPD file...
787 */
788
2e4ff8af
MS
789 cg->ppd_filename[0] = '\0';
790
3d052e43 791 if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, cg->ppd_filename,
cb7f98ee 792 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
2e4ff8af
MS
793 return (cg->ppd_filename);
794 else
795 return (NULL);
ef416fc2 796}
797
798
799/*
800 * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
801 *
5a738aea 802 * For classes, @code cupsGetPPD2@ returns the PPD file for the first printer
ef416fc2 803 * in the class.
804 *
568fa3fa 805 * The returned filename is stored in a static buffer and is overwritten with
4e6f60f0
MS
806 * each call to @link cupsGetPPD@ or @code cupsGetPPD2@. The caller "owns" the
807 * file that is created and must @code unlink@ the returned filename.
568fa3fa 808 *
f3c17241 809 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 810 */
811
812const char * /* O - Filename for PPD file */
568fa3fa
MS
813cupsGetPPD2(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
814 const char *name) /* I - Destination name */
2e4ff8af
MS
815{
816 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
817 time_t modtime = 0; /* Modification time */
818
819
820 cg->ppd_filename[0] = '\0';
821
822 if (cupsGetPPD3(http, name, &modtime, cg->ppd_filename,
cb7f98ee 823 sizeof(cg->ppd_filename)) == HTTP_STATUS_OK)
2e4ff8af
MS
824 return (cg->ppd_filename);
825 else
826 return (NULL);
827}
828
829
830/*
831 * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
832 * server if it has changed.
833 *
834 * The "modtime" parameter contains the modification time of any
835 * locally-cached content and is updated with the time from the PPD file on
836 * the server.
837 *
838 * The "buffer" parameter contains the local PPD filename. If it contains
839 * the empty string, a new temporary file is created, otherwise the existing
4e6f60f0
MS
840 * file will be overwritten as needed. The caller "owns" the file that is
841 * created and must @code unlink@ the returned filename.
2e4ff8af 842 *
cb7f98ee
MS
843 * On success, @code HTTP_STATUS_OK@ is returned for a new PPD file and
844 * @code HTTP_STATUS_NOT_MODIFIED@ if the existing PPD file is up-to-date. Any other
5a738aea
MS
845 * status is an error.
846 *
847 * For classes, @code cupsGetPPD3@ returns the PPD file for the first printer
848 * in the class.
568fa3fa 849 *
f3c17241 850 * @since CUPS 1.4/OS X 10.6@
2e4ff8af
MS
851 */
852
853http_status_t /* O - HTTP status */
5a738aea 854cupsGetPPD3(http_t *http, /* I - HTTP connection or @code CUPS_HTTP_DEFAULT@ */
568fa3fa 855 const char *name, /* I - Destination name */
2e4ff8af
MS
856 time_t *modtime, /* IO - Modification time */
857 char *buffer, /* I - Filename buffer */
858 size_t bufsize) /* I - Size of filename buffer */
ef416fc2 859{
ef416fc2 860 int http_port; /* Port number */
ed486911 861 char http_hostname[HTTP_MAX_HOST];
862 /* Hostname associated with connection */
ef416fc2 863 http_t *http2; /* Alternate HTTP connection */
ef416fc2 864 int fd; /* PPD file */
fa73b229 865 char localhost[HTTP_MAX_URI],/* Local hostname */
ef416fc2 866 hostname[HTTP_MAX_URI], /* Hostname */
867 resource[HTTP_MAX_URI]; /* Resource name */
868 int port; /* Port number */
869 http_status_t status; /* HTTP status from server */
12f89d24 870 char tempfile[1024] = ""; /* Temporary filename */
ef416fc2 871 _cups_globals_t *cg = _cupsGlobals(); /* Pointer to library globals */
ef416fc2 872
873
874 /*
875 * Range check input...
876 */
877
2e4ff8af 878 DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, "
e07d4801 879 "bufsize=%d)", http, name, modtime,
ae71f5de 880 modtime ? (int)*modtime : 0, buffer, (int)bufsize));
ef416fc2 881
2e4ff8af
MS
882 if (!name)
883 {
cb7f98ee
MS
884 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer name"), 1);
885 return (HTTP_STATUS_NOT_ACCEPTABLE);
2e4ff8af
MS
886 }
887
888 if (!modtime)
889 {
cb7f98ee
MS
890 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No modification time"), 1);
891 return (HTTP_STATUS_NOT_ACCEPTABLE);
2e4ff8af
MS
892 }
893
894 if (!buffer || bufsize <= 1)
895 {
cb7f98ee
MS
896 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad filename buffer"), 1);
897 return (HTTP_STATUS_NOT_ACCEPTABLE);
ef416fc2 898 }
899
426c6a59
MS
900#ifndef WIN32
901 /*
902 * See if the PPD file is available locally...
903 */
904
c1420c87
MS
905 if (http)
906 httpGetHostname(http, hostname, sizeof(hostname));
907 else
908 {
909 strlcpy(hostname, cupsServer(), sizeof(hostname));
910 if (hostname[0] == '/')
911 strlcpy(hostname, "localhost", sizeof(hostname));
912 }
426c6a59 913
c1420c87 914 if (!_cups_strcasecmp(hostname, "localhost"))
426c6a59
MS
915 {
916 char ppdname[1024]; /* PPD filename */
917 struct stat ppdinfo; /* PPD file information */
918
919
d1c13e16
MS
920 snprintf(ppdname, sizeof(ppdname), "%s/ppd/%s.ppd", cg->cups_serverroot,
921 name);
426c6a59
MS
922 if (!stat(ppdname, &ppdinfo))
923 {
924 /*
925 * OK, the file exists, use it!
926 */
927
928 if (buffer[0])
929 {
930 unlink(buffer);
931
932 if (symlink(ppdname, buffer) && errno != EEXIST)
933 {
cb7f98ee 934 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
426c6a59 935
cb7f98ee 936 return (HTTP_STATUS_SERVER_ERROR);
426c6a59
MS
937 }
938 }
939 else
940 {
941 int tries; /* Number of tries */
942 const char *tmpdir; /* TMPDIR environment variable */
943 struct timeval curtime; /* Current time */
944
945 /*
946 * Previously we put root temporary files in the default CUPS temporary
947 * directory under /var/spool/cups. However, since the scheduler cleans
948 * out temporary files there and runs independently of the user apps, we
949 * don't want to use it unless specifically told to by cupsd.
950 */
951
952 if ((tmpdir = getenv("TMPDIR")) == NULL)
953# ifdef __APPLE__
954 tmpdir = "/private/tmp"; /* /tmp is a symlink to /private/tmp */
955# else
956 tmpdir = "/tmp";
957# endif /* __APPLE__ */
958
959 /*
960 * Make the temporary name using the specified directory...
961 */
962
963 tries = 0;
964
965 do
966 {
967 /*
968 * Get the current time of day...
969 */
970
971 gettimeofday(&curtime, NULL);
972
973 /*
974 * Format a string using the hex time values...
975 */
976
977 snprintf(buffer, bufsize, "%s/%08lx%05lx", tmpdir,
978 (unsigned long)curtime.tv_sec,
979 (unsigned long)curtime.tv_usec);
980
981 /*
982 * Try to make a symlink...
983 */
984
985 if (!symlink(ppdname, buffer))
986 break;
987
988 tries ++;
989 }
990 while (tries < 1000);
991
992 if (tries >= 1000)
993 {
cb7f98ee 994 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
426c6a59 995
cb7f98ee 996 return (HTTP_STATUS_SERVER_ERROR);
426c6a59
MS
997 }
998 }
999
d1c13e16 1000 if (*modtime >= ppdinfo.st_mtime)
cb7f98ee 1001 return (HTTP_STATUS_NOT_MODIFIED);
426c6a59
MS
1002 else
1003 {
1004 *modtime = ppdinfo.st_mtime;
cb7f98ee 1005 return (HTTP_STATUS_OK);
426c6a59
MS
1006 }
1007 }
1008 }
1009#endif /* !WIN32 */
1010
ef416fc2 1011 /*
fa73b229 1012 * Try finding a printer URI for this printer...
ef416fc2 1013 */
1014
3d052e43 1015 if (!http)
a603edef 1016 if ((http = _cupsConnect()) == NULL)
cb7f98ee 1017 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
3d052e43 1018
fa73b229 1019 if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port,
1020 resource, sizeof(resource), 0))
cb7f98ee 1021 return (HTTP_STATUS_NOT_FOUND);
ef416fc2 1022
e07d4801 1023 DEBUG_printf(("2cupsGetPPD3: Printer hostname=\"%s\", port=%d", hostname,
1ff0402e 1024 port));
8ca02f3c 1025
33c9220c 1026 if (cupsServer()[0] == '/' && !_cups_strcasecmp(hostname, "localhost") && port == ippPort())
1e9586e1
MS
1027 {
1028 /*
1029 * Redirect localhost to domain socket...
1030 */
1031
1032 strlcpy(hostname, cupsServer(), sizeof(hostname));
1033 port = 0;
1034
1035 DEBUG_printf(("2cupsGetPPD3: Redirecting to \"%s\".", hostname));
1036 }
1037
ef416fc2 1038 /*
fa73b229 1039 * Remap local hostname to localhost...
1040 */
1041
757d2cad 1042 httpGetHostname(NULL, localhost, sizeof(localhost));
fa73b229 1043
e07d4801 1044 DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost));
8ca02f3c 1045
88f9aafc 1046 if (!_cups_strcasecmp(localhost, hostname))
5a9febac 1047 strlcpy(hostname, "localhost", sizeof(hostname));
fa73b229 1048
1049 /*
ed486911 1050 * Get the hostname and port number we are connected to...
ef416fc2 1051 */
1052
ed486911 1053 httpGetHostname(http, http_hostname, sizeof(http_hostname));
a469f8a5 1054 http_port = httpAddrPort(http->hostaddr);
ed486911 1055
e07d4801 1056 DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d",
1ff0402e 1057 http_hostname, http_port));
8ca02f3c 1058
ef416fc2 1059 /*
1060 * Reconnect to the correct server as needed...
1061 */
1062
88f9aafc 1063 if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port)
ef416fc2 1064 http2 = http;
cb7f98ee
MS
1065 else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC,
1066 cupsEncryption(), 1, 30000, NULL)) == NULL)
ef416fc2 1067 {
4d301e69 1068 DEBUG_puts("1cupsGetPPD3: Unable to connect to server");
ef416fc2 1069
cb7f98ee 1070 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
ef416fc2 1071 }
1072
1073 /*
1074 * Get a temp file...
1075 */
1076
2e4ff8af
MS
1077 if (buffer[0])
1078 fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
1079 else
12f89d24 1080 fd = cupsTempFd(tempfile, sizeof(tempfile));
2e4ff8af
MS
1081
1082 if (fd < 0)
ef416fc2 1083 {
1084 /*
1085 * Can't open file; close the server connection and return NULL...
1086 */
1087
cb7f98ee 1088 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
ef416fc2 1089
1090 if (http2 != http)
1091 httpClose(http2);
1092
cb7f98ee 1093 return (HTTP_STATUS_SERVER_ERROR);
ef416fc2 1094 }
1095
1096 /*
1097 * And send a request to the HTTP server...
1098 */
1099
fa73b229 1100 strlcat(resource, ".ppd", sizeof(resource));
ef416fc2 1101
2e4ff8af
MS
1102 if (*modtime > 0)
1103 httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
1104 httpGetDateString(*modtime));
1105
ef416fc2 1106 status = cupsGetFd(http2, resource, fd);
1107
1108 close(fd);
1109
ef416fc2 1110 /*
1111 * See if we actually got the file or an error...
1112 */
1113
cb7f98ee 1114 if (status == HTTP_STATUS_OK)
12f89d24 1115 {
2e4ff8af 1116 *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
12f89d24
MS
1117
1118 if (tempfile[0])
1119 strlcpy(buffer, tempfile, bufsize);
1120 }
cb7f98ee 1121 else if (status != HTTP_STATUS_NOT_MODIFIED)
ef416fc2 1122 {
e07d4801 1123 _cupsSetHTTPError(status);
ef416fc2 1124
12f89d24
MS
1125 if (buffer[0])
1126 unlink(buffer);
1127 else if (tempfile[0])
1128 unlink(tempfile);
ef416fc2 1129 }
12f89d24
MS
1130 else if (tempfile[0])
1131 unlink(tempfile);
ef416fc2 1132
91c84a35
MS
1133 if (http2 != http)
1134 httpClose(http2);
1135
ef416fc2 1136 /*
1137 * Return the PPD file...
1138 */
1139
e07d4801
MS
1140 DEBUG_printf(("1cupsGetPPD3: Returning status %d", status));
1141
2e4ff8af 1142 return (status);
ef416fc2 1143}
1144
1145
1146/*
1147 * 'cupsGetPrinters()' - Get a list of printers from the default server.
1148 *
5a738aea 1149 * This function is deprecated - use @link cupsGetDests@ instead.
ef416fc2 1150 *
1151 * @deprecated@
1152 */
1153
1154int /* O - Number of printers */
1155cupsGetPrinters(char ***printers) /* O - Printers */
1156{
1157 int n; /* Number of printers */
1158 ipp_t *request, /* IPP Request */
1159 *response; /* IPP Response */
1160 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 1161 char **temp; /* Temporary pointer */
3d052e43
MS
1162 http_t *http; /* Connection to server */
1163
ef416fc2 1164
3d052e43
MS
1165 /*
1166 * Range check input...
1167 */
ef416fc2 1168
3d052e43 1169 if (!printers)
ef416fc2 1170 {
cb7f98ee 1171 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 1172
1173 return (0);
1174 }
1175
3d052e43
MS
1176 *printers = NULL;
1177
ef416fc2 1178 /*
1179 * Try to connect to the server...
1180 */
1181
3d052e43 1182 if ((http = _cupsConnect()) == NULL)
ef416fc2 1183 return (0);
ef416fc2 1184
1185 /*
1186 * Build a CUPS_GET_PRINTERS request, which requires the following
1187 * attributes:
1188 *
1189 * attributes-charset
1190 * attributes-natural-language
1191 * requested-attributes
1192 */
1193
cb7f98ee 1194 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
ef416fc2 1195
1196 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1197 "requested-attributes", NULL, "printer-name");
1198
1199 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1200 "printer-type", 0);
1201
1202 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1203 "printer-type-mask", CUPS_PRINTER_CLASS);
1204
1205 /*
1206 * Do the request and get back a response...
1207 */
1208
3d052e43 1209 n = 0;
ef416fc2 1210
3d052e43 1211 if ((response = cupsDoRequest(http, request, "/")) != NULL)
ef416fc2 1212 {
1213 for (attr = response->attrs; attr != NULL; attr = attr->next)
1214 if (attr->name != NULL &&
88f9aafc 1215 _cups_strcasecmp(attr->name, "printer-name") == 0 &&
ef416fc2 1216 attr->value_tag == IPP_TAG_NAME)
1217 {
1218 if (n == 0)
1219 temp = malloc(sizeof(char *));
1220 else
7e86f2f6 1221 temp = realloc(*printers, sizeof(char *) * (size_t)(n + 1));
ef416fc2 1222
1223 if (temp == NULL)
1224 {
1225 /*
1226 * Ran out of memory!
1227 */
1228
1229 while (n > 0)
1230 {
1231 n --;
1232 free((*printers)[n]);
1233 }
1234
1235 free(*printers);
1236 ippDelete(response);
1237 return (0);
1238 }
1239
1240 *printers = temp;
1241 temp[n] = strdup(attr->values[0].string.text);
1242 n ++;
1243 }
1244
1245 ippDelete(response);
1246 }
1247
1248 return (n);
1249}
1250
1251
b94498cf 1252/*
1253 * 'cupsGetServerPPD()' - Get an available PPD file from the server.
1254 *
1255 * This function returns the named PPD file from the server. The
5a738aea 1256 * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@
b94498cf 1257 * operation.
1258 *
1259 * You must remove (unlink) the PPD file when you are finished with
1260 * it. The PPD filename is stored in a static location that will be
5a738aea
MS
1261 * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@,
1262 * or @link cupsGetServerPPD@.
b94498cf 1263 *
f3c17241 1264 * @since CUPS 1.3/OS X 10.5@
b94498cf 1265 */
1266
5a738aea 1267char * /* O - Name of PPD file or @code NULL@ on error */
568fa3fa 1268cupsGetServerPPD(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
b94498cf 1269 const char *name) /* I - Name of PPD file ("ppd-name") */
1270{
1271 int fd; /* PPD file descriptor */
1272 ipp_t *request; /* IPP request */
1273 _cups_globals_t *cg = _cupsGlobals();
1274 /* Pointer to library globals */
1275
1276
1277 /*
1278 * Range check input...
1279 */
1280
3d052e43 1281 if (!name)
b94498cf 1282 {
cb7f98ee 1283 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1);
b94498cf 1284
1285 return (NULL);
1286 }
1287
3d052e43
MS
1288 if (!http)
1289 if ((http = _cupsConnect()) == NULL)
1290 return (NULL);
1291
b94498cf 1292 /*
1293 * Get a temp file...
1294 */
1295
1296 if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
1297 {
1298 /*
1299 * Can't open file; close the server connection and return NULL...
1300 */
1301
cb7f98ee 1302 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
b94498cf 1303
1304 return (NULL);
1305 }
1306
1307 /*
1308 * Get the PPD file...
1309 */
1310
cb7f98ee 1311 request = ippNewRequest(IPP_OP_CUPS_GET_PPD);
b94498cf 1312 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
1313 name);
1314
1315 ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
1316
1317 close(fd);
1318
cb7f98ee 1319 if (cupsLastError() != IPP_STATUS_OK)
b94498cf 1320 {
1321 unlink(cg->ppd_filename);
1322 return (NULL);
1323 }
1324 else
1325 return (cg->ppd_filename);
1326}
1327
1328
ef416fc2 1329/*
1330 * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
1331 */
1332
5a738aea 1333int /* O - Job ID or 0 on error */
568fa3fa 1334cupsPrintFile(const char *name, /* I - Destination name */
ef416fc2 1335 const char *filename, /* I - File to print */
1336 const char *title, /* I - Title of job */
1337 int num_options,/* I - Number of options */
1338 cups_option_t *options) /* I - Options */
1339{
1340 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
e07d4801 1341 "title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1342 name, filename, title, num_options, options));
1343
3d052e43
MS
1344 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
1345 num_options, options));
ef416fc2 1346}
1347
1348
1349/*
5a738aea
MS
1350 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
1351 * server.
ef416fc2 1352 *
f3c17241 1353 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 1354 */
1355
5a738aea 1356int /* O - Job ID or 0 on error */
3d052e43 1357cupsPrintFile2(
568fa3fa
MS
1358 http_t *http, /* I - Connection to server */
1359 const char *name, /* I - Destination name */
3d052e43
MS
1360 const char *filename, /* I - File to print */
1361 const char *title, /* I - Title of job */
1362 int num_options, /* I - Number of options */
1363 cups_option_t *options) /* I - Options */
ef416fc2 1364{
1365 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
e07d4801 1366 "title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1367 http, name, filename, title, num_options, options));
1368
3d052e43
MS
1369 return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
1370 options));
ef416fc2 1371}
1372
1373
1374/*
fa73b229 1375 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
1376 * default server.
ef416fc2 1377 */
1378
5a738aea 1379int /* O - Job ID or 0 on error */
3d052e43 1380cupsPrintFiles(
568fa3fa 1381 const char *name, /* I - Destination name */
3d052e43
MS
1382 int num_files, /* I - Number of files */
1383 const char **files, /* I - File(s) to print */
1384 const char *title, /* I - Title of job */
1385 int num_options, /* I - Number of options */
1386 cups_option_t *options) /* I - Options */
ef416fc2 1387{
ef416fc2 1388 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
e07d4801 1389 "files=%p, title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1390 name, num_files, files, title, num_options, options));
1391
ef416fc2 1392 /*
1393 * Print the file(s)...
1394 */
1395
3d052e43 1396 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
ef416fc2 1397 num_options, options));
1398}
1399
1400
ef416fc2 1401/*
fa73b229 1402 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
1403 * specified server.
ef416fc2 1404 *
f3c17241 1405 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 1406 */
1407
5a738aea 1408int /* O - Job ID or 0 on error */
3d052e43 1409cupsPrintFiles2(
568fa3fa
MS
1410 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1411 const char *name, /* I - Destination name */
3d052e43
MS
1412 int num_files, /* I - Number of files */
1413 const char **files, /* I - File(s) to print */
1414 const char *title, /* I - Title of job */
1415 int num_options, /* I - Number of options */
1416 cups_option_t *options) /* I - Options */
ef416fc2 1417{
1418 int i; /* Looping var */
3d052e43
MS
1419 int job_id; /* New job ID */
1420 const char *docname; /* Basename of current filename */
1421 const char *format; /* Document format */
1422 cups_file_t *fp; /* Current file */
1423 char buffer[8192]; /* Copy buffer */
1424 ssize_t bytes; /* Bytes in buffer */
1425 http_status_t status; /* Status of write */
5a662dc0
MS
1426 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1427 ipp_status_t cancel_status; /* Status code to preserve */
1428 char *cancel_message; /* Error message to preserve */
ef416fc2 1429
1430
3d052e43 1431 DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
e07d4801 1432 "files=%p, title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1433 http, name, num_files, files, title, num_options, options));
1434
1435 /*
1436 * Range check input...
1437 */
1438
3d052e43 1439 if (!name || num_files < 1 || !files)
ef416fc2 1440 {
cb7f98ee 1441 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 1442
1443 return (0);
1444 }
1445
1446 /*
3d052e43 1447 * Create the print job...
ef416fc2 1448 */
1449
3d052e43 1450 if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
ef416fc2 1451 return (0);
ef416fc2 1452
1453 /*
3d052e43 1454 * Send each of the files...
ef416fc2 1455 */
1456
3d052e43
MS
1457 if (cupsGetOption("raw", num_options, options))
1458 format = CUPS_FORMAT_RAW;
1459 else if ((format = cupsGetOption("document-format", num_options,
1460 options)) == NULL)
1461 format = CUPS_FORMAT_AUTO;
ef416fc2 1462
3d052e43
MS
1463 for (i = 0; i < num_files; i ++)
1464 {
1465 /*
1466 * Start the next file...
1467 */
ef416fc2 1468
3d052e43
MS
1469 if ((docname = strrchr(files[i], '/')) != NULL)
1470 docname ++;
1471 else
1472 docname = files[i];
ef416fc2 1473
3d052e43
MS
1474 if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
1475 {
1476 /*
1477 * Unable to open print file, cancel the job and return...
1478 */
ef416fc2 1479
cb7f98ee 1480 _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0);
5a662dc0 1481 goto cancel_job;
3d052e43 1482 }
ef416fc2 1483
ba55dc12
MS
1484 status = cupsStartDocument(http, name, job_id, docname, format,
1485 i == (num_files - 1));
ef416fc2 1486
cb7f98ee 1487 while (status == HTTP_STATUS_CONTINUE &&
ba55dc12 1488 (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
7e86f2f6 1489 status = cupsWriteRequestData(http, buffer, (size_t)bytes);
ef416fc2 1490
3d052e43 1491 cupsFileClose(fp);
ef416fc2 1492
cb7f98ee 1493 if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK)
ef416fc2 1494 {
1495 /*
3d052e43 1496 * Unable to queue, cancel the job and return...
ef416fc2 1497 */
1498
5a662dc0 1499 goto cancel_job;
3d052e43
MS
1500 }
1501 }
ef416fc2 1502
3d052e43 1503 return (job_id);
5a662dc0
MS
1504
1505 /*
1506 * If we get here, something happened while sending the print job so we need
1507 * to cancel the job without setting the last error (since we need to preserve
1508 * the current error...
1509 */
1510
1511 cancel_job:
1512
1513 cancel_status = cg->last_error;
1514 cancel_message = cg->last_status_message ?
1515 _cupsStrRetain(cg->last_status_message) : NULL;
1516
1517 cupsCancelJob2(http, name, job_id, 0);
1518
1519 cg->last_error = cancel_status;
1520 cg->last_status_message = cancel_message;
1521
1522 return (0);
3d052e43 1523}
ef416fc2 1524
ef416fc2 1525
3d052e43
MS
1526/*
1527 * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
1528 *
5a738aea
MS
1529 * Use @link cupsWriteRequestData@ to write data for the document and
1530 * @link cupsFinishDocument@ to finish the document and get the submission status.
3d052e43 1531 *
5a738aea
MS
1532 * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@,
1533 * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and
1534 * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
1535 * any supported MIME type string can be supplied.
3d052e43 1536 *
f3c17241 1537 * @since CUPS 1.4/OS X 10.6@
3d052e43 1538 */
ef416fc2 1539
3d052e43
MS
1540http_status_t /* O - HTTP status of request */
1541cupsStartDocument(
568fa3fa
MS
1542 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1543 const char *name, /* I - Destination name */
5a738aea 1544 int job_id, /* I - Job ID from @link cupsCreateJob@ */
3d052e43 1545 const char *docname, /* I - Name of document */
5a738aea 1546 const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */
3d052e43
MS
1547 int last_document) /* I - 1 for last document in job, 0 otherwise */
1548{
1549 char resource[1024], /* Resource for destinatio */
1550 printer_uri[1024]; /* Printer URI */
1551 ipp_t *request; /* Send-Document request */
1552 http_status_t status; /* HTTP status */
ef416fc2 1553
bd7854cb 1554
3d052e43
MS
1555 /*
1556 * Create a Send-Document request...
1557 */
bd7854cb 1558
cb7f98ee 1559 if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
3d052e43 1560 {
cb7f98ee
MS
1561 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
1562 return (HTTP_STATUS_ERROR);
3d052e43 1563 }
bd7854cb 1564
3d052e43
MS
1565 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
1566 NULL, "localhost", ippPort(), "/printers/%s", name);
1567 snprintf(resource, sizeof(resource), "/printers/%s", name);
ef416fc2 1568
3d052e43
MS
1569 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1570 NULL, printer_uri);
1571 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
1572 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1573 NULL, cupsUser());
1574 if (docname)
1575 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
1576 NULL, docname);
1577 if (format)
1578 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1579 "document-format", NULL, format);
7e86f2f6 1580 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
ef416fc2 1581
3d052e43
MS
1582 /*
1583 * Send and delete the request, then return the status...
1584 */
ef416fc2 1585
3d052e43 1586 status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
ef416fc2 1587
3d052e43 1588 ippDelete(request);
ef416fc2 1589
3d052e43 1590 return (status);
ef416fc2 1591}
1592
1593
fa73b229 1594/*
58dc1933
MS
1595 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the
1596 * first printer in a class.
fa73b229 1597 */
1598
1599static int /* O - 1 on success, 0 on failure */
1600cups_get_printer_uri(
568fa3fa 1601 http_t *http, /* I - Connection to server */
fa73b229 1602 const char *name, /* I - Name of printer or class */
1603 char *host, /* I - Hostname buffer */
1604 int hostsize, /* I - Size of hostname buffer */
1605 int *port, /* O - Port number */
1606 char *resource, /* I - Resource buffer */
1607 int resourcesize, /* I - Size of resource buffer */
1608 int depth) /* I - Depth of query */
1609{
1610 int i; /* Looping var */
1611 int http_port; /* Port number */
1612 http_t *http2; /* Alternate HTTP connection */
1613 ipp_t *request, /* IPP request */
1614 *response; /* IPP response */
1615 ipp_attribute_t *attr; /* Current attribute */
1616 char uri[HTTP_MAX_URI], /* printer-uri attribute */
1617 scheme[HTTP_MAX_URI], /* Scheme name */
1618 username[HTTP_MAX_URI], /* Username:password */
ed486911 1619 classname[255], /* Temporary class name */
1620 http_hostname[HTTP_MAX_HOST];
1621 /* Hostname associated with connection */
fa73b229 1622 static const char * const requested_attrs[] =
1623 { /* Requested attributes */
37e7e6e0 1624 "device-uri",
58dc1933 1625 "member-uris",
fa73b229 1626 "printer-uri-supported",
58dc1933 1627 "printer-type"
fa73b229 1628 };
1629
1630
1e9586e1 1631 DEBUG_printf(("4cups_get_printer_uri(http=%p, name=\"%s\", host=%p, hostsize=%d, resource=%p, resourcesize=%d, depth=%d)", http, name, host, hostsize, resource, resourcesize, depth));
fa73b229 1632
1633 /*
1634 * Setup the printer URI...
1635 */
1636
1e9586e1 1637 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL, "localhost", 0, "/printers/%s", name) < HTTP_URI_STATUS_OK)
fa73b229 1638 {
1e9586e1 1639 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"), 1);
fa73b229 1640
1641 *host = '\0';
1642 *resource = '\0';
1643
1644 return (0);
1645 }
1646
1e9586e1 1647 DEBUG_printf(("5cups_get_printer_uri: printer-uri=\"%s\"", uri));
fa73b229 1648
1649 /*
ed486911 1650 * Get the hostname and port number we are connected to...
fa73b229 1651 */
1652
ed486911 1653 httpGetHostname(http, http_hostname, sizeof(http_hostname));
a469f8a5 1654 http_port = httpAddrPort(http->hostaddr);
fa73b229 1655
1e9586e1
MS
1656 DEBUG_printf(("5cups_get_printer_uri: http_hostname=\"%s\"", http_hostname));
1657
fa73b229 1658 /*
1659 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1660 * attributes:
1661 *
1662 * attributes-charset
1663 * attributes-natural-language
1664 * printer-uri
1665 * requested-attributes
1666 */
1667
cb7f98ee 1668 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
fa73b229 1669
1e9586e1 1670 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
fa73b229 1671
1e9586e1 1672 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requested-attributes", sizeof(requested_attrs) / sizeof(requested_attrs[0]), NULL, requested_attrs);
fa73b229 1673
1674 /*
1675 * Do the request and get back a response...
1676 */
1677
07623986 1678 snprintf(resource, (size_t)resourcesize, "/printers/%s", name);
a469f8a5
MS
1679
1680 if ((response = cupsDoRequest(http, request, resource)) != NULL)
fa73b229 1681 {
37e7e6e0
MS
1682 const char *device_uri = NULL; /* device-uri value */
1683
1e9586e1
MS
1684 if ((attr = ippFindAttribute(response, "device-uri", IPP_TAG_URI)) != NULL)
1685 {
37e7e6e0 1686 device_uri = attr->values[0].string.text;
1e9586e1
MS
1687 DEBUG_printf(("5cups_get_printer_uri: device-uri=\"%s\"", device_uri));
1688 }
37e7e6e0
MS
1689
1690 if (device_uri &&
c5b24bfa
MS
1691 (!strncmp(device_uri, "ipp://", 6) ||
1692 !strncmp(device_uri, "ipps://", 7) ||
1693 ((strstr(device_uri, "._ipp.") != NULL ||
1694 strstr(device_uri, "._ipps.") != NULL) &&
1695 !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
37e7e6e0
MS
1696 {
1697 /*
c5b24bfa 1698 * Statically-configured shared printer.
37e7e6e0
MS
1699 */
1700
1e9586e1 1701 httpSeparateURI(HTTP_URI_CODING_ALL, _httpResolveURI(device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL), scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
37e7e6e0
MS
1702 ippDelete(response);
1703
1e9586e1 1704 DEBUG_printf(("5cups_get_printer_uri: Resolved to host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
37e7e6e0
MS
1705 return (1);
1706 }
1e9586e1 1707 else if ((attr = ippFindAttribute(response, "member-uris", IPP_TAG_URI)) != NULL)
fa73b229 1708 {
1709 /*
1710 * Get the first actual printer name in the class...
1711 */
1712
1e9586e1
MS
1713 DEBUG_printf(("5cups_get_printer_uri: Got member-uris with %d values.", ippGetCount(attr)));
1714
fa73b229 1715 for (i = 0; i < attr->num_values; i ++)
1716 {
1e9586e1
MS
1717 DEBUG_printf(("5cups_get_printer_uri: member-uris[%d]=\"%s\"", i, ippGetString(attr, i, NULL)));
1718
1719 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
fa73b229 1720 if (!strncmp(resource, "/printers/", 10))
1721 {
1722 /*
1723 * Found a printer!
1724 */
1725
1726 ippDelete(response);
1727
1e9586e1 1728 DEBUG_printf(("5cups_get_printer_uri: Found printer member with host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
fa73b229 1729 return (1);
1730 }
1731 }
1732
1733 /*
1734 * No printers in this class - try recursively looking for a printer,
1735 * but not more than 3 levels deep...
1736 */
1737
1738 if (depth < 3)
1739 {
1740 for (i = 0; i < attr->num_values; i ++)
1741 {
a4d04587 1742 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
1743 scheme, sizeof(scheme), username, sizeof(username),
1744 host, hostsize, port, resource, resourcesize);
fa73b229 1745 if (!strncmp(resource, "/classes/", 9))
1746 {
1747 /*
1748 * Found a class! Connect to the right server...
1749 */
1750
88f9aafc 1751 if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
fa73b229 1752 http2 = http;
1e9586e1 1753 else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC, cupsEncryption(), 1, 30000, NULL)) == NULL)
fa73b229 1754 {
4d301e69 1755 DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
fa73b229 1756
1757 continue;
1758 }
1759
1760 /*
1761 * Look up printers on that server...
1762 */
1763
1764 strlcpy(classname, resource + 9, sizeof(classname));
1765
1766 cups_get_printer_uri(http2, classname, host, hostsize, port,
1767 resource, resourcesize, depth + 1);
1768
1769 /*
1770 * Close the connection as needed...
1771 */
1772
1773 if (http2 != http)
1774 httpClose(http2);
1775
1776 if (*host)
1777 return (1);
1778 }
1779 }
1780 }
1781 }
1e9586e1 1782 else if ((attr = ippFindAttribute(response, "printer-uri-supported", IPP_TAG_URI)) != NULL)
fa73b229 1783 {
1e9586e1 1784 httpSeparateURI(HTTP_URI_CODING_ALL, _httpResolveURI(attr->values[0].string.text, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL), scheme, sizeof(scheme), username, sizeof(username), host, hostsize, port, resource, resourcesize);
fa73b229 1785 ippDelete(response);
1786
1e9586e1
MS
1787 DEBUG_printf(("5cups_get_printer_uri: Resolved to host=\"%s\", port=%d, resource=\"%s\"", host, *port, resource));
1788
dd1abb6b
MS
1789 if (!strncmp(resource, "/classes/", 9))
1790 {
1e9586e1 1791 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found for class"), 1);
dd1abb6b
MS
1792
1793 *host = '\0';
1794 *resource = '\0';
1795
1e9586e1 1796 DEBUG_puts("5cups_get_printer_uri: Not returning class.");
dd1abb6b
MS
1797 return (0);
1798 }
1799
fa73b229 1800 return (1);
1801 }
1802
1803 ippDelete(response);
1804 }
1805
cb7f98ee
MS
1806 if (cupsLastError() != IPP_STATUS_ERROR_NOT_FOUND)
1807 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No printer-uri found"), 1);
b423cd4c 1808
fa73b229 1809 *host = '\0';
1810 *resource = '\0';
1811
1e9586e1 1812 DEBUG_puts("5cups_get_printer_uri: Printer URI not found.");
fa73b229 1813 return (0);
1814}
1815
1816
ef416fc2 1817/*
f2d18633 1818 * End of "$Id$".
ef416fc2 1819 */