]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/util.c
Full sweep of all Clang warnings, plus some bug fixes for incorrect memcpy usage.
[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
ef416fc2 1026 /*
fa73b229 1027 * Remap local hostname to localhost...
1028 */
1029
757d2cad 1030 httpGetHostname(NULL, localhost, sizeof(localhost));
fa73b229 1031
e07d4801 1032 DEBUG_printf(("2cupsGetPPD3: Local hostname=\"%s\"", localhost));
8ca02f3c 1033
88f9aafc 1034 if (!_cups_strcasecmp(localhost, hostname))
5a9febac 1035 strlcpy(hostname, "localhost", sizeof(hostname));
fa73b229 1036
1037 /*
ed486911 1038 * Get the hostname and port number we are connected to...
ef416fc2 1039 */
1040
ed486911 1041 httpGetHostname(http, http_hostname, sizeof(http_hostname));
a469f8a5 1042 http_port = httpAddrPort(http->hostaddr);
ed486911 1043
e07d4801 1044 DEBUG_printf(("2cupsGetPPD3: Connection hostname=\"%s\", port=%d",
1ff0402e 1045 http_hostname, http_port));
8ca02f3c 1046
ef416fc2 1047 /*
1048 * Reconnect to the correct server as needed...
1049 */
1050
88f9aafc 1051 if (!_cups_strcasecmp(http_hostname, hostname) && port == http_port)
ef416fc2 1052 http2 = http;
cb7f98ee
MS
1053 else if ((http2 = httpConnect2(hostname, port, NULL, AF_UNSPEC,
1054 cupsEncryption(), 1, 30000, NULL)) == NULL)
ef416fc2 1055 {
4d301e69 1056 DEBUG_puts("1cupsGetPPD3: Unable to connect to server");
ef416fc2 1057
cb7f98ee 1058 return (HTTP_STATUS_SERVICE_UNAVAILABLE);
ef416fc2 1059 }
1060
1061 /*
1062 * Get a temp file...
1063 */
1064
2e4ff8af
MS
1065 if (buffer[0])
1066 fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
1067 else
12f89d24 1068 fd = cupsTempFd(tempfile, sizeof(tempfile));
2e4ff8af
MS
1069
1070 if (fd < 0)
ef416fc2 1071 {
1072 /*
1073 * Can't open file; close the server connection and return NULL...
1074 */
1075
cb7f98ee 1076 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
ef416fc2 1077
1078 if (http2 != http)
1079 httpClose(http2);
1080
cb7f98ee 1081 return (HTTP_STATUS_SERVER_ERROR);
ef416fc2 1082 }
1083
1084 /*
1085 * And send a request to the HTTP server...
1086 */
1087
fa73b229 1088 strlcat(resource, ".ppd", sizeof(resource));
ef416fc2 1089
2e4ff8af
MS
1090 if (*modtime > 0)
1091 httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
1092 httpGetDateString(*modtime));
1093
ef416fc2 1094 status = cupsGetFd(http2, resource, fd);
1095
1096 close(fd);
1097
ef416fc2 1098 /*
1099 * See if we actually got the file or an error...
1100 */
1101
cb7f98ee 1102 if (status == HTTP_STATUS_OK)
12f89d24 1103 {
2e4ff8af 1104 *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
12f89d24
MS
1105
1106 if (tempfile[0])
1107 strlcpy(buffer, tempfile, bufsize);
1108 }
cb7f98ee 1109 else if (status != HTTP_STATUS_NOT_MODIFIED)
ef416fc2 1110 {
e07d4801 1111 _cupsSetHTTPError(status);
ef416fc2 1112
12f89d24
MS
1113 if (buffer[0])
1114 unlink(buffer);
1115 else if (tempfile[0])
1116 unlink(tempfile);
ef416fc2 1117 }
12f89d24
MS
1118 else if (tempfile[0])
1119 unlink(tempfile);
ef416fc2 1120
91c84a35
MS
1121 if (http2 != http)
1122 httpClose(http2);
1123
ef416fc2 1124 /*
1125 * Return the PPD file...
1126 */
1127
e07d4801
MS
1128 DEBUG_printf(("1cupsGetPPD3: Returning status %d", status));
1129
2e4ff8af 1130 return (status);
ef416fc2 1131}
1132
1133
1134/*
1135 * 'cupsGetPrinters()' - Get a list of printers from the default server.
1136 *
5a738aea 1137 * This function is deprecated - use @link cupsGetDests@ instead.
ef416fc2 1138 *
1139 * @deprecated@
1140 */
1141
1142int /* O - Number of printers */
1143cupsGetPrinters(char ***printers) /* O - Printers */
1144{
1145 int n; /* Number of printers */
1146 ipp_t *request, /* IPP Request */
1147 *response; /* IPP Response */
1148 ipp_attribute_t *attr; /* Current attribute */
ef416fc2 1149 char **temp; /* Temporary pointer */
3d052e43
MS
1150 http_t *http; /* Connection to server */
1151
ef416fc2 1152
3d052e43
MS
1153 /*
1154 * Range check input...
1155 */
ef416fc2 1156
3d052e43 1157 if (!printers)
ef416fc2 1158 {
cb7f98ee 1159 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 1160
1161 return (0);
1162 }
1163
3d052e43
MS
1164 *printers = NULL;
1165
ef416fc2 1166 /*
1167 * Try to connect to the server...
1168 */
1169
3d052e43 1170 if ((http = _cupsConnect()) == NULL)
ef416fc2 1171 return (0);
ef416fc2 1172
1173 /*
1174 * Build a CUPS_GET_PRINTERS request, which requires the following
1175 * attributes:
1176 *
1177 * attributes-charset
1178 * attributes-natural-language
1179 * requested-attributes
1180 */
1181
cb7f98ee 1182 request = ippNewRequest(IPP_OP_CUPS_GET_PRINTERS);
ef416fc2 1183
1184 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1185 "requested-attributes", NULL, "printer-name");
1186
1187 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1188 "printer-type", 0);
1189
1190 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_ENUM,
1191 "printer-type-mask", CUPS_PRINTER_CLASS);
1192
1193 /*
1194 * Do the request and get back a response...
1195 */
1196
3d052e43 1197 n = 0;
ef416fc2 1198
3d052e43 1199 if ((response = cupsDoRequest(http, request, "/")) != NULL)
ef416fc2 1200 {
1201 for (attr = response->attrs; attr != NULL; attr = attr->next)
1202 if (attr->name != NULL &&
88f9aafc 1203 _cups_strcasecmp(attr->name, "printer-name") == 0 &&
ef416fc2 1204 attr->value_tag == IPP_TAG_NAME)
1205 {
1206 if (n == 0)
1207 temp = malloc(sizeof(char *));
1208 else
7e86f2f6 1209 temp = realloc(*printers, sizeof(char *) * (size_t)(n + 1));
ef416fc2 1210
1211 if (temp == NULL)
1212 {
1213 /*
1214 * Ran out of memory!
1215 */
1216
1217 while (n > 0)
1218 {
1219 n --;
1220 free((*printers)[n]);
1221 }
1222
1223 free(*printers);
1224 ippDelete(response);
1225 return (0);
1226 }
1227
1228 *printers = temp;
1229 temp[n] = strdup(attr->values[0].string.text);
1230 n ++;
1231 }
1232
1233 ippDelete(response);
1234 }
1235
1236 return (n);
1237}
1238
1239
b94498cf 1240/*
1241 * 'cupsGetServerPPD()' - Get an available PPD file from the server.
1242 *
1243 * This function returns the named PPD file from the server. The
5a738aea 1244 * list of available PPDs is provided by the IPP @code CUPS_GET_PPDS@
b94498cf 1245 * operation.
1246 *
1247 * You must remove (unlink) the PPD file when you are finished with
1248 * it. The PPD filename is stored in a static location that will be
5a738aea
MS
1249 * overwritten on the next call to @link cupsGetPPD@, @link cupsGetPPD2@,
1250 * or @link cupsGetServerPPD@.
b94498cf 1251 *
f3c17241 1252 * @since CUPS 1.3/OS X 10.5@
b94498cf 1253 */
1254
5a738aea 1255char * /* O - Name of PPD file or @code NULL@ on error */
568fa3fa 1256cupsGetServerPPD(http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
b94498cf 1257 const char *name) /* I - Name of PPD file ("ppd-name") */
1258{
1259 int fd; /* PPD file descriptor */
1260 ipp_t *request; /* IPP request */
1261 _cups_globals_t *cg = _cupsGlobals();
1262 /* Pointer to library globals */
1263
1264
1265 /*
1266 * Range check input...
1267 */
1268
3d052e43 1269 if (!name)
b94498cf 1270 {
cb7f98ee 1271 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("No PPD name"), 1);
b94498cf 1272
1273 return (NULL);
1274 }
1275
3d052e43
MS
1276 if (!http)
1277 if ((http = _cupsConnect()) == NULL)
1278 return (NULL);
1279
b94498cf 1280 /*
1281 * Get a temp file...
1282 */
1283
1284 if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
1285 {
1286 /*
1287 * Can't open file; close the server connection and return NULL...
1288 */
1289
cb7f98ee 1290 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, NULL, 0);
b94498cf 1291
1292 return (NULL);
1293 }
1294
1295 /*
1296 * Get the PPD file...
1297 */
1298
cb7f98ee 1299 request = ippNewRequest(IPP_OP_CUPS_GET_PPD);
b94498cf 1300 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
1301 name);
1302
1303 ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
1304
1305 close(fd);
1306
cb7f98ee 1307 if (cupsLastError() != IPP_STATUS_OK)
b94498cf 1308 {
1309 unlink(cg->ppd_filename);
1310 return (NULL);
1311 }
1312 else
1313 return (cg->ppd_filename);
1314}
1315
1316
ef416fc2 1317/*
1318 * 'cupsPrintFile()' - Print a file to a printer or class on the default server.
1319 */
1320
5a738aea 1321int /* O - Job ID or 0 on error */
568fa3fa 1322cupsPrintFile(const char *name, /* I - Destination name */
ef416fc2 1323 const char *filename, /* I - File to print */
1324 const char *title, /* I - Title of job */
1325 int num_options,/* I - Number of options */
1326 cups_option_t *options) /* I - Options */
1327{
1328 DEBUG_printf(("cupsPrintFile(name=\"%s\", filename=\"%s\", "
e07d4801 1329 "title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1330 name, filename, title, num_options, options));
1331
3d052e43
MS
1332 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
1333 num_options, options));
ef416fc2 1334}
1335
1336
1337/*
5a738aea
MS
1338 * 'cupsPrintFile2()' - Print a file to a printer or class on the specified
1339 * server.
ef416fc2 1340 *
f3c17241 1341 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 1342 */
1343
5a738aea 1344int /* O - Job ID or 0 on error */
3d052e43 1345cupsPrintFile2(
568fa3fa
MS
1346 http_t *http, /* I - Connection to server */
1347 const char *name, /* I - Destination name */
3d052e43
MS
1348 const char *filename, /* I - File to print */
1349 const char *title, /* I - Title of job */
1350 int num_options, /* I - Number of options */
1351 cups_option_t *options) /* I - Options */
ef416fc2 1352{
1353 DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
e07d4801 1354 "title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1355 http, name, filename, title, num_options, options));
1356
3d052e43
MS
1357 return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
1358 options));
ef416fc2 1359}
1360
1361
1362/*
fa73b229 1363 * 'cupsPrintFiles()' - Print one or more files to a printer or class on the
1364 * default server.
ef416fc2 1365 */
1366
5a738aea 1367int /* O - Job ID or 0 on error */
3d052e43 1368cupsPrintFiles(
568fa3fa 1369 const char *name, /* I - Destination name */
3d052e43
MS
1370 int num_files, /* I - Number of files */
1371 const char **files, /* I - File(s) to print */
1372 const char *title, /* I - Title of job */
1373 int num_options, /* I - Number of options */
1374 cups_option_t *options) /* I - Options */
ef416fc2 1375{
ef416fc2 1376 DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
e07d4801 1377 "files=%p, title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1378 name, num_files, files, title, num_options, options));
1379
ef416fc2 1380 /*
1381 * Print the file(s)...
1382 */
1383
3d052e43 1384 return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
ef416fc2 1385 num_options, options));
1386}
1387
1388
ef416fc2 1389/*
fa73b229 1390 * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
1391 * specified server.
ef416fc2 1392 *
f3c17241 1393 * @since CUPS 1.1.21/OS X 10.4@
ef416fc2 1394 */
1395
5a738aea 1396int /* O - Job ID or 0 on error */
3d052e43 1397cupsPrintFiles2(
568fa3fa
MS
1398 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1399 const char *name, /* I - Destination name */
3d052e43
MS
1400 int num_files, /* I - Number of files */
1401 const char **files, /* I - File(s) to print */
1402 const char *title, /* I - Title of job */
1403 int num_options, /* I - Number of options */
1404 cups_option_t *options) /* I - Options */
ef416fc2 1405{
1406 int i; /* Looping var */
3d052e43
MS
1407 int job_id; /* New job ID */
1408 const char *docname; /* Basename of current filename */
1409 const char *format; /* Document format */
1410 cups_file_t *fp; /* Current file */
1411 char buffer[8192]; /* Copy buffer */
1412 ssize_t bytes; /* Bytes in buffer */
1413 http_status_t status; /* Status of write */
5a662dc0
MS
1414 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1415 ipp_status_t cancel_status; /* Status code to preserve */
1416 char *cancel_message; /* Error message to preserve */
ef416fc2 1417
1418
3d052e43 1419 DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
e07d4801 1420 "files=%p, title=\"%s\", num_options=%d, options=%p)",
ef416fc2 1421 http, name, num_files, files, title, num_options, options));
1422
1423 /*
1424 * Range check input...
1425 */
1426
3d052e43 1427 if (!name || num_files < 1 || !files)
ef416fc2 1428 {
cb7f98ee 1429 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
ef416fc2 1430
1431 return (0);
1432 }
1433
1434 /*
3d052e43 1435 * Create the print job...
ef416fc2 1436 */
1437
3d052e43 1438 if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
ef416fc2 1439 return (0);
ef416fc2 1440
1441 /*
3d052e43 1442 * Send each of the files...
ef416fc2 1443 */
1444
3d052e43
MS
1445 if (cupsGetOption("raw", num_options, options))
1446 format = CUPS_FORMAT_RAW;
1447 else if ((format = cupsGetOption("document-format", num_options,
1448 options)) == NULL)
1449 format = CUPS_FORMAT_AUTO;
ef416fc2 1450
3d052e43
MS
1451 for (i = 0; i < num_files; i ++)
1452 {
1453 /*
1454 * Start the next file...
1455 */
ef416fc2 1456
3d052e43
MS
1457 if ((docname = strrchr(files[i], '/')) != NULL)
1458 docname ++;
1459 else
1460 docname = files[i];
ef416fc2 1461
3d052e43
MS
1462 if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
1463 {
1464 /*
1465 * Unable to open print file, cancel the job and return...
1466 */
ef416fc2 1467
cb7f98ee 1468 _cupsSetError(IPP_STATUS_ERROR_DOCUMENT_ACCESS, NULL, 0);
5a662dc0 1469 goto cancel_job;
3d052e43 1470 }
ef416fc2 1471
ba55dc12
MS
1472 status = cupsStartDocument(http, name, job_id, docname, format,
1473 i == (num_files - 1));
ef416fc2 1474
cb7f98ee 1475 while (status == HTTP_STATUS_CONTINUE &&
ba55dc12 1476 (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
7e86f2f6 1477 status = cupsWriteRequestData(http, buffer, (size_t)bytes);
ef416fc2 1478
3d052e43 1479 cupsFileClose(fp);
ef416fc2 1480
cb7f98ee 1481 if (status != HTTP_STATUS_CONTINUE || cupsFinishDocument(http, name) != IPP_STATUS_OK)
ef416fc2 1482 {
1483 /*
3d052e43 1484 * Unable to queue, cancel the job and return...
ef416fc2 1485 */
1486
5a662dc0 1487 goto cancel_job;
3d052e43
MS
1488 }
1489 }
ef416fc2 1490
3d052e43 1491 return (job_id);
5a662dc0
MS
1492
1493 /*
1494 * If we get here, something happened while sending the print job so we need
1495 * to cancel the job without setting the last error (since we need to preserve
1496 * the current error...
1497 */
1498
1499 cancel_job:
1500
1501 cancel_status = cg->last_error;
1502 cancel_message = cg->last_status_message ?
1503 _cupsStrRetain(cg->last_status_message) : NULL;
1504
1505 cupsCancelJob2(http, name, job_id, 0);
1506
1507 cg->last_error = cancel_status;
1508 cg->last_status_message = cancel_message;
1509
1510 return (0);
3d052e43 1511}
ef416fc2 1512
ef416fc2 1513
3d052e43
MS
1514/*
1515 * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
1516 *
5a738aea
MS
1517 * Use @link cupsWriteRequestData@ to write data for the document and
1518 * @link cupsFinishDocument@ to finish the document and get the submission status.
3d052e43 1519 *
5a738aea
MS
1520 * The MIME type constants @code CUPS_FORMAT_AUTO@, @code CUPS_FORMAT_PDF@,
1521 * @code CUPS_FORMAT_POSTSCRIPT@, @code CUPS_FORMAT_RAW@, and
1522 * @code CUPS_FORMAT_TEXT@ are provided for the "format" argument, although
1523 * any supported MIME type string can be supplied.
3d052e43 1524 *
f3c17241 1525 * @since CUPS 1.4/OS X 10.6@
3d052e43 1526 */
ef416fc2 1527
3d052e43
MS
1528http_status_t /* O - HTTP status of request */
1529cupsStartDocument(
568fa3fa
MS
1530 http_t *http, /* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
1531 const char *name, /* I - Destination name */
5a738aea 1532 int job_id, /* I - Job ID from @link cupsCreateJob@ */
3d052e43 1533 const char *docname, /* I - Name of document */
5a738aea 1534 const char *format, /* I - MIME type or @code CUPS_FORMAT_foo@ */
3d052e43
MS
1535 int last_document) /* I - 1 for last document in job, 0 otherwise */
1536{
1537 char resource[1024], /* Resource for destinatio */
1538 printer_uri[1024]; /* Printer URI */
1539 ipp_t *request; /* Send-Document request */
1540 http_status_t status; /* HTTP status */
ef416fc2 1541
bd7854cb 1542
3d052e43
MS
1543 /*
1544 * Create a Send-Document request...
1545 */
bd7854cb 1546
cb7f98ee 1547 if ((request = ippNewRequest(IPP_OP_SEND_DOCUMENT)) == NULL)
3d052e43 1548 {
cb7f98ee
MS
1549 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(ENOMEM), 0);
1550 return (HTTP_STATUS_ERROR);
3d052e43 1551 }
bd7854cb 1552
3d052e43
MS
1553 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
1554 NULL, "localhost", ippPort(), "/printers/%s", name);
1555 snprintf(resource, sizeof(resource), "/printers/%s", name);
ef416fc2 1556
3d052e43
MS
1557 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1558 NULL, printer_uri);
1559 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
1560 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1561 NULL, cupsUser());
1562 if (docname)
1563 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
1564 NULL, docname);
1565 if (format)
1566 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1567 "document-format", NULL, format);
7e86f2f6 1568 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", (char)last_document);
ef416fc2 1569
3d052e43
MS
1570 /*
1571 * Send and delete the request, then return the status...
1572 */
ef416fc2 1573
3d052e43 1574 status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
ef416fc2 1575
3d052e43 1576 ippDelete(request);
ef416fc2 1577
3d052e43 1578 return (status);
ef416fc2 1579}
1580
1581
fa73b229 1582/*
58dc1933
MS
1583 * 'cups_get_printer_uri()' - Get the printer-uri-supported attribute for the
1584 * first printer in a class.
fa73b229 1585 */
1586
1587static int /* O - 1 on success, 0 on failure */
1588cups_get_printer_uri(
568fa3fa 1589 http_t *http, /* I - Connection to server */
fa73b229 1590 const char *name, /* I - Name of printer or class */
1591 char *host, /* I - Hostname buffer */
1592 int hostsize, /* I - Size of hostname buffer */
1593 int *port, /* O - Port number */
1594 char *resource, /* I - Resource buffer */
1595 int resourcesize, /* I - Size of resource buffer */
1596 int depth) /* I - Depth of query */
1597{
1598 int i; /* Looping var */
1599 int http_port; /* Port number */
1600 http_t *http2; /* Alternate HTTP connection */
1601 ipp_t *request, /* IPP request */
1602 *response; /* IPP response */
1603 ipp_attribute_t *attr; /* Current attribute */
1604 char uri[HTTP_MAX_URI], /* printer-uri attribute */
1605 scheme[HTTP_MAX_URI], /* Scheme name */
1606 username[HTTP_MAX_URI], /* Username:password */
ed486911 1607 classname[255], /* Temporary class name */
1608 http_hostname[HTTP_MAX_HOST];
1609 /* Hostname associated with connection */
fa73b229 1610 static const char * const requested_attrs[] =
1611 { /* Requested attributes */
37e7e6e0 1612 "device-uri",
58dc1933 1613 "member-uris",
fa73b229 1614 "printer-uri-supported",
58dc1933 1615 "printer-type"
fa73b229 1616 };
1617
1618
e07d4801
MS
1619 DEBUG_printf(("7cups_get_printer_uri(http=%p, name=\"%s\", host=%p, "
1620 "hostsize=%d, resource=%p, resourcesize=%d, depth=%d)",
1621 http, name, host, hostsize, resource, resourcesize, depth));
fa73b229 1622
1623 /*
1624 * Setup the printer URI...
1625 */
1626
a4d04587 1627 if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
cb7f98ee
MS
1628 "localhost", 0, "/printers/%s",
1629 name) < HTTP_URI_STATUS_OK)
fa73b229 1630 {
cb7f98ee
MS
1631 _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create printer-uri"),
1632 1);
fa73b229 1633
1634 *host = '\0';
1635 *resource = '\0';
1636
1637 return (0);
1638 }
1639
e07d4801 1640 DEBUG_printf(("9cups_get_printer_uri: printer-uri=\"%s\"", uri));
fa73b229 1641
1642 /*
ed486911 1643 * Get the hostname and port number we are connected to...
fa73b229 1644 */
1645
ed486911 1646 httpGetHostname(http, http_hostname, sizeof(http_hostname));
a469f8a5 1647 http_port = httpAddrPort(http->hostaddr);
fa73b229 1648
1649 /*
1650 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1651 * attributes:
1652 *
1653 * attributes-charset
1654 * attributes-natural-language
1655 * printer-uri
1656 * requested-attributes
1657 */
1658
cb7f98ee 1659 request = ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES);
fa73b229 1660
1661 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1662 NULL, uri);
1663
1664 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1665 "requested-attributes",
1666 sizeof(requested_attrs) / sizeof(requested_attrs[0]),
1667 NULL, requested_attrs);
1668
1669 /*
1670 * Do the request and get back a response...
1671 */
1672
a469f8a5
MS
1673 snprintf(resource, resourcesize, "/printers/%s", name);
1674
1675 if ((response = cupsDoRequest(http, request, resource)) != NULL)
fa73b229 1676 {
37e7e6e0
MS
1677 const char *device_uri = NULL; /* device-uri value */
1678
1679 if ((attr = ippFindAttribute(response, "device-uri",
1680 IPP_TAG_URI)) != NULL)
1681 device_uri = attr->values[0].string.text;
1682
1683 if (device_uri &&
c5b24bfa
MS
1684 (!strncmp(device_uri, "ipp://", 6) ||
1685 !strncmp(device_uri, "ipps://", 7) ||
1686 ((strstr(device_uri, "._ipp.") != NULL ||
1687 strstr(device_uri, "._ipps.") != NULL) &&
1688 !strcmp(device_uri + strlen(device_uri) - 5, "/cups"))))
37e7e6e0
MS
1689 {
1690 /*
c5b24bfa 1691 * Statically-configured shared printer.
37e7e6e0
MS
1692 */
1693
1694 httpSeparateURI(HTTP_URI_CODING_ALL,
1695 _httpResolveURI(device_uri, uri, sizeof(uri),
1696 _HTTP_RESOLVE_DEFAULT, NULL, NULL),
1697 scheme, sizeof(scheme), username, sizeof(username),
1698 host, hostsize, port, resource, resourcesize);
1699 ippDelete(response);
1700
1701 return (1);
1702 }
1703 else if ((attr = ippFindAttribute(response, "member-uris",
1704 IPP_TAG_URI)) != NULL)
fa73b229 1705 {
1706 /*
1707 * Get the first actual printer name in the class...
1708 */
1709
1710 for (i = 0; i < attr->num_values; i ++)
1711 {
a4d04587 1712 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
1713 scheme, sizeof(scheme), username, sizeof(username),
1714 host, hostsize, port, resource, resourcesize);
fa73b229 1715 if (!strncmp(resource, "/printers/", 10))
1716 {
1717 /*
1718 * Found a printer!
1719 */
1720
1721 ippDelete(response);
1722
1723 return (1);
1724 }
1725 }
1726
1727 /*
1728 * No printers in this class - try recursively looking for a printer,
1729 * but not more than 3 levels deep...
1730 */
1731
1732 if (depth < 3)
1733 {
1734 for (i = 0; i < attr->num_values; i ++)
1735 {
a4d04587 1736 httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text,
1737 scheme, sizeof(scheme), username, sizeof(username),
1738 host, hostsize, port, resource, resourcesize);
fa73b229 1739 if (!strncmp(resource, "/classes/", 9))
1740 {
1741 /*
1742 * Found a class! Connect to the right server...
1743 */
1744
88f9aafc 1745 if (!_cups_strcasecmp(http_hostname, host) && *port == http_port)
fa73b229 1746 http2 = http;
cb7f98ee
MS
1747 else if ((http2 = httpConnect2(host, *port, NULL, AF_UNSPEC,
1748 cupsEncryption(), 1, 30000,
1749 NULL)) == NULL)
fa73b229 1750 {
4d301e69 1751 DEBUG_puts("8cups_get_printer_uri: Unable to connect to server");
fa73b229 1752
1753 continue;
1754 }
1755
1756 /*
1757 * Look up printers on that server...
1758 */
1759
1760 strlcpy(classname, resource + 9, sizeof(classname));
1761
1762 cups_get_printer_uri(http2, classname, host, hostsize, port,
1763 resource, resourcesize, depth + 1);
1764
1765 /*
1766 * Close the connection as needed...
1767 */
1768
1769 if (http2 != http)
1770 httpClose(http2);
1771
1772 if (*host)
1773 return (1);
1774 }
1775 }
1776 }
1777 }
1778 else if ((attr = ippFindAttribute(response, "printer-uri-supported",
1779 IPP_TAG_URI)) != NULL)
1780 {
58dc1933
MS
1781 httpSeparateURI(HTTP_URI_CODING_ALL,
1782 _httpResolveURI(attr->values[0].string.text, uri,
eac3a0a0
MS
1783 sizeof(uri), _HTTP_RESOLVE_DEFAULT,
1784 NULL, NULL),
a4d04587 1785 scheme, sizeof(scheme), username, sizeof(username),
1786 host, hostsize, port, resource, resourcesize);
fa73b229 1787 ippDelete(response);
1788
dd1abb6b
MS
1789 if (!strncmp(resource, "/classes/", 9))
1790 {
cb7f98ee 1791 _cupsSetError(IPP_STATUS_ERROR_INTERNAL,
4d301e69 1792 _("No printer-uri found for class"), 1);
dd1abb6b
MS
1793
1794 *host = '\0';
1795 *resource = '\0';
1796
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
1812 return (0);
1813}
1814
1815
ef416fc2 1816/*
f2d18633 1817 * End of "$Id$".
ef416fc2 1818 */