]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/ipp.c
88cc66ac0fdf0fdc6badb7cad4c22e5ef7a31a54
[thirdparty/cups.git] / scheduler / ipp.c
1 /*
2 * "$Id: ipp.c,v 1.146 2001/10/22 21:04:08 mike Exp $"
3 *
4 * IPP routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-2001 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636-3111 USA
19 *
20 * Voice: (301) 373-9603
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * ProcessIPPRequest() - Process an incoming IPP request...
27 * accept_jobs() - Accept print jobs to a printer.
28 * add_class() - Add a class to the system.
29 * add_file() - Add a file to a job.
30 * add_job_state_reasons() - Add the "job-state-reasons" attribute based
31 * upon the job and printer state...
32 * add_printer() - Add a printer to the system.
33 * add_printer_state_reasons() - Add the "printer-state-reasons" attribute
34 * based upon the printer state...
35 * add_queued_job_count() - Add the "queued-job-count" attribute for
36 * cancel_all_jobs() - Cancel all print jobs.
37 * cancel_job() - Cancel a print job.
38 * check_quotas() - Check quotas for a printer and user.
39 * copy_attribute() - Copy a single attribute.
40 * copy_attrs() - Copy attributes from one request to another.
41 * create_job() - Print a file to a printer or class.
42 * copy_banner() - Copy a banner file to the requests directory
43 * for the specified job.
44 * copy_file() - Copy a PPD file or interface script...
45 * delete_printer() - Remove a printer or class from the system.
46 * get_default() - Get the default destination.
47 * get_devices() - Get the list of available devices on the
48 * local system.
49 * get_jobs() - Get a list of jobs for the specified printer.
50 * get_job_attrs() - Get job attributes.
51 * get_ppds() - Get the list of PPD files on the local
52 * system.
53 * get_printer_attrs() - Get printer attributes.
54 * get_printers() - Get a list of printers.
55 * hold_job() - Hold a print job.
56 * move_job() - Move a job.
57 * print_job() - Print a file to a printer or class.
58 * reject_jobs() - Reject print jobs to a printer.
59 * release_job() - Release a held print job.
60 * restart_job() - Restart an old print job.
61 * send_document() - Send a file to a printer or class.
62 * send_ipp_error() - Send an error status back to the IPP client.
63 * set_default() - Set the default destination...
64 * set_job_attrs() - Set job attributes.
65 * start_printer() - Start a printer.
66 * stop_printer() - Stop a printer.
67 * validate_job() - Validate printer options and destination.
68 * validate_user() - Validate the user for the request.
69 */
70
71 /*
72 * Include necessary headers...
73 */
74
75 #include "cupsd.h"
76 #include <pwd.h>
77 #include <grp.h>
78 #ifdef HAVE_LIBZ
79 # include <zlib.h>
80 #endif /* HAVE_LIBZ */
81
82
83 /*
84 * Local functions...
85 */
86
87 static void accept_jobs(client_t *con, ipp_attribute_t *uri);
88 static void add_class(client_t *con, ipp_attribute_t *uri);
89 static int add_file(client_t *con, job_t *job, mime_type_t *filetype);
90 static void add_job_state_reasons(client_t *con, job_t *job);
91 static void add_printer(client_t *con, ipp_attribute_t *uri);
92 static void add_printer_state_reasons(client_t *con, printer_t *p);
93 static void add_queued_job_count(client_t *con, printer_t *p);
94 static void cancel_all_jobs(client_t *con, ipp_attribute_t *uri);
95 static void cancel_job(client_t *con, ipp_attribute_t *uri);
96 static int check_quotas(client_t *con, printer_t *p);
97 static void copy_attribute(ipp_t *to, ipp_attribute_t *attr,
98 int quickcopy);
99 static void copy_attrs(ipp_t *to, ipp_t *from, ipp_attribute_t *req,
100 ipp_tag_t group);
101 static int copy_banner(client_t *con, job_t *job, const char *name);
102 static int copy_file(const char *from, const char *to);
103 static void create_job(client_t *con, ipp_attribute_t *uri);
104 static void delete_printer(client_t *con, ipp_attribute_t *uri);
105 static void get_default(client_t *con);
106 static void get_devices(client_t *con);
107 static void get_jobs(client_t *con, ipp_attribute_t *uri);
108 static void get_job_attrs(client_t *con, ipp_attribute_t *uri);
109 static void get_ppds(client_t *con);
110 static void get_printers(client_t *con, int type);
111 static void get_printer_attrs(client_t *con, ipp_attribute_t *uri);
112 static void hold_job(client_t *con, ipp_attribute_t *uri);
113 static void move_job(client_t *con, ipp_attribute_t *uri);
114 static void print_job(client_t *con, ipp_attribute_t *uri);
115 static void reject_jobs(client_t *con, ipp_attribute_t *uri);
116 static void release_job(client_t *con, ipp_attribute_t *uri);
117 static void restart_job(client_t *con, ipp_attribute_t *uri);
118 static void send_document(client_t *con, ipp_attribute_t *uri);
119 static void send_ipp_error(client_t *con, ipp_status_t status);
120 static void set_default(client_t *con, ipp_attribute_t *uri);
121 static void set_job_attrs(client_t *con, ipp_attribute_t *uri);
122 static void start_printer(client_t *con, ipp_attribute_t *uri);
123 static void stop_printer(client_t *con, ipp_attribute_t *uri);
124 static void validate_job(client_t *con, ipp_attribute_t *uri);
125 static int validate_user(client_t *con, const char *owner, char *username,
126 int userlen);
127
128
129 /*
130 * 'ProcessIPPRequest()' - Process an incoming IPP request...
131 */
132
133 void
134 ProcessIPPRequest(client_t *con) /* I - Client connection */
135 {
136 ipp_tag_t group; /* Current group tag */
137 ipp_attribute_t *attr; /* Current attribute */
138 ipp_attribute_t *charset; /* Character set attribute */
139 ipp_attribute_t *language; /* Language attribute */
140 ipp_attribute_t *uri; /* Printer URI attribute */
141 ipp_attribute_t *username; /* requesting-user-name attr */
142
143
144 LogMessage(L_DEBUG2, "ProcessIPPRequest(%p[%d]): operation_id = %04x",
145 con, con->http.fd, con->request->request.op.operation_id);
146
147 /*
148 * First build an empty response message for this request...
149 */
150
151 con->response = ippNew();
152
153 con->response->request.status.version[0] = con->request->request.op.version[0];
154 con->response->request.status.version[1] = con->request->request.op.version[1];
155 con->response->request.status.request_id = con->request->request.op.request_id;
156
157 /*
158 * Then validate the request header and required attributes...
159 */
160
161 if (con->request->request.any.version[0] != 1)
162 {
163 /*
164 * Return an error, since we only support IPP 1.x.
165 */
166
167 LogMessage(L_ERROR, "ProcessIPPRequest: bad request version (%d.%d)!",
168 con->request->request.any.version[0],
169 con->request->request.any.version[1]);
170
171 send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
172 }
173 else if (con->request->attrs == NULL)
174 {
175 LogMessage(L_ERROR, "ProcessIPPRequest: no attributes in request!");
176 send_ipp_error(con, IPP_BAD_REQUEST);
177 }
178 else
179 {
180 /*
181 * Make sure that the attributes are provided in the correct order and
182 * don't repeat groups...
183 */
184
185 for (attr = con->request->attrs, group = attr->group_tag;
186 attr != NULL;
187 attr = attr->next)
188 if (attr->group_tag < group)
189 {
190 /*
191 * Out of order; return an error...
192 */
193
194 LogMessage(L_ERROR, "ProcessIPPRequest: attribute groups are out of order!");
195 send_ipp_error(con, IPP_BAD_REQUEST);
196 break;
197 }
198 else
199 group = attr->group_tag;
200
201 if (attr == NULL)
202 {
203 /*
204 * Then make sure that the first three attributes are:
205 *
206 * attributes-charset
207 * attributes-natural-language
208 * printer-uri/job-uri
209 */
210
211 attr = con->request->attrs;
212 if (attr != NULL && strcmp(attr->name, "attributes-charset") == 0 &&
213 attr->value_tag == IPP_TAG_CHARSET)
214 charset = attr;
215 else
216 charset = NULL;
217
218 if (attr)
219 attr = attr->next;
220 if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0 &&
221 attr->value_tag == IPP_TAG_LANGUAGE)
222 language = attr;
223 else
224 language = NULL;
225
226 if ((attr = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) != NULL)
227 uri = attr;
228 else if ((attr = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) != NULL)
229 uri = attr;
230 else
231 uri = NULL;
232
233 if (charset)
234 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
235 "attributes-charset", NULL, charset->values[0].string.text);
236 else
237 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
238 "attributes-charset", NULL, DefaultCharset);
239
240 if (language)
241 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
242 "attributes-natural-language", NULL,
243 language->values[0].string.text);
244 else
245 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
246 "attributes-natural-language", NULL, DefaultLanguage);
247
248 if (charset == NULL || language == NULL ||
249 (uri == NULL &&
250 con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
251 con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
252 con->request->request.op.operation_id != CUPS_GET_CLASSES &&
253 con->request->request.op.operation_id != CUPS_GET_DEVICES &&
254 con->request->request.op.operation_id != CUPS_GET_PPDS))
255 {
256 /*
257 * Return an error, since attributes-charset,
258 * attributes-natural-language, and printer-uri/job-uri are required
259 * for all operations.
260 */
261
262 if (charset == NULL)
263 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-charset attribute!");
264
265 if (language == NULL)
266 LogMessage(L_ERROR, "ProcessIPPRequest: missing attributes-natural-language attribute!");
267
268 if (uri == NULL)
269 LogMessage(L_ERROR, "ProcessIPPRequest: missing printer-uri or job-uri attribute!");
270
271 LogMessage(L_DEBUG, "Request attributes follow...");
272
273 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
274 LogMessage(L_DEBUG, "attr \"%s\": group_tag = %x, value_tag = %x",
275 attr->name ? attr->name : "(null)", attr->group_tag,
276 attr->value_tag);
277
278 LogMessage(L_DEBUG, "End of attributes...");
279
280 send_ipp_error(con, IPP_BAD_REQUEST);
281 }
282 else
283 {
284 /*
285 * OK, all the checks pass so far; make sure requesting-user-name is
286 * not "root" from a remote host...
287 */
288
289 if ((username = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
290 {
291 /*
292 * Check for root user...
293 */
294
295 if (strcmp(username->values[0].string.text, "root") == 0 &&
296 ntohl(con->http.hostaddr.sin_addr.s_addr) != 0x7f000001 &&
297 strcmp(con->username, "root") != 0)
298 {
299 /*
300 * Remote unauthenticated user masquerading as local root...
301 */
302
303 free(username->values[0].string.text);
304 username->values[0].string.text = strdup(RemoteRoot);
305 }
306 }
307
308 /*
309 * Then try processing the operation...
310 */
311
312 switch (con->request->request.op.operation_id)
313 {
314 case IPP_PRINT_JOB :
315 print_job(con, uri);
316 break;
317
318 case IPP_VALIDATE_JOB :
319 validate_job(con, uri);
320 break;
321
322 case IPP_CREATE_JOB :
323 create_job(con, uri);
324 break;
325
326 case IPP_SEND_DOCUMENT :
327 send_document(con, uri);
328 break;
329
330 case IPP_CANCEL_JOB :
331 cancel_job(con, uri);
332 break;
333
334 case IPP_GET_JOB_ATTRIBUTES :
335 get_job_attrs(con, uri);
336 break;
337
338 case IPP_GET_JOBS :
339 get_jobs(con, uri);
340 break;
341
342 case IPP_GET_PRINTER_ATTRIBUTES :
343 get_printer_attrs(con, uri);
344 break;
345
346 case IPP_HOLD_JOB :
347 hold_job(con, uri);
348 break;
349
350 case IPP_RELEASE_JOB :
351 release_job(con, uri);
352 break;
353
354 case IPP_RESTART_JOB :
355 restart_job(con, uri);
356 break;
357
358 case IPP_PAUSE_PRINTER :
359 stop_printer(con, uri);
360 break;
361
362 case IPP_RESUME_PRINTER :
363 start_printer(con, uri);
364 break;
365
366 case IPP_PURGE_JOBS :
367 cancel_all_jobs(con, uri);
368 break;
369
370 case IPP_SET_JOB_ATTRIBUTES :
371 set_job_attrs(con, uri);
372 break;
373
374 case CUPS_GET_DEFAULT :
375 get_default(con);
376 break;
377
378 case CUPS_GET_PRINTERS :
379 get_printers(con, 0);
380 break;
381
382 case CUPS_GET_CLASSES :
383 get_printers(con, CUPS_PRINTER_CLASS);
384 break;
385
386 case CUPS_ADD_PRINTER :
387 add_printer(con, uri);
388 break;
389
390 case CUPS_DELETE_PRINTER :
391 delete_printer(con, uri);
392 break;
393
394 case CUPS_ADD_CLASS :
395 add_class(con, uri);
396 break;
397
398 case CUPS_DELETE_CLASS :
399 delete_printer(con, uri);
400 break;
401
402 case CUPS_ACCEPT_JOBS :
403 case IPP_ENABLE_PRINTER :
404 accept_jobs(con, uri);
405 break;
406
407 case CUPS_REJECT_JOBS :
408 case IPP_DISABLE_PRINTER :
409 reject_jobs(con, uri);
410 break;
411
412 case CUPS_SET_DEFAULT :
413 set_default(con, uri);
414 break;
415
416 case CUPS_GET_DEVICES :
417 get_devices(con);
418 break;
419
420 case CUPS_GET_PPDS :
421 get_ppds(con);
422 break;
423
424 case CUPS_MOVE_JOB :
425 move_job(con, uri);
426 break;
427
428 default :
429 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
430 }
431 }
432 }
433 }
434
435 SendHeader(con, HTTP_OK, "application/ipp");
436
437 con->http.data_encoding = HTTP_ENCODE_LENGTH;
438 con->http.data_remaining = ippLength(con->response);
439
440 httpPrintf(HTTP(con), "Content-Length: %d\r\n\r\n",
441 con->http.data_remaining);
442
443 FD_SET(con->http.fd, &OutputSet);
444 }
445
446
447 /*
448 * 'accept_jobs()' - Accept print jobs to a printer.
449 */
450
451 static void
452 accept_jobs(client_t *con, /* I - Client connection */
453 ipp_attribute_t *uri) /* I - Printer or class URI */
454 {
455 cups_ptype_t dtype; /* Destination type (printer or class) */
456 char method[HTTP_MAX_URI],
457 /* Method portion of URI */
458 username[HTTP_MAX_URI],
459 /* Username portion of URI */
460 host[HTTP_MAX_URI],
461 /* Host portion of URI */
462 resource[HTTP_MAX_URI];
463 /* Resource portion of URI */
464 int port; /* Port portion of URI */
465 const char *name; /* Printer name */
466 printer_t *printer; /* Printer data */
467
468
469 LogMessage(L_DEBUG2, "accept_jobs(%p[%d], %s)\n", con, con->http.fd,
470 uri->values[0].string.text);
471
472 /*
473 * Was this operation called from the correct URI?
474 */
475
476 if (strncmp(con->uri, "/admin/", 7) != 0)
477 {
478 LogMessage(L_ERROR, "accept_jobs: admin request on bad resource \'%s\'!",
479 con->uri);
480 send_ipp_error(con, IPP_NOT_AUTHORIZED);
481 return;
482 }
483
484 /*
485 * Is the destination valid?
486 */
487
488 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
489
490 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
491 {
492 /*
493 * Bad URI...
494 */
495
496 LogMessage(L_ERROR, "accept_jobs: resource name \'%s\' no good!", resource);
497 send_ipp_error(con, IPP_NOT_FOUND);
498 return;
499 }
500
501 /*
502 * Accept jobs sent to the printer...
503 */
504
505 if (dtype == CUPS_PRINTER_CLASS)
506 printer = FindClass(name);
507 else
508 printer = FindPrinter(name);
509
510 printer->accepting = 1;
511 printer->state_message[0] = '\0';
512
513 if (dtype == CUPS_PRINTER_CLASS)
514 SaveAllClasses();
515 else
516 SaveAllPrinters();
517
518 LogMessage(L_INFO, "Printer \'%s\' now accepting jobs (\'%s\').", name,
519 con->username);
520
521 /*
522 * Everything was ok, so return OK status...
523 */
524
525 con->response->request.status.status_code = IPP_OK;
526 }
527
528
529 /*
530 * 'add_class()' - Add a class to the system.
531 */
532
533 static void
534 add_class(client_t *con, /* I - Client connection */
535 ipp_attribute_t *uri) /* I - URI of class */
536 {
537 int i; /* Looping var */
538 char method[HTTP_MAX_URI],
539 /* Method portion of URI */
540 username[HTTP_MAX_URI],
541 /* Username portion of URI */
542 host[HTTP_MAX_URI],
543 /* Host portion of URI */
544 resource[HTTP_MAX_URI];
545 /* Resource portion of URI */
546 int port; /* Port portion of URI */
547 printer_t *pclass; /* Class */
548 cups_ptype_t dtype; /* Destination type */
549 const char *dest; /* Printer or class name */
550 ipp_attribute_t *attr; /* Printer attribute */
551 int modify; /* Non-zero if we just modified */
552
553
554 LogMessage(L_DEBUG2, "add_class(%p[%d], %s)\n", con, con->http.fd,
555 uri->values[0].string.text);
556
557 /*
558 * Was this operation called from the correct URI?
559 */
560
561 if (strncmp(con->uri, "/admin/", 7) != 0)
562 {
563 LogMessage(L_ERROR, "add_class: admin request on bad resource \'%s\'!",
564 con->uri);
565 send_ipp_error(con, IPP_NOT_AUTHORIZED);
566 return;
567 }
568
569 /*
570 * Do we have a valid URI?
571 */
572
573 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
574
575 if (strncmp(resource, "/classes/", 9) != 0 || strlen(resource) == 9)
576 {
577 /*
578 * No, return an error...
579 */
580
581 send_ipp_error(con, IPP_BAD_REQUEST);
582 return;
583 }
584
585 /*
586 * See if the class already exists; if not, create a new class...
587 */
588
589 if ((pclass = FindClass(resource + 9)) == NULL)
590 {
591 /*
592 * Class doesn't exist; see if we have a printer of the same name...
593 */
594
595 if ((pclass = FindPrinter(resource + 9)) != NULL &&
596 !(pclass->type & CUPS_PRINTER_REMOTE))
597 {
598 /*
599 * Yes, return an error...
600 */
601
602 send_ipp_error(con, IPP_NOT_POSSIBLE);
603 return;
604 }
605
606 /*
607 * No, add the pclass...
608 */
609
610 pclass = AddClass(resource + 9);
611 modify = 0;
612 }
613 else if (pclass->type & CUPS_PRINTER_IMPLICIT)
614 {
615 /*
616 * Rename the implicit class to "AnyClass" or remove it...
617 */
618
619 if (ImplicitAnyClasses)
620 {
621 snprintf(pclass->name, sizeof(pclass->name), "Any%s", resource + 9);
622 SortPrinters();
623 }
624 else
625 DeletePrinter(pclass);
626
627 /*
628 * Add the class as a new local class...
629 */
630
631 pclass = AddClass(resource + 9);
632 modify = 0;
633 }
634 else if (pclass->type & CUPS_PRINTER_REMOTE)
635 {
636 /*
637 * Rename the remote class to "Class"...
638 */
639
640 DeletePrinterFilters(pclass);
641 snprintf(pclass->name, sizeof(pclass->name), "%s@%s", resource + 9,
642 pclass->hostname);
643 SetPrinterAttrs(pclass);
644 SortPrinters();
645
646 /*
647 * Add the class as a new local class...
648 */
649
650 pclass = AddClass(resource + 9);
651 modify = 0;
652 }
653 else
654 modify = 1;
655
656 /*
657 * Look for attributes and copy them over as needed...
658 */
659
660 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
661 {
662 strncpy(pclass->location, attr->values[0].string.text, sizeof(pclass->location) - 1);
663 pclass->location[sizeof(pclass->location) - 1] = '\0';
664 }
665
666 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
667 {
668 strncpy(pclass->info, attr->values[0].string.text, sizeof(pclass->info) - 1);
669 pclass->info[sizeof(pclass->info) - 1] = '\0';
670 }
671
672 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
673 {
674 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
675 pclass->name, attr->values[0].boolean, pclass->accepting);
676
677 pclass->accepting = attr->values[0].boolean;
678 }
679 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
680 {
681 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", pclass->name,
682 attr->values[0].integer, pclass->state);
683
684 if (pclass->state == IPP_PRINTER_STOPPED &&
685 attr->values[0].integer != IPP_PRINTER_STOPPED)
686 pclass->state = IPP_PRINTER_IDLE;
687 else if (pclass->state != IPP_PRINTER_STOPPED &&
688 attr->values[0].integer == IPP_PRINTER_STOPPED)
689 {
690 if (pclass->state == IPP_PRINTER_PROCESSING)
691 StopJob(((job_t *)pclass->job)->id);
692
693 pclass->state = IPP_PRINTER_STOPPED;
694 }
695
696 pclass->browse_time = 0;
697 }
698 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
699 {
700 strncpy(pclass->state_message, attr->values[0].string.text,
701 sizeof(pclass->state_message) - 1);
702 pclass->state_message[sizeof(pclass->state_message) - 1] = '\0';
703 }
704 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
705 !Classification[0])
706 {
707 strncpy(pclass->job_sheets[0], attr->values[0].string.text,
708 sizeof(pclass->job_sheets[0]) - 1);
709 if (attr->num_values > 1)
710 strncpy(pclass->job_sheets[1], attr->values[1].string.text,
711 sizeof(pclass->job_sheets[1]) - 1);
712 else
713 strcpy(pclass->job_sheets[1], "none");
714 }
715 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
716 IPP_TAG_ZERO)) != NULL)
717 {
718 FreePrinterUsers(pclass);
719
720 pclass->deny_users = 0;
721 if (attr->value_tag == IPP_TAG_NAME &&
722 (attr->num_values > 1 ||
723 strcmp(attr->values[0].string.text, "all") != 0))
724 for (i = 0; i < attr->num_values; i ++)
725 AddPrinterUser(pclass, attr->values[i].string.text);
726 }
727 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
728 IPP_TAG_ZERO)) != NULL)
729 {
730 FreePrinterUsers(pclass);
731
732 pclass->deny_users = 1;
733 if (attr->value_tag == IPP_TAG_NAME &&
734 (attr->num_values > 1 ||
735 strcmp(attr->values[0].string.text, "none") != 0))
736 for (i = 0; i < attr->num_values; i ++)
737 AddPrinterUser(pclass, attr->values[i].string.text);
738 }
739 if ((attr = ippFindAttribute(con->request, "job-quota-period",
740 IPP_TAG_INTEGER)) != NULL)
741 {
742 FreeQuotas(pclass);
743 pclass->quota_period = attr->values[0].integer;
744 }
745 if ((attr = ippFindAttribute(con->request, "job-k-limit",
746 IPP_TAG_INTEGER)) != NULL)
747 {
748 FreeQuotas(pclass);
749 pclass->k_limit = attr->values[0].integer;
750 }
751 if ((attr = ippFindAttribute(con->request, "job-page-limit",
752 IPP_TAG_INTEGER)) != NULL)
753 {
754 FreeQuotas(pclass);
755 pclass->page_limit = attr->values[0].integer;
756 }
757
758 if ((attr = ippFindAttribute(con->request, "member-uris", IPP_TAG_URI)) != NULL)
759 {
760 /*
761 * Clear the printer array as needed...
762 */
763
764 if (pclass->num_printers > 0)
765 {
766 free(pclass->printers);
767 pclass->num_printers = 0;
768 }
769
770 /*
771 * Add each printer or class that is listed...
772 */
773
774 for (i = 0; i < attr->num_values; i ++)
775 {
776 /*
777 * Search for the printer or class URI...
778 */
779
780 httpSeparate(attr->values[i].string.text, method, username, host,
781 &port, resource);
782
783 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
784 {
785 /*
786 * Bad URI...
787 */
788
789 LogMessage(L_ERROR, "add_class: resource name \'%s\' no good!", resource);
790 send_ipp_error(con, IPP_NOT_FOUND);
791 return;
792 }
793
794 /*
795 * Add it to the class...
796 */
797
798 if (dtype == CUPS_PRINTER_CLASS)
799 {
800 AddPrinterToClass(pclass, FindClass(dest));
801
802 LogMessage(L_DEBUG, "add_class: Added class \"%s\" to class \"%s\"...",
803 dest, pclass->name);
804 }
805 else
806 {
807 AddPrinterToClass(pclass, FindPrinter(dest));
808
809 LogMessage(L_DEBUG, "add_class: Added printer \"%s\" to class \"%s\"...",
810 dest, pclass->name);
811 }
812 }
813 }
814
815 /*
816 * Update the printer class attributes and return...
817 */
818
819 SetPrinterAttrs(pclass);
820 SaveAllClasses();
821 CheckJobs();
822
823 if (modify)
824 LogMessage(L_INFO, "Class \'%s\' modified by \'%s\'.", pclass->name,
825 con->username);
826 else
827 LogMessage(L_INFO, "New class \'%s\' added by \'%s\'.", pclass->name,
828 con->username);
829
830 con->response->request.status.status_code = IPP_OK;
831 }
832
833
834 /*
835 * 'add_file()' - Add a file to a job.
836 */
837
838 static int /* O - 0 on success, -1 on error */
839 add_file(client_t *con, /* I - Connection to client */
840 job_t *job, /* I - Job to add to */
841 mime_type_t *filetype) /* I - Type of file */
842 {
843 mime_type_t **filetypes; /* New filetypes array... */
844
845
846 LogMessage(L_DEBUG2, "add_file(%p[%d], %d, %s/%s)\n", con, con->http.fd,
847 job->id, filetype->super, filetype->type);
848
849 /*
850 * Add the file to the job...
851 */
852
853 if (job->num_files == 0)
854 filetypes = (mime_type_t **)malloc(sizeof(mime_type_t *));
855 else
856 filetypes = (mime_type_t **)realloc(job->filetypes,
857 (job->num_files + 1) *
858 sizeof(mime_type_t));
859
860 if (filetypes == NULL)
861 {
862 CancelJob(job->id, 1);
863 LogMessage(L_ERROR, "add_file: unable to allocate memory for file types!");
864 send_ipp_error(con, IPP_INTERNAL_ERROR);
865 return (-1);
866 }
867
868 job->filetypes = filetypes;
869 job->filetypes[job->num_files] = filetype;
870
871 job->num_files ++;
872
873 return (0);
874 }
875
876
877 /*
878 * 'add_job_state_reasons()' - Add the "job-state-reasons" attribute based
879 * upon the job and printer state...
880 */
881
882 static void
883 add_job_state_reasons(client_t *con, /* I - Client connection */
884 job_t *job) /* I - Job info */
885 {
886 printer_t *dest; /* Destination printer */
887
888
889 LogMessage(L_DEBUG2, "add_job_state_reasons(%p[%d], %d)\n", con, con->http.fd,
890 job->id);
891
892 switch (job->state->values[0].integer)
893 {
894 case IPP_JOB_PENDING :
895 if (job->dtype & CUPS_PRINTER_CLASS)
896 dest = FindClass(job->dest);
897 else
898 dest = FindPrinter(job->dest);
899
900 if (dest != NULL && dest->state == IPP_PRINTER_STOPPED)
901 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
902 "job-state-reasons", NULL, "printer-stopped");
903 else
904 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
905 "job-state-reasons", NULL, "none");
906 break;
907
908 case IPP_JOB_HELD :
909 if (ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD) != NULL ||
910 ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME) != NULL)
911 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
912 "job-state-reasons", NULL, "job-hold-until-specified");
913 else
914 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
915 "job-state-reasons", NULL, "job-incoming");
916 break;
917
918 case IPP_JOB_PROCESSING :
919 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
920 "job-state-reasons", NULL, "job-printing");
921 break;
922
923 case IPP_JOB_STOPPED :
924 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
925 "job-state-reasons", NULL, "job-stopped");
926 break;
927
928 case IPP_JOB_CANCELLED :
929 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
930 "job-state-reasons", NULL, "job-canceled-by-user");
931 break;
932
933 case IPP_JOB_ABORTED :
934 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
935 "job-state-reasons", NULL, "aborted-by-system");
936 break;
937
938 case IPP_JOB_COMPLETED :
939 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
940 "job-state-reasons", NULL, "job-completed-successfully");
941 break;
942 }
943 }
944
945
946 /*
947 * 'add_printer()' - Add a printer to the system.
948 */
949
950 static void
951 add_printer(client_t *con, /* I - Client connection */
952 ipp_attribute_t *uri) /* I - URI of printer */
953 {
954 int i; /* Looping var */
955 char method[HTTP_MAX_URI],
956 /* Method portion of URI */
957 username[HTTP_MAX_URI],
958 /* Username portion of URI */
959 host[HTTP_MAX_URI],
960 /* Host portion of URI */
961 resource[HTTP_MAX_URI];
962 /* Resource portion of URI */
963 int port; /* Port portion of URI */
964 printer_t *printer; /* Printer/class */
965 ipp_attribute_t *attr; /* Printer attribute */
966 #ifdef HAVE_LIBZ
967 gzFile fp; /* Script/PPD file */
968 #else
969 FILE *fp; /* Script/PPD file */
970 #endif /* HAVE_LIBZ */
971 char line[1024]; /* Line from file... */
972 char srcfile[1024], /* Source Script/PPD file */
973 dstfile[1024]; /* Destination Script/PPD file */
974 int modify; /* Non-zero if we are modifying */
975
976
977 LogMessage(L_DEBUG2, "add_printer(%p[%d], %s)\n", con, con->http.fd,
978 uri->values[0].string.text);
979
980 /*
981 * Was this operation called from the correct URI?
982 */
983
984 if (strncmp(con->uri, "/admin/", 7) != 0)
985 {
986 LogMessage(L_ERROR, "add_printer: admin request on bad resource \'%s\'!",
987 con->uri);
988 send_ipp_error(con, IPP_NOT_AUTHORIZED);
989 return;
990 }
991
992 /*
993 * Do we have a valid URI?
994 */
995
996 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
997
998 if (strncmp(resource, "/printers/", 10) != 0 || strlen(resource) == 10)
999 {
1000 /*
1001 * No, return an error...
1002 */
1003
1004 send_ipp_error(con, IPP_BAD_REQUEST);
1005 return;
1006 }
1007
1008 /*
1009 * See if the printer already exists; if not, create a new printer...
1010 */
1011
1012 if ((printer = FindPrinter(resource + 10)) == NULL)
1013 {
1014 /*
1015 * Printer doesn't exist; see if we have a class of the same name...
1016 */
1017
1018 if ((printer = FindClass(resource + 10)) != NULL &&
1019 !(printer->type & CUPS_PRINTER_REMOTE))
1020 {
1021 /*
1022 * Yes, return an error...
1023 */
1024
1025 send_ipp_error(con, IPP_NOT_POSSIBLE);
1026 return;
1027 }
1028
1029 /*
1030 * No, add the printer...
1031 */
1032
1033 printer = AddPrinter(resource + 10);
1034 modify = 0;
1035 }
1036 else if (printer->type & CUPS_PRINTER_IMPLICIT)
1037 {
1038 /*
1039 * Rename the implicit printer to "AnyPrinter" or delete it...
1040 */
1041
1042 if (ImplicitAnyClasses)
1043 {
1044 snprintf(printer->name, sizeof(printer->name), "Any%s", resource + 10);
1045 SortPrinters();
1046 }
1047 else
1048 DeletePrinter(printer);
1049
1050 /*
1051 * Add the printer as a new local printer...
1052 */
1053
1054 printer = AddPrinter(resource + 10);
1055 modify = 0;
1056 }
1057 else if (printer->type & CUPS_PRINTER_REMOTE)
1058 {
1059 /*
1060 * Rename the remote printer to "Printer@server"...
1061 */
1062
1063 DeletePrinterFilters(printer);
1064 snprintf(printer->name, sizeof(printer->name), "%s@%s", resource + 10,
1065 printer->hostname);
1066 SetPrinterAttrs(printer);
1067 SortPrinters();
1068
1069 /*
1070 * Add the printer as a new local printer...
1071 */
1072
1073 printer = AddPrinter(resource + 10);
1074 modify = 0;
1075 }
1076 else
1077 modify = 1;
1078
1079 /*
1080 * Look for attributes and copy them over as needed...
1081 */
1082
1083 if ((attr = ippFindAttribute(con->request, "printer-location", IPP_TAG_TEXT)) != NULL)
1084 {
1085 strncpy(printer->location, attr->values[0].string.text, sizeof(printer->location) - 1);
1086 printer->location[sizeof(printer->location) - 1] = '\0';
1087 }
1088
1089 if ((attr = ippFindAttribute(con->request, "printer-info", IPP_TAG_TEXT)) != NULL)
1090 {
1091 strncpy(printer->info, attr->values[0].string.text, sizeof(printer->info) - 1);
1092 printer->info[sizeof(printer->info) - 1] = '\0';
1093 }
1094
1095 if ((attr = ippFindAttribute(con->request, "device-uri", IPP_TAG_URI)) != NULL)
1096 {
1097 ipp_attribute_t *device; /* Current device */
1098 int methodlen; /* Length of method string */
1099
1100
1101 /*
1102 * Do we have a valid device URI?
1103 */
1104
1105 httpSeparate(attr->values[0].string.text, method, username, host,
1106 &port, resource);
1107 methodlen = strlen(method);
1108
1109 if (strcmp(method, "file") != 0)
1110 {
1111 /*
1112 * See if the backend is listed as a device...
1113 */
1114
1115 for (device = ippFindAttribute(Devices, "device-uri", IPP_TAG_URI);
1116 device != NULL;
1117 device = ippFindNextAttribute(Devices, "device-uri", IPP_TAG_URI))
1118 if (strncmp(method, device->values[0].string.text, methodlen) == 0 &&
1119 (device->values[0].string.text[methodlen] == ':' ||
1120 device->values[0].string.text[methodlen] == '\0'))
1121 break;
1122
1123 if (device == NULL)
1124 {
1125 /*
1126 * Could not find device in list!
1127 */
1128
1129 LogMessage(L_ERROR, "add_printer: bad device-uri attribute \'%s\'!",
1130 attr->values[0].string.text);
1131 send_ipp_error(con, IPP_NOT_POSSIBLE);
1132 return;
1133 }
1134 }
1135
1136 LogMessage(L_INFO, "Setting %s device-uri to \"%s\" (was \"%s\".)",
1137 printer->name, attr->values[0].string.text, printer->device_uri);
1138
1139 strncpy(printer->device_uri, attr->values[0].string.text,
1140 sizeof(printer->device_uri) - 1);
1141 printer->device_uri[sizeof(printer->device_uri) - 1] = '\0';
1142 }
1143
1144 if ((attr = ippFindAttribute(con->request, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
1145 {
1146 LogMessage(L_INFO, "Setting %s printer-is-accepting-jobs to %d (was %d.)",
1147 printer->name, attr->values[0].boolean, printer->accepting);
1148
1149 printer->accepting = attr->values[0].boolean;
1150 }
1151 if ((attr = ippFindAttribute(con->request, "printer-state", IPP_TAG_ENUM)) != NULL)
1152 {
1153 LogMessage(L_INFO, "Setting %s printer-state to %d (was %d.)", printer->name,
1154 attr->values[0].integer, printer->state);
1155
1156 if (printer->state == IPP_PRINTER_STOPPED &&
1157 attr->values[0].integer != IPP_PRINTER_STOPPED)
1158 printer->state = IPP_PRINTER_IDLE;
1159 else if (printer->state != IPP_PRINTER_STOPPED &&
1160 attr->values[0].integer == IPP_PRINTER_STOPPED)
1161 {
1162 if (printer->state == IPP_PRINTER_PROCESSING)
1163 StopJob(((job_t *)printer->job)->id);
1164
1165 printer->state = IPP_PRINTER_STOPPED;
1166 }
1167
1168 printer->browse_time = 0;
1169 }
1170 if ((attr = ippFindAttribute(con->request, "printer-state-message", IPP_TAG_TEXT)) != NULL)
1171 {
1172 strncpy(printer->state_message, attr->values[0].string.text,
1173 sizeof(printer->state_message) - 1);
1174 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
1175 }
1176 if ((attr = ippFindAttribute(con->request, "job-sheets-default", IPP_TAG_ZERO)) != NULL &&
1177 !Classification[0])
1178 {
1179 strncpy(printer->job_sheets[0], attr->values[0].string.text,
1180 sizeof(printer->job_sheets[0]) - 1);
1181 if (attr->num_values > 1)
1182 strncpy(printer->job_sheets[1], attr->values[1].string.text,
1183 sizeof(printer->job_sheets[1]) - 1);
1184 else
1185 strcpy(printer->job_sheets[1], "none");
1186 }
1187 if ((attr = ippFindAttribute(con->request, "requesting-user-name-allowed",
1188 IPP_TAG_ZERO)) != NULL)
1189 {
1190 FreePrinterUsers(printer);
1191
1192 printer->deny_users = 0;
1193 if (attr->value_tag == IPP_TAG_NAME)
1194 for (i = 0; i < attr->num_values; i ++)
1195 AddPrinterUser(printer, attr->values[i].string.text);
1196 }
1197 else if ((attr = ippFindAttribute(con->request, "requesting-user-name-denied",
1198 IPP_TAG_ZERO)) != NULL)
1199 {
1200 FreePrinterUsers(printer);
1201
1202 printer->deny_users = 1;
1203 if (attr->value_tag == IPP_TAG_NAME)
1204 for (i = 0; i < attr->num_values; i ++)
1205 AddPrinterUser(printer, attr->values[i].string.text);
1206 }
1207 if ((attr = ippFindAttribute(con->request, "job-quota-period",
1208 IPP_TAG_INTEGER)) != NULL)
1209 {
1210 FreeQuotas(printer);
1211 printer->quota_period = attr->values[0].integer;
1212 }
1213 if ((attr = ippFindAttribute(con->request, "job-k-limit",
1214 IPP_TAG_INTEGER)) != NULL)
1215 {
1216 FreeQuotas(printer);
1217 printer->k_limit = attr->values[0].integer;
1218 }
1219 if ((attr = ippFindAttribute(con->request, "job-page-limit",
1220 IPP_TAG_INTEGER)) != NULL)
1221 {
1222 FreeQuotas(printer);
1223 printer->page_limit = attr->values[0].integer;
1224 }
1225
1226 /*
1227 * See if we have all required attributes...
1228 */
1229
1230 if (printer->device_uri[0] == '\0')
1231 strcpy(printer->device_uri, "file:/dev/null");
1232
1233 /*
1234 * See if we have an interface script or PPD file attached to the request...
1235 */
1236
1237 if (con->filename[0])
1238 {
1239 strncpy(srcfile, con->filename, sizeof(srcfile) - 1);
1240 srcfile[sizeof(srcfile) - 1] = '\0';
1241 }
1242 else if ((attr = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME)) != NULL)
1243 {
1244 if (strcmp(attr->values[0].string.text, "raw") == 0)
1245 strcpy(srcfile, "raw");
1246 else
1247 snprintf(srcfile, sizeof(srcfile), "%s/model/%s", DataDir,
1248 attr->values[0].string.text);
1249 }
1250 else
1251 srcfile[0] = '\0';
1252
1253 LogMessage(L_DEBUG, "add_printer: srcfile = \"%s\"", srcfile);
1254
1255 if (strcmp(srcfile, "raw") == 0)
1256 {
1257 /*
1258 * Raw driver, remove any existing PPD or interface script files.
1259 */
1260
1261 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1262 printer->name);
1263 unlink(dstfile);
1264
1265 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1266 printer->name);
1267 unlink(dstfile);
1268 }
1269 #ifdef HAVE_LIBZ
1270 else if (srcfile[0] && (fp = gzopen(srcfile, "rb")) != NULL)
1271 #else
1272 else if (srcfile[0] && (fp = fopen(srcfile, "rb")) != NULL)
1273 #endif /* HAVE_LIBZ */
1274 {
1275 /*
1276 * Yes; get the first line from it...
1277 */
1278
1279 line[0] = '\0';
1280 #ifdef HAVE_LIBZ
1281 gzgets(fp, line, sizeof(line));
1282 gzclose(fp);
1283 #else
1284 fgets(line, sizeof(line), fp);
1285 fclose(fp);
1286 #endif /* HAVE_LIBZ */
1287
1288 /*
1289 * Then see what kind of file it is...
1290 */
1291
1292 snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
1293 printer->name);
1294
1295 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1296 {
1297 /*
1298 * The new file is a PPD file, so remove any old interface script
1299 * that might be lying around...
1300 */
1301
1302 unlink(dstfile);
1303 }
1304 else
1305 {
1306 /*
1307 * This must be an interface script, so move the file over to the
1308 * interfaces directory and make it executable...
1309 */
1310
1311 if (copy_file(srcfile, dstfile))
1312 {
1313 LogMessage(L_ERROR, "add_printer: Unable to copy interface script from %s to %s - %s!",
1314 srcfile, dstfile, strerror(errno));
1315 send_ipp_error(con, IPP_INTERNAL_ERROR);
1316 return;
1317 }
1318 else
1319 {
1320 LogMessage(L_DEBUG, "add_printer: Copied interface script successfully!");
1321 chmod(dstfile, 0755);
1322 }
1323 }
1324
1325 snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
1326 printer->name);
1327
1328 if (strncmp(line, "*PPD-Adobe", 10) == 0)
1329 {
1330 /*
1331 * The new file is a PPD file, so move the file over to the
1332 * ppd directory and make it readable by all...
1333 */
1334
1335 if (copy_file(srcfile, dstfile))
1336 {
1337 LogMessage(L_ERROR, "add_printer: Unable to copy PPD file from %s to %s - %s!",
1338 srcfile, dstfile, strerror(errno));
1339 send_ipp_error(con, IPP_INTERNAL_ERROR);
1340 return;
1341 }
1342 else
1343 {
1344 LogMessage(L_DEBUG, "add_printer: Copied PPD file successfully!");
1345 chmod(dstfile, 0644);
1346 }
1347 }
1348 else
1349 {
1350 /*
1351 * This must be an interface script, so remove any old PPD file that
1352 * may be lying around...
1353 */
1354
1355 unlink(dstfile);
1356 }
1357 }
1358
1359 /*
1360 * Make this printer the default if there is none...
1361 */
1362
1363 if (DefaultPrinter == NULL)
1364 DefaultPrinter = printer;
1365
1366 /*
1367 * Update the printer attributes and return...
1368 */
1369
1370 SetPrinterAttrs(printer);
1371 SaveAllPrinters();
1372
1373 if (printer->job != NULL)
1374 {
1375 /*
1376 * Stop the current job and then restart it below...
1377 */
1378
1379 StopJob(((job_t *)printer->job)->id);
1380 }
1381
1382 CheckJobs();
1383
1384 if (modify)
1385 LogMessage(L_INFO, "Printer \'%s\' modified by \'%s\'.", printer->name,
1386 con->username);
1387 else
1388 LogMessage(L_INFO, "New printer \'%s\' added by \'%s\'.", printer->name,
1389 con->username);
1390
1391 con->response->request.status.status_code = IPP_OK;
1392 }
1393
1394
1395 /*
1396 * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
1397 * based upon the printer state...
1398 */
1399
1400 static void
1401 add_printer_state_reasons(client_t *con, /* I - Client connection */
1402 printer_t *p) /* I - Printer info */
1403 {
1404 LogMessage(L_DEBUG2, "add_printer_state_reasons(%p[%d], %p[%s])\n",
1405 con, con->http.fd, p, p->name);
1406
1407 switch (p->state)
1408 {
1409 case IPP_PRINTER_STOPPED :
1410 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1411 "printer-state-reasons", NULL, "paused");
1412 break;
1413
1414 default :
1415 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
1416 "printer-state-reasons", NULL, "none");
1417 break;
1418 }
1419 }
1420
1421
1422 /*
1423 * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
1424 * the specified printer or class.
1425 */
1426
1427 static void
1428 add_queued_job_count(client_t *con, /* I - Client connection */
1429 printer_t *p) /* I - Printer or class */
1430 {
1431 int count; /* Number of jobs on destination */
1432
1433
1434 LogMessage(L_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])\n",
1435 con, con->http.fd, p, p->name);
1436
1437 count = GetPrinterJobCount(p->name);
1438
1439 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
1440 "queued-job-count", count);
1441 }
1442
1443
1444 /*
1445 * 'cancel_all_jobs()' - Cancel all print jobs.
1446 */
1447
1448 static void
1449 cancel_all_jobs(client_t *con, /* I - Client connection */
1450 ipp_attribute_t *uri) /* I - Job or Printer URI */
1451 {
1452 const char *dest; /* Destination */
1453 cups_ptype_t dtype; /* Destination type */
1454 char method[HTTP_MAX_URI],
1455 /* Method portion of URI */
1456 username[HTTP_MAX_URI],
1457 /* Username portion of URI */
1458 host[HTTP_MAX_URI],
1459 /* Host portion of URI */
1460 resource[HTTP_MAX_URI];
1461 /* Resource portion of URI */
1462 int port; /* Port portion of URI */
1463 printer_t *printer; /* Current printer */
1464
1465
1466 LogMessage(L_DEBUG2, "cancel_all_jobs(%p[%d], %s)\n", con, con->http.fd,
1467 uri->values[0].string.text);
1468
1469 /*
1470 * Was this operation called from the correct URI?
1471 */
1472
1473 if (strncmp(con->uri, "/admin/", 7) != 0)
1474 {
1475 LogMessage(L_ERROR, "cancel_all_jobs: admin request on bad resource \'%s\'!",
1476 con->uri);
1477 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1478 return;
1479 }
1480
1481 /*
1482 * See if we have a printer URI...
1483 */
1484
1485 if (strcmp(uri->name, "printer-uri") != 0)
1486 {
1487 LogMessage(L_ERROR, "cancel_all_jobs: bad %s attribute \'%s\'!",
1488 uri->name, uri->values[0].string.text);
1489 send_ipp_error(con, IPP_BAD_REQUEST);
1490 return;
1491 }
1492
1493 /*
1494 * And if the destination is valid...
1495 */
1496
1497 httpSeparate(uri->values[0].string.text, method, username, host, &port,
1498 resource);
1499
1500 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
1501 {
1502 /*
1503 * Bad URI?
1504 */
1505
1506 if (strcmp(resource, "/printers/") != 0)
1507 {
1508 LogMessage(L_ERROR, "cancel_all_jobs: resource name \'%s\' no good!", resource);
1509 send_ipp_error(con, IPP_NOT_FOUND);
1510 return;
1511 }
1512
1513 /*
1514 * Cancel all jobs on all printers...
1515 */
1516
1517 for (printer = Printers; printer; printer = printer->next)
1518 {
1519 CancelJobs(printer->name);
1520 LogMessage(L_INFO, "All jobs on \'%s\' were cancelled by \'%s\'.",
1521 printer->name, con->username);
1522 }
1523 }
1524 else
1525 {
1526 /*
1527 * Cancel all of the jobs on the named printer...
1528 */
1529
1530 CancelJobs(dest);
1531 LogMessage(L_INFO, "All jobs on \'%s\' were cancelled by \'%s\'.", dest,
1532 con->username);
1533 }
1534
1535 con->response->request.status.status_code = IPP_OK;
1536 }
1537
1538
1539 /*
1540 * 'cancel_job()' - Cancel a print job.
1541 */
1542
1543 static void
1544 cancel_job(client_t *con, /* I - Client connection */
1545 ipp_attribute_t *uri) /* I - Job or Printer URI */
1546 {
1547 ipp_attribute_t *attr; /* Current attribute */
1548 int jobid; /* Job ID */
1549 char method[HTTP_MAX_URI],
1550 /* Method portion of URI */
1551 username[HTTP_MAX_URI],
1552 /* Username portion of URI */
1553 host[HTTP_MAX_URI],
1554 /* Host portion of URI */
1555 resource[HTTP_MAX_URI];
1556 /* Resource portion of URI */
1557 int port; /* Port portion of URI */
1558 job_t *job; /* Job information */
1559 const char *dest; /* Destination */
1560 cups_ptype_t dtype; /* Destination type (printer or class) */
1561 printer_t *printer; /* Printer data */
1562
1563
1564 LogMessage(L_DEBUG2, "cancel_job(%p[%d], %s)\n", con, con->http.fd,
1565 uri->values[0].string.text);
1566
1567 /*
1568 * Verify that the POST operation was done to a valid URI.
1569 */
1570
1571 if (strncmp(con->uri, "/classes/", 9) != 0 &&
1572 strncmp(con->uri, "/jobs/", 5) != 0 &&
1573 strncmp(con->uri, "/printers/", 10) != 0)
1574 {
1575 LogMessage(L_ERROR, "cancel_job: cancel request on bad resource \'%s\'!",
1576 con->uri);
1577 send_ipp_error(con, IPP_NOT_AUTHORIZED);
1578 return;
1579 }
1580
1581 /*
1582 * See if we have a job URI or a printer URI...
1583 */
1584
1585 if (strcmp(uri->name, "printer-uri") == 0)
1586 {
1587 /*
1588 * Got a printer URI; see if we also have a job-id attribute...
1589 */
1590
1591 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
1592 {
1593 LogMessage(L_ERROR, "cancel_job: got a printer-uri attribute but no job-id!");
1594 send_ipp_error(con, IPP_BAD_REQUEST);
1595 return;
1596 }
1597
1598 if ((jobid = attr->values[0].integer) == 0)
1599 {
1600 /*
1601 * Find the current job on the specified printer...
1602 */
1603
1604 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1605
1606 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
1607 {
1608 /*
1609 * Bad URI...
1610 */
1611
1612 LogMessage(L_ERROR, "cancel_job: resource name \'%s\' no good!", resource);
1613 send_ipp_error(con, IPP_NOT_FOUND);
1614 return;
1615 }
1616
1617 if (dtype & CUPS_PRINTER_CLASS)
1618 printer = FindClass(dest);
1619 else
1620 printer = FindPrinter(dest);
1621
1622 /*
1623 * See if the printer is currently printing a job...
1624 */
1625
1626 if (printer->job)
1627 jobid = ((job_t *)printer->job)->id;
1628 else
1629 {
1630 /*
1631 * No, see if there are any pending jobs...
1632 */
1633
1634 for (job = Jobs; job != NULL; job = job->next)
1635 if (job->state->values[0].integer <= IPP_JOB_PROCESSING &&
1636 strcasecmp(job->dest, dest) == 0)
1637 break;
1638
1639 if (job != NULL)
1640 jobid = job->id;
1641 else
1642 {
1643 LogMessage(L_ERROR, "cancel_job: No active jobs on %s!", dest);
1644 send_ipp_error(con, IPP_NOT_POSSIBLE);
1645 return;
1646 }
1647 }
1648 }
1649 }
1650 else
1651 {
1652 /*
1653 * Got a job URI; parse it to get the job ID...
1654 */
1655
1656 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
1657
1658 if (strncmp(resource, "/jobs/", 6) != 0)
1659 {
1660 /*
1661 * Not a valid URI!
1662 */
1663
1664 LogMessage(L_ERROR, "cancel_job: bad job-uri attribute \'%s\'!",
1665 uri->values[0].string.text);
1666 send_ipp_error(con, IPP_BAD_REQUEST);
1667 return;
1668 }
1669
1670 jobid = atoi(resource + 6);
1671 }
1672
1673 /*
1674 * See if the job exists...
1675 */
1676
1677 if ((job = FindJob(jobid)) == NULL)
1678 {
1679 /*
1680 * Nope - return a "not found" error...
1681 */
1682
1683 LogMessage(L_ERROR, "cancel_job: job #%d doesn't exist!", jobid);
1684 send_ipp_error(con, IPP_NOT_FOUND);
1685 return;
1686 }
1687
1688 /*
1689 * See if the job is owned by the requesting user...
1690 */
1691
1692 if (!validate_user(con, job->username, username, sizeof(username)))
1693 {
1694 LogMessage(L_ERROR, "cancel_job: \"%s\" not authorized to delete job id %d owned by \"%s\"!",
1695 username, jobid, job->username);
1696 send_ipp_error(con, IPP_FORBIDDEN);
1697 return;
1698 }
1699
1700 /*
1701 * See if the job is already completed, cancelled, or aborted; if so,
1702 * we can't cancel...
1703 */
1704
1705 if (job->state->values[0].integer >= IPP_JOB_CANCELLED)
1706 {
1707 LogMessage(L_ERROR, "cancel_job: job id %d is %s - can't cancel!",
1708 jobid,
1709 job->state->values[0].integer == IPP_JOB_CANCELLED ? "cancelled" :
1710 job->state->values[0].integer == IPP_JOB_ABORTED ? "aborted" :
1711 "completed");
1712 send_ipp_error(con, IPP_NOT_POSSIBLE);
1713 return;
1714 }
1715
1716 /*
1717 * Cancel the job and return...
1718 */
1719
1720 CancelJob(jobid, 0);
1721 CheckJobs();
1722
1723 LogMessage(L_INFO, "Job %d was cancelled by \'%s\'.", jobid, username);
1724
1725 con->response->request.status.status_code = IPP_OK;
1726 }
1727
1728
1729 /*
1730 * 'check_quotas()' - Check quotas for a printer and user.
1731 */
1732
1733 static int /* O - 1 if OK, 0 if not */
1734 check_quotas(client_t *con, /* I - Client connection */
1735 printer_t *p) /* I - Printer or class */
1736 {
1737 int i; /* Looping var */
1738 ipp_attribute_t *attr; /* Current attribute */
1739 char username[33]; /* Username */
1740 quota_t *q; /* Quota data */
1741
1742
1743 LogMessage(L_DEBUG2, "check_quotas(%p[%d], %p[%s])\n",
1744 con, con->http.fd, p, p->name);
1745
1746 /*
1747 * Check input...
1748 */
1749
1750 if (con == NULL || p == NULL)
1751 return (0);
1752
1753 /*
1754 * Figure out who is printing...
1755 */
1756
1757 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
1758
1759 if (con->username[0])
1760 {
1761 strncpy(username, con->username, sizeof(username) - 1);
1762 username[sizeof(username) - 1] = '\0';
1763 }
1764 else if (attr != NULL)
1765 {
1766 LogMessage(L_DEBUG, "check_quotas: requesting-user-name = \'%s\'",
1767 attr->values[0].string.text);
1768
1769 strncpy(username, attr->values[0].string.text, sizeof(username) - 1);
1770 username[sizeof(username) - 1] = '\0';
1771 }
1772 else
1773 strcpy(username, "anonymous");
1774
1775 /*
1776 * Check global active job limits for printers and users...
1777 */
1778
1779 if (MaxJobsPerPrinter)
1780 {
1781 /*
1782 * Check if there are too many pending jobs on this printer...
1783 */
1784
1785 if (GetPrinterJobCount(p->name) >= MaxJobsPerPrinter)
1786 {
1787 LogMessage(L_INFO, "Too many jobs for printer \"%s\"...", p->name);
1788 return (0);
1789 }
1790 }
1791
1792 if (MaxJobsPerUser)
1793 {
1794 /*
1795 * Check if there are too many pending jobs for this user...
1796 */
1797
1798 if (GetUserJobCount(username) >= MaxJobsPerUser)
1799 {
1800 LogMessage(L_INFO, "Too many jobs for user \"%s\"...", username);
1801 return (0);
1802 }
1803 }
1804
1805 /*
1806 * Check against users...
1807 */
1808
1809 if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
1810 return (1);
1811
1812 if (p->num_users)
1813 {
1814 for (i = 0; i < p->num_users; i ++)
1815 if (strcasecmp(username, p->users[i]) == 0)
1816 break;
1817
1818 if ((i < p->num_users) == p->deny_users)
1819 {
1820 LogMessage(L_INFO, "Denying user \"%s\" access to printer \"%s\"...",
1821 username, p->name);
1822 return (0);
1823 }
1824 }
1825
1826 /*
1827 * Check quotas...
1828 */
1829
1830 if (p->k_limit || p->page_limit)
1831 {
1832 if ((q = UpdateQuota(p, username, 0, 0)) == NULL)
1833 {
1834 LogMessage(L_ERROR, "Unable to allocate quota data for user \"%s\"!",
1835 username);
1836 return (0);
1837 }
1838
1839 if ((q->k_count >= p->k_limit && p->k_limit) ||
1840 (q->page_count >= p->page_limit && p->page_limit))
1841 {
1842 LogMessage(L_INFO, "User \"%s\" is over the quota limit...",
1843 username);
1844 return (0);
1845 }
1846 }
1847
1848 /*
1849 * If we have gotten this far, we're done!
1850 */
1851
1852 return (1);
1853 }
1854
1855
1856 /*
1857 * 'copy_attribute()' - Copy a single attribute.
1858 */
1859
1860 static void
1861 copy_attribute(ipp_t *to, /* O - Destination request/response */
1862 ipp_attribute_t *attr, /* I - Attribute to copy */
1863 int quickcopy)/* I - Do a quick copy? */
1864 {
1865 int i; /* Looping var */
1866 ipp_attribute_t *toattr; /* Destination attribute */
1867
1868
1869 LogMessage(L_DEBUG2, "copy_attribute(%p, %p[%s,%x,%x])\n", to, attr,
1870 attr->name ? attr->name : "(null)", attr->group_tag,
1871 attr->value_tag);
1872
1873 switch (attr->value_tag & ~IPP_TAG_COPY)
1874 {
1875 case IPP_TAG_ZERO :
1876 ippAddSeparator(to);
1877 break;
1878
1879 case IPP_TAG_INTEGER :
1880 case IPP_TAG_ENUM :
1881 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
1882 attr->name, attr->num_values, NULL);
1883
1884 for (i = 0; i < attr->num_values; i ++)
1885 toattr->values[i].integer = attr->values[i].integer;
1886 break;
1887
1888 case IPP_TAG_BOOLEAN :
1889 toattr = ippAddBooleans(to, attr->group_tag, attr->name,
1890 attr->num_values, NULL);
1891
1892 for (i = 0; i < attr->num_values; i ++)
1893 toattr->values[i].boolean = attr->values[i].boolean;
1894 break;
1895
1896 case IPP_TAG_STRING :
1897 case IPP_TAG_TEXT :
1898 case IPP_TAG_NAME :
1899 case IPP_TAG_KEYWORD :
1900 case IPP_TAG_URI :
1901 case IPP_TAG_URISCHEME :
1902 case IPP_TAG_CHARSET :
1903 case IPP_TAG_LANGUAGE :
1904 case IPP_TAG_MIMETYPE :
1905 toattr = ippAddStrings(to, attr->group_tag,
1906 (ipp_tag_t)(attr->value_tag | quickcopy),
1907 attr->name, attr->num_values, NULL, NULL);
1908
1909 if (quickcopy)
1910 {
1911 for (i = 0; i < attr->num_values; i ++)
1912 toattr->values[i].string.text = attr->values[i].string.text;
1913 }
1914 else
1915 {
1916 for (i = 0; i < attr->num_values; i ++)
1917 toattr->values[i].string.text = strdup(attr->values[i].string.text);
1918 }
1919 break;
1920
1921 case IPP_TAG_DATE :
1922 toattr = ippAddDate(to, attr->group_tag, attr->name,
1923 attr->values[0].date);
1924 break;
1925
1926 case IPP_TAG_RESOLUTION :
1927 toattr = ippAddResolutions(to, attr->group_tag, attr->name,
1928 attr->num_values, IPP_RES_PER_INCH,
1929 NULL, NULL);
1930
1931 for (i = 0; i < attr->num_values; i ++)
1932 {
1933 toattr->values[i].resolution.xres = attr->values[i].resolution.xres;
1934 toattr->values[i].resolution.yres = attr->values[i].resolution.yres;
1935 toattr->values[i].resolution.units = attr->values[i].resolution.units;
1936 }
1937 break;
1938
1939 case IPP_TAG_RANGE :
1940 toattr = ippAddRanges(to, attr->group_tag, attr->name,
1941 attr->num_values, NULL, NULL);
1942
1943 for (i = 0; i < attr->num_values; i ++)
1944 {
1945 toattr->values[i].range.lower = attr->values[i].range.lower;
1946 toattr->values[i].range.upper = attr->values[i].range.upper;
1947 }
1948 break;
1949
1950 case IPP_TAG_TEXTLANG :
1951 case IPP_TAG_NAMELANG :
1952 toattr = ippAddStrings(to, attr->group_tag,
1953 (ipp_tag_t)(attr->value_tag | quickcopy),
1954 attr->name, attr->num_values, NULL, NULL);
1955
1956 if (quickcopy)
1957 {
1958 for (i = 0; i < attr->num_values; i ++)
1959 {
1960 toattr->values[i].string.charset = attr->values[i].string.charset;
1961 toattr->values[i].string.text = attr->values[i].string.text;
1962 }
1963 }
1964 else
1965 {
1966 for (i = 0; i < attr->num_values; i ++)
1967 {
1968 if (!i)
1969 toattr->values[i].string.charset =
1970 strdup(attr->values[i].string.charset);
1971 else
1972 toattr->values[i].string.charset =
1973 toattr->values[0].string.charset;
1974
1975 toattr->values[i].string.text = strdup(attr->values[i].string.text);
1976 }
1977 }
1978 break;
1979
1980 default :
1981 toattr = ippAddIntegers(to, attr->group_tag, attr->value_tag,
1982 attr->name, attr->num_values, NULL);
1983
1984 for (i = 0; i < attr->num_values; i ++)
1985 {
1986 toattr->values[i].unknown.length = attr->values[i].unknown.length;
1987
1988 if (toattr->values[i].unknown.length > 0)
1989 {
1990 if ((toattr->values[i].unknown.data = malloc(toattr->values[i].unknown.length)) == NULL)
1991 toattr->values[i].unknown.length = 0;
1992 else
1993 memcpy(toattr->values[i].unknown.data,
1994 attr->values[i].unknown.data,
1995 toattr->values[i].unknown.length);
1996 }
1997 }
1998 break; /* anti-compiler-warning-code */
1999 }
2000 }
2001
2002
2003 /*
2004 * 'copy_attrs()' - Copy attributes from one request to another.
2005 */
2006
2007 static void
2008 copy_attrs(ipp_t *to, /* I - Destination request */
2009 ipp_t *from, /* I - Source request */
2010 ipp_attribute_t *req, /* I - Requested attributes */
2011 ipp_tag_t group) /* I - Group to copy */
2012 {
2013 int i; /* Looping var */
2014 ipp_attribute_t *fromattr; /* Source attribute */
2015
2016
2017 LogMessage(L_DEBUG2, "copy_attrs(%p, %p, %p, %x)\n", to, from, req, group);
2018
2019 if (to == NULL || from == NULL)
2020 return;
2021
2022 if (req != NULL && strcmp(req->values[0].string.text, "all") == 0)
2023 req = NULL; /* "all" means no filter... */
2024
2025 for (fromattr = from->attrs; fromattr != NULL; fromattr = fromattr->next)
2026 {
2027 /*
2028 * Filter attributes as needed...
2029 */
2030
2031 if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
2032 fromattr->group_tag != IPP_TAG_ZERO)
2033 continue;
2034
2035 if (req != NULL && fromattr->name != NULL)
2036 {
2037 for (i = 0; i < req->num_values; i ++)
2038 if (strcmp(fromattr->name, req->values[i].string.text) == 0)
2039 break;
2040
2041 if (i == req->num_values)
2042 continue;
2043 }
2044
2045 copy_attribute(to, fromattr, IPP_TAG_COPY);
2046 }
2047 }
2048
2049
2050 /*
2051 * 'create_job()' - Print a file to a printer or class.
2052 */
2053
2054 static void
2055 create_job(client_t *con, /* I - Client connection */
2056 ipp_attribute_t *uri) /* I - Printer URI */
2057 {
2058 ipp_attribute_t *attr; /* Current attribute */
2059 const char *dest; /* Destination */
2060 cups_ptype_t dtype; /* Destination type (printer or class) */
2061 int priority; /* Job priority */
2062 char *title; /* Job name/title */
2063 job_t *job; /* Current job */
2064 char job_uri[HTTP_MAX_URI],
2065 /* Job URI */
2066 printer_uri[HTTP_MAX_URI],
2067 /* Printer URI */
2068 method[HTTP_MAX_URI],
2069 /* Method portion of URI */
2070 username[HTTP_MAX_URI],
2071 /* Username portion of URI */
2072 host[HTTP_MAX_URI],
2073 /* Host portion of URI */
2074 resource[HTTP_MAX_URI];
2075 /* Resource portion of URI */
2076 int port; /* Port portion of URI */
2077 printer_t *printer; /* Printer data */
2078 int kbytes; /* Size of print file */
2079
2080
2081 LogMessage(L_DEBUG2, "create_job(%p[%d], %s)\n", con, con->http.fd,
2082 uri->values[0].string.text);
2083
2084 /*
2085 * Verify that the POST operation was done to a valid URI.
2086 */
2087
2088 if (strncmp(con->uri, "/classes/", 9) != 0 &&
2089 strncmp(con->uri, "/printers/", 10) != 0)
2090 {
2091 LogMessage(L_ERROR, "create_job: cancel request on bad resource \'%s\'!",
2092 con->uri);
2093 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2094 return;
2095 }
2096
2097 /*
2098 * Is the destination valid?
2099 */
2100
2101 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2102
2103 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
2104 {
2105 /*
2106 * Bad URI...
2107 */
2108
2109 LogMessage(L_ERROR, "create_job: resource name \'%s\' no good!", resource);
2110 send_ipp_error(con, IPP_NOT_FOUND);
2111 return;
2112 }
2113
2114 /*
2115 * See if the printer is accepting jobs...
2116 */
2117
2118 if (dtype == CUPS_PRINTER_CLASS)
2119 {
2120 printer = FindClass(dest);
2121 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
2122 ServerName, ntohs(con->http.hostaddr.sin_port), dest);
2123 }
2124 else
2125 {
2126 printer = FindPrinter(dest);
2127
2128 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
2129 ServerName, ntohs(con->http.hostaddr.sin_port), dest);
2130 }
2131
2132 if (!printer->accepting)
2133 {
2134 LogMessage(L_INFO, "create_job: destination \'%s\' is not accepting jobs.",
2135 dest);
2136 send_ipp_error(con, IPP_NOT_ACCEPTING);
2137 return;
2138 }
2139
2140 /*
2141 * Make sure we aren't over our limit...
2142 */
2143
2144 if (NumJobs >= MaxJobs && MaxJobs)
2145 CleanJobs();
2146
2147 if (NumJobs >= MaxJobs && MaxJobs)
2148 {
2149 LogMessage(L_INFO, "create_job: too many jobs.");
2150 send_ipp_error(con, IPP_NOT_POSSIBLE);
2151 return;
2152 }
2153
2154 if (!check_quotas(con, printer))
2155 {
2156 send_ipp_error(con, IPP_NOT_POSSIBLE);
2157 return;
2158 }
2159
2160 /*
2161 * Create the job and set things up...
2162 */
2163
2164 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
2165 priority = attr->values[0].integer;
2166 else
2167 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2168 priority = 50);
2169
2170 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
2171 title = attr->values[0].string.text;
2172 else
2173 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
2174 title = "Untitled");
2175
2176 if ((job = AddJob(priority, printer->name)) == NULL)
2177 {
2178 LogMessage(L_ERROR, "create_job: unable to add job for destination \'%s\'!",
2179 dest);
2180 send_ipp_error(con, IPP_INTERNAL_ERROR);
2181 return;
2182 }
2183
2184 job->dtype = dtype;
2185 job->attrs = con->request;
2186 con->request = NULL;
2187
2188 strncpy(job->title, title, sizeof(job->title) - 1);
2189
2190 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
2191
2192 if (con->username[0])
2193 {
2194 strncpy(job->username, con->username, sizeof(job->username) - 1);
2195 job->username[sizeof(job->username) - 1] = '\0';
2196 }
2197 else if (attr != NULL)
2198 {
2199 LogMessage(L_DEBUG, "create_job: requesting-user-name = \'%s\'",
2200 attr->values[0].string.text);
2201
2202 strncpy(job->username, attr->values[0].string.text,
2203 sizeof(job->username) - 1);
2204 job->username[sizeof(job->username) - 1] = '\0';
2205 }
2206 else
2207 strcpy(job->username, "anonymous");
2208
2209 if (attr == NULL)
2210 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
2211 NULL, job->username);
2212 else
2213 {
2214 attr->group_tag = IPP_TAG_JOB;
2215 free(attr->name);
2216 attr->name = strdup("job-originating-user-name");
2217 }
2218
2219 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
2220 "job-originating-host-name", NULL, con->http.hostname);
2221 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
2222 time(NULL));
2223 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2224 "time-at-processing", 0);
2225 attr->value_tag = IPP_TAG_NOVALUE;
2226 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2227 "time-at-completed", 0);
2228 attr->value_tag = IPP_TAG_NOVALUE;
2229
2230 /*
2231 * Add remaining job attributes...
2232 */
2233
2234 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
2235 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
2236 "job-state", IPP_JOB_STOPPED);
2237 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2238 "job-media-sheets-completed", 0);
2239 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
2240 printer_uri);
2241 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
2242 title);
2243
2244 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
2245 attr->values[0].integer = 0;
2246 else
2247 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
2248 "job-k-octets", 0);
2249
2250 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
2251 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2252 if (attr == NULL)
2253 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2254 "job-hold-until", NULL, "no-hold");
2255 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
2256 !(printer->type & CUPS_PRINTER_REMOTE))
2257 {
2258 /*
2259 * Hold job until specified time...
2260 */
2261
2262 SetJobHoldUntil(job->id, attr->values[0].string.text);
2263 }
2264 else
2265 job->hold_until = time(NULL) + 60;
2266
2267 job->state->values[0].integer = IPP_JOB_HELD;
2268
2269 if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification[0])
2270 {
2271 /*
2272 * Add job sheets options...
2273 */
2274
2275 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
2276 {
2277 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
2278 2, NULL, NULL);
2279 attr->values[0].string.text = strdup(printer->job_sheets[0]);
2280 attr->values[1].string.text = strdup(printer->job_sheets[1]);
2281 }
2282
2283 job->job_sheets = attr;
2284
2285 /*
2286 * Enforce classification level if set...
2287 */
2288
2289 if (Classification[0])
2290 {
2291 if (ClassifyOverride)
2292 {
2293 if (strcmp(attr->values[0].string.text, "none") == 0 &&
2294 (attr->num_values == 1 ||
2295 strcmp(attr->values[1].string.text, "none") == 0))
2296 {
2297 /*
2298 * Force the leading banner to have the classification on it...
2299 */
2300
2301 free(attr->values[0].string.text);
2302 attr->values[0].string.text = strdup(Classification);
2303 }
2304 else if (attr->num_values == 2 &&
2305 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
2306 strcmp(attr->values[0].string.text, "none") != 0 &&
2307 strcmp(attr->values[1].string.text, "none") != 0)
2308 {
2309 /*
2310 * Can't put two different security markings on the same document!
2311 */
2312
2313 free(attr->values[1].string.text);
2314 attr->values[1].string.text = strdup(attr->values[0].string.text);
2315 }
2316 }
2317 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
2318 (attr->num_values == 1 ||
2319 strcmp(attr->values[1].string.text, Classification) != 0))
2320 {
2321 /*
2322 * Force the leading banner to have the classification on it...
2323 */
2324
2325 free(attr->values[0].string.text);
2326 attr->values[0].string.text = strdup(Classification);
2327 }
2328 }
2329
2330 /*
2331 * See if we need to add the starting sheet...
2332 */
2333
2334 kbytes = copy_banner(con, job, attr->values[0].string.text);
2335
2336 UpdateQuota(printer, job->username, 0, kbytes);
2337 }
2338
2339 /*
2340 * Save and log the job...
2341 */
2342
2343 SaveJob(job->id);
2344
2345 LogMessage(L_INFO, "Job %d created on \'%s\' by \'%s\'.", job->id,
2346 job->dest, job->username);
2347
2348 /*
2349 * Fill in the response info...
2350 */
2351
2352 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
2353 ntohs(con->http.hostaddr.sin_port), job->id);
2354 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
2355
2356 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
2357
2358 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
2359 job->state->values[0].integer);
2360
2361 con->response->request.status.status_code = IPP_OK;
2362 }
2363
2364
2365 /*
2366 * 'copy_banner()' - Copy a banner file to the requests directory for the
2367 * specified job.
2368 */
2369
2370 static int /* O - Size of banner file in kbytes */
2371 copy_banner(client_t *con, /* I - Client connection */
2372 job_t *job, /* I - Job information */
2373 const char *name) /* I - Name of banner */
2374 {
2375 int i; /* Looping var */
2376 int kbytes; /* Size of banner file in kbytes */
2377 char filename[1024]; /* Job filename */
2378 banner_t *banner; /* Pointer to banner */
2379 FILE *in; /* Input file */
2380 FILE *out; /* Output file */
2381 int ch; /* Character from file */
2382 char attrname[255], /* Name of attribute */
2383 *s; /* Pointer into name */
2384 ipp_attribute_t *attr; /* Attribute */
2385
2386
2387 LogMessage(L_DEBUG2, "copy_banner(%p[%d], %p[%d], %s)",
2388 con, con->http.fd, job, job->id, name ? name : "(null)");
2389
2390 /*
2391 * Find the banner; return if not found or "none"...
2392 */
2393
2394 if (name == NULL ||
2395 strcmp(name, "none") == 0 ||
2396 (banner = FindBanner(name)) == NULL)
2397 return (0);
2398
2399 /*
2400 * Open the banner and job files...
2401 */
2402
2403 if (add_file(con, job, banner->filetype))
2404 return (0);
2405
2406 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
2407 job->num_files);
2408 if ((out = fopen(filename, "w")) == NULL)
2409 {
2410 LogMessage(L_ERROR, "copy_banner: Unable to create banner job file %s - %s",
2411 filename, strerror(errno));
2412 job->num_files --;
2413 return (0);
2414 }
2415
2416 fchmod(fileno(out), 0640);
2417 fchown(fileno(out), User, Group);
2418
2419 if (con->language)
2420 {
2421 /*
2422 * Try the localized banner file under the subdirectory...
2423 */
2424
2425 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2426 con->language->language, name);
2427
2428 if (access(filename, 0) && con->language->language[2])
2429 {
2430 /*
2431 * Wasn't able to find "ll_CC" locale file; try the non-national
2432 * localization banner directory.
2433 */
2434
2435 attrname[0] = con->language->language[0];
2436 attrname[1] = con->language->language[1];
2437 attrname[2] = '\0';
2438
2439 snprintf(filename, sizeof(filename), "%s/banners/%s/%s", DataDir,
2440 attrname, name);
2441 }
2442
2443 if (access(filename, 0))
2444 {
2445 /*
2446 * Use the non-localized banner file.
2447 */
2448
2449 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2450 }
2451 }
2452 else
2453 {
2454 /*
2455 * Use the non-localized banner file.
2456 */
2457
2458 snprintf(filename, sizeof(filename), "%s/banners/%s", DataDir, name);
2459 }
2460
2461 if ((in = fopen(filename, "r")) == NULL)
2462 {
2463 fclose(out);
2464 unlink(filename);
2465 LogMessage(L_ERROR, "copy_banner: Unable to open banner template file %s - %s",
2466 filename, strerror(errno));
2467 job->num_files --;
2468 return (0);
2469 }
2470
2471 /*
2472 * Parse the file to the end...
2473 */
2474
2475 while ((ch = getc(in)) != EOF)
2476 if (ch == '{')
2477 {
2478 /*
2479 * Get an attribute name...
2480 */
2481
2482 for (s = attrname; (ch = getc(in)) != EOF;)
2483 if (ch == '}' || isspace(ch))
2484 break;
2485 else if (s < (attrname + sizeof(attrname) - 1))
2486 *s++ = ch;
2487
2488 if (isspace(ch) && s == attrname)
2489 {
2490 /*
2491 * Ignore { followed by whitespace...
2492 */
2493
2494 putc('{', out);
2495 putc(ch, out);
2496 continue;
2497 }
2498
2499 *s = '\0';
2500
2501 /*
2502 * See if it is defined...
2503 */
2504
2505 if (strcmp(attrname, "printer-name") == 0)
2506 {
2507 fputs(job->dest, out);
2508 continue;
2509 }
2510 else if ((attr = ippFindAttribute(job->attrs, attrname, IPP_TAG_ZERO)) == NULL)
2511 continue; /* Nope */
2512
2513 /*
2514 * Output value(s)...
2515 */
2516
2517 for (i = 0; i < attr->num_values; i ++)
2518 {
2519 if (i)
2520 putc(',', out);
2521
2522 switch (attr->value_tag)
2523 {
2524 case IPP_TAG_INTEGER :
2525 case IPP_TAG_ENUM :
2526 if (strncmp(attrname, "time-at-", 8) == 0)
2527 fputs(GetDateTime(attr->values[i].integer), out);
2528 else
2529 fprintf(out, "%d", attr->values[i].integer);
2530 break;
2531
2532 case IPP_TAG_BOOLEAN :
2533 fprintf(out, "%d", attr->values[i].boolean);
2534 break;
2535
2536 case IPP_TAG_NOVALUE :
2537 fputs("novalue", out);
2538 break;
2539
2540 case IPP_TAG_RANGE :
2541 fprintf(out, "%d-%d", attr->values[i].range.lower,
2542 attr->values[i].range.upper);
2543 break;
2544
2545 case IPP_TAG_RESOLUTION :
2546 fprintf(out, "%dx%d%s", attr->values[i].resolution.xres,
2547 attr->values[i].resolution.yres,
2548 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
2549 "dpi" : "dpc");
2550 break;
2551
2552 case IPP_TAG_URI :
2553 case IPP_TAG_STRING :
2554 case IPP_TAG_TEXT :
2555 case IPP_TAG_NAME :
2556 case IPP_TAG_KEYWORD :
2557 case IPP_TAG_CHARSET :
2558 case IPP_TAG_LANGUAGE :
2559 if (strcasecmp(banner->filetype->type, "postscript") == 0)
2560 {
2561 /*
2562 * Need to quote strings for PS banners...
2563 */
2564
2565 const char *p;
2566
2567 for (p = attr->values[i].string.text; *p; p ++)
2568 {
2569 if (*p == '(' || *p == ')' || *p == '\\')
2570 {
2571 putc('\\', out);
2572 putc(*p, out);
2573 }
2574 else if (*p < 32 || *p > 126)
2575 fprintf(out, "\\%03o", *p);
2576 else
2577 putc(*p, out);
2578 }
2579 }
2580 else
2581 fputs(attr->values[i].string.text, out);
2582 break;
2583
2584 default :
2585 break; /* anti-compiler-warning-code */
2586 }
2587 }
2588 }
2589 else if (ch == '\\') /* Quoted char */
2590 putc(getc(in), out);
2591 else
2592 putc(ch, out);
2593
2594 fclose(in);
2595
2596 kbytes = (ftell(out) + 1023) / 1024;
2597
2598 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
2599 attr->values[0].integer += kbytes;
2600
2601 fclose(out);
2602
2603 return (kbytes);
2604 }
2605
2606
2607 /*
2608 * 'copy_file()' - Copy a PPD file or interface script...
2609 */
2610
2611 static int /* O - 0 = success, -1 = error */
2612 copy_file(const char *from, /* I - Source file */
2613 const char *to) /* I - Destination file */
2614 {
2615 #ifdef HAVE_LIBZ
2616 gzFile src; /* Source file */
2617 #else
2618 int src; /* Source file */
2619 #endif /* HAVE_LIBZ */
2620 int dst, /* Destination file */
2621 bytes; /* Bytes to read/write */
2622 char buffer[8192]; /* Copy buffer */
2623
2624
2625 LogMessage(L_DEBUG2, "copy_file(\"%s\", \"%s\")\n", from, to);
2626
2627 #ifdef HAVE_LIBZ
2628 if ((src = gzopen(from, "rb")) == NULL)
2629 return (-1);
2630 #else
2631 if ((src = open(from, O_RDONLY)) < 0)
2632 return (-1);
2633 #endif /* HAVE_LIBZ */
2634
2635 if ((dst = open(to, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
2636 {
2637 #ifdef HAVE_LIBZ
2638 gzclose(src);
2639 #else
2640 close(src);
2641 #endif /* HAVE_LIBZ */
2642 return (-1);
2643 }
2644
2645 #ifdef HAVE_LIBZ
2646 while ((bytes = gzread(src, buffer, sizeof(buffer))) > 0)
2647 #else
2648 while ((bytes = read(src, buffer, sizeof(buffer))) > 0)
2649 #endif /* HAVE_LIBZ */
2650 if (write(dst, buffer, bytes) < bytes)
2651 {
2652 #ifdef HAVE_LIBZ
2653 gzclose(src);
2654 #else
2655 close(src);
2656 #endif /* HAVE_LIBZ */
2657 close(dst);
2658 return (-1);
2659 }
2660
2661 #ifdef HAVE_LIBZ
2662 gzclose(src);
2663 #else
2664 close(src);
2665 #endif /* HAVE_LIBZ */
2666 close(dst);
2667
2668 return (0);
2669 }
2670
2671
2672 /*
2673 * 'delete_printer()' - Remove a printer or class from the system.
2674 */
2675
2676 static void
2677 delete_printer(client_t *con, /* I - Client connection */
2678 ipp_attribute_t *uri) /* I - URI of printer or class */
2679 {
2680 const char *dest; /* Destination */
2681 cups_ptype_t dtype; /* Destination type (printer or class) */
2682 char method[HTTP_MAX_URI],
2683 /* Method portion of URI */
2684 username[HTTP_MAX_URI],
2685 /* Username portion of URI */
2686 host[HTTP_MAX_URI],
2687 /* Host portion of URI */
2688 resource[HTTP_MAX_URI];
2689 /* Resource portion of URI */
2690 int port; /* Port portion of URI */
2691 printer_t *printer; /* Printer/class */
2692 char filename[1024]; /* Script/PPD filename */
2693
2694
2695 LogMessage(L_DEBUG2, "delete_printer(%p[%d], %s)\n", con, con->http.fd,
2696 uri->values[0].string.text);
2697
2698 /*
2699 * Was this operation called from the correct URI?
2700 */
2701
2702 if (strncmp(con->uri, "/admin/", 7) != 0)
2703 {
2704 LogMessage(L_ERROR, "delete_printer: admin request on bad resource \'%s\'!",
2705 con->uri);
2706 send_ipp_error(con, IPP_NOT_AUTHORIZED);
2707 return;
2708 }
2709
2710 /*
2711 * Do we have a valid URI?
2712 */
2713
2714 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2715
2716 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
2717 {
2718 /*
2719 * Bad URI...
2720 */
2721
2722 LogMessage(L_ERROR, "delete_printer: resource name \'%s\' no good!", resource);
2723 send_ipp_error(con, IPP_NOT_FOUND);
2724 return;
2725 }
2726
2727 /*
2728 * Find the printer or class and delete it...
2729 */
2730
2731 if (dtype == CUPS_PRINTER_CLASS)
2732 printer = FindClass(dest);
2733 else
2734 printer = FindPrinter(dest);
2735
2736 /*
2737 * Remove old jobs...
2738 */
2739
2740 CancelJobs(dest);
2741
2742 /*
2743 * Remove any old PPD or script files...
2744 */
2745
2746 snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
2747 unlink(filename);
2748
2749 snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
2750 unlink(filename);
2751
2752 if (dtype == CUPS_PRINTER_CLASS)
2753 {
2754 LogMessage(L_INFO, "Class \'%s\' deleted by \'%s\'.", dest,
2755 con->username);
2756
2757 DeletePrinter(printer);
2758 SaveAllClasses();
2759 }
2760 else
2761 {
2762 LogMessage(L_INFO, "Printer \'%s\' deleted by \'%s\'.", dest,
2763 con->username);
2764
2765 DeletePrinter(printer);
2766 SaveAllPrinters();
2767 }
2768
2769 /*
2770 * Return with no errors...
2771 */
2772
2773 con->response->request.status.status_code = IPP_OK;
2774 }
2775
2776
2777 /*
2778 * 'get_default()' - Get the default destination.
2779 */
2780
2781 static void
2782 get_default(client_t *con) /* I - Client connection */
2783 {
2784 LogMessage(L_DEBUG2, "get_default(%p[%d])\n", con, con->http.fd);
2785
2786 if (DefaultPrinter != NULL)
2787 {
2788 copy_attrs(con->response, DefaultPrinter->attrs,
2789 ippFindAttribute(con->request, "requested-attributes",
2790 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
2791
2792 con->response->request.status.status_code = IPP_OK;
2793 }
2794 else
2795 con->response->request.status.status_code = IPP_NOT_FOUND;
2796 }
2797
2798
2799 /*
2800 * 'get_devices()' - Get the list of available devices on the local system.
2801 */
2802
2803 static void
2804 get_devices(client_t *con) /* I - Client connection */
2805 {
2806 LogMessage(L_DEBUG2, "get_devices(%p[%d])\n", con, con->http.fd);
2807
2808 /*
2809 * Copy the device attributes to the response using the requested-attributes
2810 * attribute that may be provided by the client.
2811 */
2812
2813 copy_attrs(con->response, Devices,
2814 ippFindAttribute(con->request, "requested-attributes",
2815 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
2816
2817 con->response->request.status.status_code = IPP_OK;
2818 }
2819
2820
2821 /*
2822 * 'get_jobs()' - Get a list of jobs for the specified printer.
2823 */
2824
2825 static void
2826 get_jobs(client_t *con, /* I - Client connection */
2827 ipp_attribute_t *uri) /* I - Printer URI */
2828 {
2829 ipp_attribute_t *attr, /* Current attribute */
2830 *requested; /* Requested attributes */
2831 const char *dest; /* Destination */
2832 cups_ptype_t dtype; /* Destination type (printer or class) */
2833 char method[HTTP_MAX_URI],
2834 /* Method portion of URI */
2835 username[HTTP_MAX_URI],
2836 /* Username portion of URI */
2837 host[HTTP_MAX_URI],
2838 /* Host portion of URI */
2839 resource[HTTP_MAX_URI];
2840 /* Resource portion of URI */
2841 int port; /* Port portion of URI */
2842 int completed; /* Completed jobs? */
2843 int limit; /* Maximum number of jobs to return */
2844 int count; /* Number of jobs that match */
2845 job_t *job; /* Current job pointer */
2846 char job_uri[HTTP_MAX_URI];
2847 /* Job URI... */
2848
2849
2850 LogMessage(L_DEBUG2, "get_jobs(%p[%d], %s)\n", con, con->http.fd,
2851 uri->values[0].string.text);
2852
2853 /*
2854 * Is the destination valid?
2855 */
2856
2857 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
2858
2859 if ((strncmp(resource, "/jobs", 5) == 0 && strlen(resource) <= 6) ||
2860 (strncmp(resource, "/printers", 9) == 0 && strlen(resource) <= 10))
2861 {
2862 dest = NULL;
2863 dtype = (cups_ptype_t)0;
2864 }
2865 else if (strncmp(resource, "/classes", 8) == 0 && strlen(resource) <= 9)
2866 {
2867 dest = NULL;
2868 dtype = CUPS_PRINTER_CLASS;
2869 }
2870 else if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
2871 {
2872 /*
2873 * Bad URI...
2874 */
2875
2876 LogMessage(L_ERROR, "get_jobs: resource name \'%s\' no good!", resource);
2877 send_ipp_error(con, IPP_NOT_FOUND);
2878 return;
2879 }
2880
2881 /*
2882 * See if the "which-jobs" attribute have been specified; if so, return
2883 * right away if they specify "completed" - we don't keep old job records...
2884 */
2885
2886 if ((attr = ippFindAttribute(con->request, "which-jobs", IPP_TAG_KEYWORD)) != NULL &&
2887 strcmp(attr->values[0].string.text, "completed") == 0)
2888 completed = 1;
2889 else
2890 completed = 0;
2891
2892 /*
2893 * See if they want to limit the number of jobs reported...
2894 */
2895
2896 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
2897 limit = attr->values[0].integer;
2898 else
2899 limit = 1000000;
2900
2901 /*
2902 * See if we only want to see jobs for a specific user...
2903 */
2904
2905 if ((attr = ippFindAttribute(con->request, "my-jobs", IPP_TAG_BOOLEAN)) != NULL &&
2906 attr->values[0].boolean)
2907 {
2908 if (con->username[0])
2909 {
2910 strncpy(username, con->username, sizeof(username) - 1);
2911 username[sizeof(username) - 1] = '\0';
2912 }
2913 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
2914 {
2915 strncpy(username, attr->values[0].string.text, sizeof(username) - 1);
2916 username[sizeof(username) - 1] = '\0';
2917 }
2918 else
2919 strcpy(username, "anonymous");
2920 }
2921 else
2922 username[0] = '\0';
2923
2924 requested = ippFindAttribute(con->request, "requested-attributes",
2925 IPP_TAG_KEYWORD);
2926
2927 /*
2928 * OK, build a list of jobs for this printer...
2929 */
2930
2931 for (count = 0, job = Jobs; count < limit && job != NULL; job = job->next)
2932 {
2933 /*
2934 * Filter out jobs that don't match...
2935 */
2936
2937 LogMessage(L_DEBUG2, "get_jobs: job->id = %d", job->id);
2938
2939 if ((dest != NULL && strcmp(job->dest, dest) != 0))
2940 continue;
2941 if (job->dtype != dtype &&
2942 (username[0] == '\0' || strncmp(resource, "/jobs", 5) != 0))
2943 continue;
2944 if (username[0] != '\0' && strcmp(username, job->username) != 0)
2945 continue;
2946
2947 if (completed && job->state->values[0].integer <= IPP_JOB_STOPPED)
2948 continue;
2949 if (!completed && job->state->values[0].integer > IPP_JOB_STOPPED)
2950 continue;
2951
2952 count ++;
2953
2954 LogMessage(L_DEBUG2, "get_jobs: count = %d", count);
2955
2956 /*
2957 * Send the requested attributes for each job...
2958 */
2959
2960 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
2961 ntohs(con->http.hostaddr.sin_port), job->id);
2962
2963 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
2964 "job-more-info", NULL, job_uri);
2965
2966 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
2967 "job-uri", NULL, job_uri);
2968
2969 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
2970 "job-printer-up-time", time(NULL));
2971
2972 /*
2973 * Copy the job attributes to the response using the requested-attributes
2974 * attribute that may be provided by the client.
2975 */
2976
2977 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB);
2978
2979 add_job_state_reasons(con, job);
2980
2981 ippAddSeparator(con->response);
2982 }
2983
2984 if (requested != NULL)
2985 con->response->request.status.status_code = IPP_OK_SUBST;
2986 else
2987 con->response->request.status.status_code = IPP_OK;
2988 }
2989
2990
2991 /*
2992 * 'get_job_attrs()' - Get job attributes.
2993 */
2994
2995 static void
2996 get_job_attrs(client_t *con, /* I - Client connection */
2997 ipp_attribute_t *uri) /* I - Job URI */
2998 {
2999 ipp_attribute_t *attr, /* Current attribute */
3000 *requested; /* Requested attributes */
3001 int jobid; /* Job ID */
3002 job_t *job; /* Current job */
3003 char method[HTTP_MAX_URI],
3004 /* Method portion of URI */
3005 username[HTTP_MAX_URI],
3006 /* Username portion of URI */
3007 host[HTTP_MAX_URI],
3008 /* Host portion of URI */
3009 resource[HTTP_MAX_URI];
3010 /* Resource portion of URI */
3011 int port; /* Port portion of URI */
3012 char job_uri[HTTP_MAX_URI];
3013 /* Job URI... */
3014
3015
3016 LogMessage(L_DEBUG2, "get_job_attrs(%p[%d], %s)\n", con, con->http.fd,
3017 uri->values[0].string.text);
3018
3019 /*
3020 * See if we have a job URI or a printer URI...
3021 */
3022
3023 if (strcmp(uri->name, "printer-uri") == 0)
3024 {
3025 /*
3026 * Got a printer URI; see if we also have a job-id attribute...
3027 */
3028
3029 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
3030 {
3031 LogMessage(L_ERROR, "get_job_attrs: got a printer-uri attribute but no job-id!");
3032 send_ipp_error(con, IPP_BAD_REQUEST);
3033 return;
3034 }
3035
3036 jobid = attr->values[0].integer;
3037 }
3038 else
3039 {
3040 /*
3041 * Got a job URI; parse it to get the job ID...
3042 */
3043
3044 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3045
3046 if (strncmp(resource, "/jobs/", 6) != 0)
3047 {
3048 /*
3049 * Not a valid URI!
3050 */
3051
3052 LogMessage(L_ERROR, "get_job_attrs: bad job-uri attribute \'%s\'!\n",
3053 uri->values[0].string.text);
3054 send_ipp_error(con, IPP_BAD_REQUEST);
3055 return;
3056 }
3057
3058 jobid = atoi(resource + 6);
3059 }
3060
3061 /*
3062 * See if the job exists...
3063 */
3064
3065 if ((job = FindJob(jobid)) == NULL)
3066 {
3067 /*
3068 * Nope - return a "not found" error...
3069 */
3070
3071 LogMessage(L_ERROR, "get_job_attrs: job #%d doesn't exist!", jobid);
3072 send_ipp_error(con, IPP_NOT_FOUND);
3073 return;
3074 }
3075
3076 /*
3077 * Put out the standard attributes...
3078 */
3079
3080 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d",
3081 ServerName, ntohs(con->http.hostaddr.sin_port),
3082 job->id);
3083
3084 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3085
3086 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3087 "job-more-info", NULL, job_uri);
3088
3089 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
3090 "job-uri", NULL, job_uri);
3091
3092 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
3093 "job-printer-up-time", time(NULL));
3094
3095 /*
3096 * Copy the job attributes to the response using the requested-attributes
3097 * attribute that may be provided by the client.
3098 */
3099
3100 requested = ippFindAttribute(con->request, "requested-attributes",
3101 IPP_TAG_KEYWORD);
3102
3103 copy_attrs(con->response, job->attrs, requested, IPP_TAG_JOB);
3104
3105 add_job_state_reasons(con, job);
3106
3107 if (requested != NULL)
3108 con->response->request.status.status_code = IPP_OK_SUBST;
3109 else
3110 con->response->request.status.status_code = IPP_OK;
3111 }
3112
3113
3114 /*
3115 * 'get_ppds()' - Get the list of PPD files on the local system.
3116 */
3117
3118 static void
3119 get_ppds(client_t *con) /* I - Client connection */
3120 {
3121 LogMessage(L_DEBUG2, "get_ppds(%p[%d])\n", con, con->http.fd);
3122
3123 /*
3124 * Copy the PPD attributes to the response using the requested-attributes
3125 * attribute that may be provided by the client.
3126 */
3127
3128 copy_attrs(con->response, PPDs,
3129 ippFindAttribute(con->request, "requested-attributes",
3130 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
3131
3132 con->response->request.status.status_code = IPP_OK;
3133 }
3134
3135
3136 /*
3137 * 'get_printer_attrs()' - Get printer attributes.
3138 */
3139
3140 static void
3141 get_printer_attrs(client_t *con, /* I - Client connection */
3142 ipp_attribute_t *uri) /* I - Printer URI */
3143 {
3144 const char *dest; /* Destination */
3145 cups_ptype_t dtype; /* Destination type (printer or class) */
3146 char method[HTTP_MAX_URI],
3147 /* Method portion of URI */
3148 username[HTTP_MAX_URI],
3149 /* Username portion of URI */
3150 host[HTTP_MAX_URI],
3151 /* Host portion of URI */
3152 resource[HTTP_MAX_URI];
3153 /* Resource portion of URI */
3154 int port; /* Port portion of URI */
3155 printer_t *printer; /* Printer/class */
3156 time_t curtime; /* Current time */
3157
3158
3159 LogMessage(L_DEBUG2, "get_printer_attrs(%p[%d], %s)\n", con, con->http.fd,
3160 uri->values[0].string.text);
3161
3162 /*
3163 * Is the destination valid?
3164 */
3165
3166 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3167
3168 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3169 {
3170 /*
3171 * Bad URI...
3172 */
3173
3174 LogMessage(L_ERROR, "get_printer_attrs: resource name \'%s\' no good!", resource);
3175 send_ipp_error(con, IPP_NOT_FOUND);
3176 return;
3177 }
3178
3179 if (dtype == CUPS_PRINTER_CLASS)
3180 printer = FindClass(dest);
3181 else
3182 printer = FindPrinter(dest);
3183
3184 curtime = time(NULL);
3185
3186 /*
3187 * Copy the printer attributes to the response using requested-attributes
3188 * and document-format attributes that may be provided by the client.
3189 */
3190
3191 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state",
3192 printer->state);
3193
3194 add_printer_state_reasons(con, printer);
3195
3196 if (printer->state_message[0])
3197 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3198 "printer-state-message", NULL, printer->state_message);
3199
3200 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3201 printer->accepting);
3202
3203 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3204 "printer-up-time", curtime);
3205 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3206 ippTimeToDate(curtime));
3207
3208 add_queued_job_count(con, printer);
3209
3210 copy_attrs(con->response, printer->attrs,
3211 ippFindAttribute(con->request, "requested-attributes",
3212 IPP_TAG_KEYWORD), IPP_TAG_ZERO);
3213
3214 con->response->request.status.status_code = IPP_OK;
3215 }
3216
3217
3218 /*
3219 * 'get_printers()' - Get a list of printers or classes.
3220 */
3221
3222 static void
3223 get_printers(client_t *con, /* I - Client connection */
3224 int type) /* I - 0 or CUPS_PRINTER_CLASS */
3225 {
3226 ipp_attribute_t *attr, /* Current attribute */
3227 *requested; /* Requested attributes */
3228 int limit; /* Maximum number of printers to return */
3229 int count; /* Number of printers that match */
3230 printer_t *printer; /* Current printer pointer */
3231 time_t curtime; /* Current time */
3232 int printer_type, /* printer-type attribute */
3233 printer_mask; /* printer-type-mask attribute */
3234 char *location; /* Location string */
3235 char name[IPP_MAX_NAME],
3236 /* Printer name */
3237 *nameptr; /* Pointer into name */
3238 printer_t *iclass; /* Implicit class */
3239
3240
3241 LogMessage(L_DEBUG2, "get_printers(%p[%d], %x)\n", con, con->http.fd, type);
3242
3243 /*
3244 * See if they want to limit the number of printers reported...
3245 */
3246
3247 if ((attr = ippFindAttribute(con->request, "limit", IPP_TAG_INTEGER)) != NULL)
3248 limit = attr->values[0].integer;
3249 else
3250 limit = 10000000;
3251
3252 /*
3253 * Support filtering...
3254 */
3255
3256 if ((attr = ippFindAttribute(con->request, "printer-type", IPP_TAG_ENUM)) != NULL)
3257 printer_type = attr->values[0].integer;
3258 else
3259 printer_type = 0;
3260
3261 if ((attr = ippFindAttribute(con->request, "printer-type-mask", IPP_TAG_ENUM)) != NULL)
3262 printer_mask = attr->values[0].integer;
3263 else
3264 printer_mask = 0;
3265
3266 if ((attr = ippFindAttribute(con->request, "location", IPP_TAG_TEXT)) != NULL)
3267 location = attr->values[0].string.text;
3268 else
3269 location = NULL;
3270
3271 requested = ippFindAttribute(con->request, "requested-attributes",
3272 IPP_TAG_KEYWORD);
3273
3274 /*
3275 * OK, build a list of printers for this printer...
3276 */
3277
3278 curtime = time(NULL);
3279
3280 for (count = 0, printer = Printers;
3281 count < limit && printer != NULL;
3282 printer = printer->next)
3283 if ((printer->type & CUPS_PRINTER_CLASS) == type &&
3284 (printer->type & printer_mask) == printer_type &&
3285 (location == NULL || strcasecmp(printer->location, location) == 0))
3286 {
3287 /*
3288 * If HideImplicitMembers is enabled, see if this printer or class
3289 * is a member of an implicit class...
3290 */
3291
3292 if (ImplicitClasses && HideImplicitMembers &&
3293 (printer->type & CUPS_PRINTER_REMOTE))
3294 {
3295 /*
3296 * Make a copy of the printer name...
3297 *
3298 * Note: name and printer->name are both IPP_MAX_NAME characters
3299 * in size, so strcpy() is safe...
3300 */
3301
3302 strcpy(name, printer->name);
3303
3304 if ((nameptr = strchr(name, '@')) != NULL)
3305 {
3306 /*
3307 * Strip trailing @server...
3308 */
3309
3310 *nameptr = '\0';
3311
3312 /*
3313 * Find the core printer, if any...
3314 */
3315
3316 if ((iclass = FindPrinter(name)) != NULL &&
3317 (iclass->type & CUPS_PRINTER_IMPLICIT))
3318 continue;
3319 }
3320 }
3321
3322 /*
3323 * Add the group separator as needed...
3324 */
3325
3326 if (count > 0)
3327 ippAddSeparator(con->response);
3328
3329 count ++;
3330
3331 /*
3332 * Send the following attributes for each printer:
3333 *
3334 * printer-state
3335 * printer-state-message
3336 * printer-is-accepting-jobs
3337 * + all printer attributes
3338 */
3339
3340 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM,
3341 "printer-state", printer->state);
3342
3343 add_printer_state_reasons(con, printer);
3344
3345 if (printer->state_message[0])
3346 ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
3347 "printer-state-message", NULL, printer->state_message);
3348
3349 ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
3350 printer->accepting);
3351
3352 ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
3353 "printer-up-time", curtime);
3354 ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
3355 ippTimeToDate(curtime));
3356
3357 add_queued_job_count(con, printer);
3358
3359 copy_attrs(con->response, printer->attrs, requested, IPP_TAG_ZERO);
3360 }
3361
3362 con->response->request.status.status_code = IPP_OK;
3363 }
3364
3365
3366 /*
3367 * 'hold_job()' - Hold a print job.
3368 */
3369
3370 static void
3371 hold_job(client_t *con, /* I - Client connection */
3372 ipp_attribute_t *uri) /* I - Job or Printer URI */
3373 {
3374 ipp_attribute_t *attr, /* Current job-hold-until */
3375 *newattr; /* New job-hold-until */
3376 int jobid; /* Job ID */
3377 char method[HTTP_MAX_URI],
3378 /* Method portion of URI */
3379 username[HTTP_MAX_URI],
3380 /* Username portion of URI */
3381 host[HTTP_MAX_URI],
3382 /* Host portion of URI */
3383 resource[HTTP_MAX_URI];
3384 /* Resource portion of URI */
3385 int port; /* Port portion of URI */
3386 job_t *job; /* Job information */
3387
3388
3389 LogMessage(L_DEBUG2, "hold_job(%p[%d], %s)\n", con, con->http.fd,
3390 uri->values[0].string.text);
3391
3392 /*
3393 * Verify that the POST operation was done to a valid URI.
3394 */
3395
3396 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3397 strncmp(con->uri, "/jobs/", 5) != 0 &&
3398 strncmp(con->uri, "/printers/", 10) != 0)
3399 {
3400 LogMessage(L_ERROR, "hold_job: hold request on bad resource \'%s\'!",
3401 con->uri);
3402 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3403 return;
3404 }
3405
3406 /*
3407 * See if we have a job URI or a printer URI...
3408 */
3409
3410 if (strcmp(uri->name, "printer-uri") == 0)
3411 {
3412 /*
3413 * Got a printer URI; see if we also have a job-id attribute...
3414 */
3415
3416 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
3417 {
3418 LogMessage(L_ERROR, "hold_job: got a printer-uri attribute but no job-id!");
3419 send_ipp_error(con, IPP_BAD_REQUEST);
3420 return;
3421 }
3422
3423 jobid = attr->values[0].integer;
3424 }
3425 else
3426 {
3427 /*
3428 * Got a job URI; parse it to get the job ID...
3429 */
3430
3431 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3432
3433 if (strncmp(resource, "/jobs/", 6) != 0)
3434 {
3435 /*
3436 * Not a valid URI!
3437 */
3438
3439 LogMessage(L_ERROR, "hold_job: bad job-uri attribute \'%s\'!",
3440 uri->values[0].string.text);
3441 send_ipp_error(con, IPP_BAD_REQUEST);
3442 return;
3443 }
3444
3445 jobid = atoi(resource + 6);
3446 }
3447
3448 /*
3449 * See if the job exists...
3450 */
3451
3452 if ((job = FindJob(jobid)) == NULL)
3453 {
3454 /*
3455 * Nope - return a "not found" error...
3456 */
3457
3458 LogMessage(L_ERROR, "hold_job: job #%d doesn't exist!", jobid);
3459 send_ipp_error(con, IPP_NOT_FOUND);
3460 return;
3461 }
3462
3463 /*
3464 * See if the job is owned by the requesting user...
3465 */
3466
3467 if (!validate_user(con, job->username, username, sizeof(username)))
3468 {
3469 LogMessage(L_ERROR, "hold_job: \"%s\" not authorized to hold job id %d owned by \"%s\"!",
3470 username, jobid, job->username);
3471 send_ipp_error(con, IPP_FORBIDDEN);
3472 return;
3473 }
3474
3475 /*
3476 * Hold the job and return...
3477 */
3478
3479 HoldJob(jobid);
3480
3481 if ((newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3482 newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
3483
3484 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
3485 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
3486
3487 if (attr != NULL)
3488 {
3489 /*
3490 * Free the old hold value and copy the new one over...
3491 */
3492
3493 free(attr->values[0].string.text);
3494
3495 if (newattr != NULL)
3496 {
3497 attr->value_tag = newattr->value_tag;
3498 attr->values[0].string.text = strdup(newattr->values[0].string.text);
3499 }
3500 else
3501 {
3502 attr->value_tag = IPP_TAG_KEYWORD;
3503 attr->values[0].string.text = strdup("indefinite");
3504 }
3505
3506 /*
3507 * Hold job until specified time...
3508 */
3509
3510 SetJobHoldUntil(job->id, attr->values[0].string.text);
3511 }
3512
3513 LogMessage(L_INFO, "Job %d was held by \'%s\'.", jobid, username);
3514
3515 con->response->request.status.status_code = IPP_OK;
3516 }
3517
3518
3519 /*
3520 * 'move_job()' - Set job attributes.
3521 */
3522
3523 static void
3524 move_job(client_t *con, /* I - Client connection */
3525 ipp_attribute_t *uri) /* I - Job URI */
3526 {
3527 ipp_attribute_t *attr; /* Current attribute */
3528 int jobid; /* Job ID */
3529 job_t *job; /* Current job */
3530 const char *dest; /* Destination */
3531 cups_ptype_t dtype; /* Destination type (printer or class) */
3532 char method[HTTP_MAX_URI],
3533 /* Method portion of URI */
3534 username[HTTP_MAX_URI],
3535 /* Username portion of URI */
3536 host[HTTP_MAX_URI],
3537 /* Host portion of URI */
3538 resource[HTTP_MAX_URI];
3539 /* Resource portion of URI */
3540 int port; /* Port portion of URI */
3541
3542
3543 LogMessage(L_DEBUG2, "move_job(%p[%d], %s)\n", con, con->http.fd,
3544 uri->values[0].string.text);
3545
3546 /*
3547 * See if we have a job URI or a printer URI...
3548 */
3549
3550 if (strcmp(uri->name, "printer-uri") == 0)
3551 {
3552 /*
3553 * Got a printer URI; see if we also have a job-id attribute...
3554 */
3555
3556 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
3557 {
3558 LogMessage(L_ERROR, "move_job: got a printer-uri attribute but no job-id!");
3559 send_ipp_error(con, IPP_BAD_REQUEST);
3560 return;
3561 }
3562
3563 jobid = attr->values[0].integer;
3564 }
3565 else
3566 {
3567 /*
3568 * Got a job URI; parse it to get the job ID...
3569 */
3570
3571 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3572
3573 if (strncmp(resource, "/jobs/", 6) != 0)
3574 {
3575 /*
3576 * Not a valid URI!
3577 */
3578
3579 LogMessage(L_ERROR, "move_job: bad job-uri attribute \'%s\'!\n",
3580 uri->values[0].string.text);
3581 send_ipp_error(con, IPP_BAD_REQUEST);
3582 return;
3583 }
3584
3585 jobid = atoi(resource + 6);
3586 }
3587
3588 /*
3589 * See if the job exists...
3590 */
3591
3592 if ((job = FindJob(jobid)) == NULL)
3593 {
3594 /*
3595 * Nope - return a "not found" error...
3596 */
3597
3598 LogMessage(L_ERROR, "move_job: job #%d doesn't exist!", jobid);
3599 send_ipp_error(con, IPP_NOT_FOUND);
3600 return;
3601 }
3602
3603 /*
3604 * See if the job has been completed...
3605 */
3606
3607 if (job->state->values[0].integer > IPP_JOB_STOPPED)
3608 {
3609 /*
3610 * Return a "not-possible" error...
3611 */
3612
3613 LogMessage(L_ERROR, "move_job: job #%d is finished and cannot be altered!", jobid);
3614 send_ipp_error(con, IPP_NOT_POSSIBLE);
3615 return;
3616 }
3617
3618 /*
3619 * See if the job is owned by the requesting user...
3620 */
3621
3622 if (!validate_user(con, job->username, username, sizeof(username)))
3623 {
3624 LogMessage(L_ERROR, "move_job: \"%s\" not authorized to move job id %d owned by \"%s\"!",
3625 username, jobid, job->username);
3626 send_ipp_error(con, IPP_FORBIDDEN);
3627 return;
3628 }
3629
3630 if ((attr = ippFindAttribute(con->request, "job-printer-uri", IPP_TAG_URI)) == NULL)
3631 {
3632 /*
3633 * Need job-printer-uri...
3634 */
3635
3636 LogMessage(L_ERROR, "move_job: job-printer-uri attribute missing!");
3637 send_ipp_error(con, IPP_BAD_REQUEST);
3638 return;
3639 }
3640
3641 /*
3642 * Move the job to a different printer or class...
3643 */
3644
3645 httpSeparate(attr->values[0].string.text, method, username, host, &port,
3646 resource);
3647 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3648 {
3649 /*
3650 * Bad URI...
3651 */
3652
3653 LogMessage(L_ERROR, "move_job: resource name \'%s\' no good!", resource);
3654 send_ipp_error(con, IPP_NOT_FOUND);
3655 return;
3656 }
3657
3658 MoveJob(jobid, dest);
3659
3660 /*
3661 * Start jobs if possible...
3662 */
3663
3664 CheckJobs();
3665
3666 /*
3667 * Return with "everything is OK" status...
3668 */
3669
3670 con->response->request.status.status_code = IPP_OK;
3671 }
3672
3673
3674 /*
3675 * 'print_job()' - Print a file to a printer or class.
3676 */
3677
3678 static void
3679 print_job(client_t *con, /* I - Client connection */
3680 ipp_attribute_t *uri) /* I - Printer URI */
3681 {
3682 ipp_attribute_t *attr; /* Current attribute */
3683 ipp_attribute_t *format; /* Document-format attribute */
3684 const char *dest; /* Destination */
3685 cups_ptype_t dtype; /* Destination type (printer or class) */
3686 int priority; /* Job priority */
3687 char *title; /* Job name/title */
3688 job_t *job; /* Current job */
3689 char job_uri[HTTP_MAX_URI],
3690 /* Job URI */
3691 printer_uri[HTTP_MAX_URI],
3692 /* Printer URI */
3693 method[HTTP_MAX_URI],
3694 /* Method portion of URI */
3695 username[HTTP_MAX_URI],
3696 /* Username portion of URI */
3697 host[HTTP_MAX_URI],
3698 /* Host portion of URI */
3699 resource[HTTP_MAX_URI],
3700 /* Resource portion of URI */
3701 filename[1024]; /* Job filename */
3702 int port; /* Port portion of URI */
3703 mime_type_t *filetype; /* Type of file */
3704 char super[MIME_MAX_SUPER],
3705 /* Supertype of file */
3706 type[MIME_MAX_TYPE],
3707 /* Subtype of file */
3708 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
3709 /* Textual name of mime type */
3710 printer_t *printer; /* Printer data */
3711 struct stat fileinfo; /* File information */
3712 int kbytes; /* Size of file */
3713
3714
3715 LogMessage(L_DEBUG2, "print_job(%p[%d], %s)\n", con, con->http.fd,
3716 uri->values[0].string.text);
3717
3718 /*
3719 * Verify that the POST operation was done to a valid URI.
3720 */
3721
3722 if (strncmp(con->uri, "/classes/", 9) != 0 &&
3723 strncmp(con->uri, "/printers/", 10) != 0)
3724 {
3725 LogMessage(L_ERROR, "print_job: cancel request on bad resource \'%s\'!",
3726 con->uri);
3727 send_ipp_error(con, IPP_NOT_AUTHORIZED);
3728 return;
3729 }
3730
3731 /*
3732 * OK, see if the client is sending the document compressed - CUPS
3733 * doesn't support compression yet...
3734 */
3735
3736 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
3737 strcmp(attr->values[0].string.text, "none") == 0)
3738 {
3739 LogMessage(L_ERROR, "print_job: Unsupported compression attribute %s!",
3740 attr->values[0].string.text);
3741 send_ipp_error(con, IPP_ATTRIBUTES);
3742 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
3743 "compression", NULL, attr->values[0].string.text);
3744 return;
3745 }
3746
3747 /*
3748 * Do we have a file to print?
3749 */
3750
3751 if (con->filename[0] == '\0')
3752 {
3753 LogMessage(L_ERROR, "print_job: No file!?!");
3754 send_ipp_error(con, IPP_BAD_REQUEST);
3755 return;
3756 }
3757
3758 /*
3759 * Is it a format we support?
3760 */
3761
3762 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
3763 {
3764 /*
3765 * Grab format from client...
3766 */
3767
3768 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
3769 {
3770 LogMessage(L_ERROR, "print_job: could not scan type \'%s\'!",
3771 format->values[0].string.text);
3772 send_ipp_error(con, IPP_BAD_REQUEST);
3773 return;
3774 }
3775 }
3776 else
3777 {
3778 /*
3779 * No document format attribute? Auto-type it!
3780 */
3781
3782 strcpy(super, "application");
3783 strcpy(type, "octet-stream");
3784 }
3785
3786 if (strcmp(super, "application") == 0 &&
3787 strcmp(type, "octet-stream") == 0)
3788 {
3789 /*
3790 * Auto-type the file...
3791 */
3792
3793 LogMessage(L_DEBUG, "print_job: auto-typing file...");
3794
3795 filetype = mimeFileType(MimeDatabase, con->filename);
3796
3797 if (filetype != NULL)
3798 {
3799 /*
3800 * Replace the document-format attribute value with the auto-typed one.
3801 */
3802
3803 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
3804 filetype->type);
3805
3806 if (format != NULL)
3807 {
3808 free(format->values[0].string.text);
3809 format->values[0].string.text = strdup(mimetype);
3810 }
3811 else
3812 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
3813 "document-format", NULL, mimetype);
3814 }
3815 else
3816 filetype = mimeType(MimeDatabase, super, type);
3817 }
3818 else
3819 filetype = mimeType(MimeDatabase, super, type);
3820
3821 if (filetype == NULL)
3822 {
3823 LogMessage(L_ERROR, "print_job: Unsupported format \'%s/%s\'!",
3824 super, type);
3825 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
3826
3827 if (format)
3828 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
3829 "document-format", NULL, format->values[0].string.text);
3830
3831 return;
3832 }
3833
3834 LogMessage(L_DEBUG, "print_job: request file type is %s/%s.",
3835 filetype->super, filetype->type);
3836
3837 /*
3838 * Is the destination valid?
3839 */
3840
3841 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
3842
3843 if ((dest = ValidateDest(host, resource, &dtype)) == NULL)
3844 {
3845 /*
3846 * Bad URI...
3847 */
3848
3849 LogMessage(L_ERROR, "print_job: resource name \'%s\' no good!", resource);
3850 send_ipp_error(con, IPP_NOT_FOUND);
3851 return;
3852 }
3853
3854 /*
3855 * See if the printer is accepting jobs...
3856 */
3857
3858 if (dtype == CUPS_PRINTER_CLASS)
3859 {
3860 printer = FindClass(dest);
3861 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/classes/%s",
3862 ServerName, ntohs(con->http.hostaddr.sin_port), dest);
3863 }
3864 else
3865 {
3866 printer = FindPrinter(dest);
3867
3868 snprintf(printer_uri, sizeof(printer_uri), "http://%s:%d/printers/%s",
3869 ServerName, ntohs(con->http.hostaddr.sin_port), dest);
3870 }
3871
3872 if (!printer->accepting)
3873 {
3874 LogMessage(L_INFO, "print_job: destination \'%s\' is not accepting jobs.",
3875 dest);
3876 send_ipp_error(con, IPP_NOT_ACCEPTING);
3877 return;
3878 }
3879
3880 /*
3881 * Make sure we aren't over our limit...
3882 */
3883
3884 if (NumJobs >= MaxJobs && MaxJobs)
3885 CleanJobs();
3886
3887 if (NumJobs >= MaxJobs && MaxJobs)
3888 {
3889 LogMessage(L_INFO, "print_job: too many jobs.");
3890 send_ipp_error(con, IPP_NOT_POSSIBLE);
3891 return;
3892 }
3893
3894 if (!check_quotas(con, printer))
3895 {
3896 send_ipp_error(con, IPP_NOT_POSSIBLE);
3897 return;
3898 }
3899
3900 /*
3901 * Create the job and set things up...
3902 */
3903
3904 if ((attr = ippFindAttribute(con->request, "job-priority", IPP_TAG_INTEGER)) != NULL)
3905 priority = attr->values[0].integer;
3906 else
3907 ippAddInteger(con->request, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
3908 priority = 50);
3909
3910 if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_NAME)) != NULL)
3911 title = attr->values[0].string.text;
3912 else
3913 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3914 title = "Untitled");
3915
3916 if ((job = AddJob(priority, printer->name)) == NULL)
3917 {
3918 LogMessage(L_ERROR, "print_job: unable to add job for destination \'%s\'!",
3919 dest);
3920 send_ipp_error(con, IPP_INTERNAL_ERROR);
3921 return;
3922 }
3923
3924 job->dtype = dtype;
3925 job->attrs = con->request;
3926 con->request = NULL;
3927
3928 /*
3929 * Copy the rest of the job info...
3930 */
3931
3932 strncpy(job->title, title, sizeof(job->title) - 1);
3933
3934 attr = ippFindAttribute(job->attrs, "requesting-user-name", IPP_TAG_NAME);
3935
3936 if (con->username[0])
3937 {
3938 strncpy(job->username, con->username, sizeof(job->username) - 1);
3939 job->username[sizeof(job->username) - 1] = '\0';
3940 }
3941 if (attr != NULL)
3942 {
3943 LogMessage(L_DEBUG, "print_job: requesting-user-name = \'%s\'",
3944 attr->values[0].string.text);
3945
3946 strncpy(job->username, attr->values[0].string.text, sizeof(job->username) - 1);
3947 job->username[sizeof(job->username) - 1] = '\0';
3948 }
3949 else
3950 strcpy(job->username, "anonymous");
3951
3952 if (attr == NULL)
3953 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-originating-user-name",
3954 NULL, job->username);
3955 else
3956 {
3957 attr->group_tag = IPP_TAG_JOB;
3958 free(attr->name);
3959 attr->name = strdup("job-originating-user-name");
3960 }
3961
3962 /*
3963 * Add remaining job attributes...
3964 */
3965
3966 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
3967 "job-originating-host-name", NULL, con->http.hostname);
3968 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
3969 job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
3970 "job-state", IPP_JOB_PENDING);
3971 job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3972 "job-media-sheets-completed", 0);
3973 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
3974 printer_uri);
3975 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
3976 title);
3977
3978 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) == NULL)
3979 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3980 "job-k-octets", 0);
3981
3982 if (stat(con->filename, &fileinfo))
3983 kbytes = 0;
3984 else
3985 kbytes = (fileinfo.st_size + 1023) / 1024;
3986
3987 UpdateQuota(printer, job->username, 0, kbytes);
3988 attr->values[0].integer += kbytes;
3989
3990 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "time-at-creation",
3991 time(NULL));
3992 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3993 "time-at-processing", 0);
3994 attr->value_tag = IPP_TAG_NOVALUE;
3995 attr = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
3996 "time-at-completed", 0);
3997 attr->value_tag = IPP_TAG_NOVALUE;
3998
3999 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4000 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4001 if (attr == NULL)
4002 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
4003 "job-hold-until", NULL, "no-hold");
4004
4005 if (attr != NULL && strcmp(attr->values[0].string.text, "no-hold") != 0 &&
4006 !(printer->type & CUPS_PRINTER_REMOTE))
4007 {
4008 /*
4009 * Hold job until specified time...
4010 */
4011
4012 job->state->values[0].integer = IPP_JOB_HELD;
4013 SetJobHoldUntil(job->id, attr->values[0].string.text);
4014 }
4015
4016 if (!(printer->type & CUPS_PRINTER_REMOTE) || Classification[0])
4017 {
4018 /*
4019 * Add job sheets options...
4020 */
4021
4022 if ((attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) == NULL)
4023 {
4024 attr = ippAddStrings(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, "job-sheets",
4025 2, NULL, NULL);
4026 attr->values[0].string.text = strdup(printer->job_sheets[0]);
4027 attr->values[1].string.text = strdup(printer->job_sheets[1]);
4028 }
4029
4030 job->job_sheets = attr;
4031
4032 /*
4033 * Enforce classification level if set...
4034 */
4035
4036 if (Classification[0])
4037 {
4038 if (ClassifyOverride)
4039 {
4040 if (strcmp(attr->values[0].string.text, "none") == 0 &&
4041 (attr->num_values == 1 ||
4042 strcmp(attr->values[1].string.text, "none") == 0))
4043 {
4044 /*
4045 * Force the leading banner to have the classification on it...
4046 */
4047
4048 free(attr->values[0].string.text);
4049 attr->values[0].string.text = strdup(Classification);
4050 }
4051 else if (attr->num_values == 2 &&
4052 strcmp(attr->values[0].string.text, attr->values[1].string.text) != 0 &&
4053 strcmp(attr->values[0].string.text, "none") != 0 &&
4054 strcmp(attr->values[1].string.text, "none") != 0)
4055 {
4056 /*
4057 * Can't put two different security markings on the same document!
4058 */
4059
4060 free(attr->values[1].string.text);
4061 attr->values[1].string.text = strdup(attr->values[0].string.text);
4062 }
4063 }
4064 else if (strcmp(attr->values[0].string.text, Classification) != 0 &&
4065 (attr->num_values == 1 ||
4066 strcmp(attr->values[1].string.text, Classification) != 0))
4067 {
4068 /*
4069 * Force the leading banner to have the classification on it...
4070 */
4071
4072 free(attr->values[0].string.text);
4073 attr->values[0].string.text = strdup(Classification);
4074 }
4075 }
4076
4077 /*
4078 * Add the starting sheet...
4079 */
4080
4081 kbytes = copy_banner(con, job, attr->values[0].string.text);
4082
4083 UpdateQuota(printer, job->username, 0, kbytes);
4084 }
4085
4086 /*
4087 * Add the job file...
4088 */
4089
4090 if (add_file(con, job, filetype))
4091 return;
4092
4093 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4094 job->num_files);
4095 rename(con->filename, filename);
4096 con->filename[0] = '\0';
4097
4098 /*
4099 * See if we need to add the ending sheet...
4100 */
4101
4102 if (!(printer->type & CUPS_PRINTER_REMOTE) && attr->num_values > 1)
4103 {
4104 /*
4105 * Yes...
4106 */
4107
4108 kbytes = copy_banner(con, job, attr->values[1].string.text);
4109 UpdateQuota(printer, job->username, 0, kbytes);
4110 }
4111
4112 /*
4113 * Log and save the job...
4114 */
4115
4116 LogMessage(L_INFO, "Job %d queued on \'%s\' by \'%s\'.", job->id,
4117 job->dest, job->username);
4118 LogMessage(L_DEBUG, "Job %d hold_until = %d", job->id, job->hold_until);
4119
4120 SaveJob(job->id);
4121
4122 /*
4123 * Start the job if possible...
4124 */
4125
4126 CheckJobs();
4127
4128 /*
4129 * Fill in the response info...
4130 */
4131
4132 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4133 ntohs(con->http.hostaddr.sin_port), job->id);
4134 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL, job_uri);
4135
4136 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4137
4138 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4139 job->state->values[0].integer);
4140 add_job_state_reasons(con, job);
4141
4142 con->response->request.status.status_code = IPP_OK;
4143 }
4144
4145
4146 /*
4147 * 'reject_jobs()' - Reject print jobs to a printer.
4148 */
4149
4150 static void
4151 reject_jobs(client_t *con, /* I - Client connection */
4152 ipp_attribute_t *uri) /* I - Printer or class URI */
4153 {
4154 cups_ptype_t dtype; /* Destination type (printer or class) */
4155 char method[HTTP_MAX_URI],
4156 /* Method portion of URI */
4157 username[HTTP_MAX_URI],
4158 /* Username portion of URI */
4159 host[HTTP_MAX_URI],
4160 /* Host portion of URI */
4161 resource[HTTP_MAX_URI];
4162 /* Resource portion of URI */
4163 int port; /* Port portion of URI */
4164 const char *name; /* Printer name */
4165 printer_t *printer; /* Printer data */
4166 ipp_attribute_t *attr; /* printer-state-message text */
4167
4168
4169 LogMessage(L_DEBUG2, "reject_jobs(%p[%d], %s)\n", con, con->http.fd,
4170 uri->values[0].string.text);
4171
4172 /*
4173 * Was this operation called from the correct URI?
4174 */
4175
4176 if (strncmp(con->uri, "/admin/", 7) != 0)
4177 {
4178 LogMessage(L_ERROR, "reject_jobs: admin request on bad resource \'%s\'!",
4179 con->uri);
4180 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4181 return;
4182 }
4183
4184 /*
4185 * Is the destination valid?
4186 */
4187
4188 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4189
4190 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
4191 {
4192 /*
4193 * Bad URI...
4194 */
4195
4196 LogMessage(L_ERROR, "reject_jobs: resource name \'%s\' no good!", resource);
4197 send_ipp_error(con, IPP_NOT_FOUND);
4198 return;
4199 }
4200
4201 /*
4202 * Reject jobs sent to the printer...
4203 */
4204
4205 if (dtype == CUPS_PRINTER_CLASS)
4206 printer = FindClass(name);
4207 else
4208 printer = FindPrinter(name);
4209
4210 printer->accepting = 0;
4211
4212 if ((attr = ippFindAttribute(con->request, "printer-state-message",
4213 IPP_TAG_TEXT)) == NULL)
4214 strcpy(printer->state_message, "Rejecting Jobs");
4215 else
4216 {
4217 strncpy(printer->state_message, attr->values[0].string.text,
4218 sizeof(printer->state_message) - 1);
4219 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
4220 }
4221
4222 if (dtype == CUPS_PRINTER_CLASS)
4223 SaveAllClasses();
4224 else
4225 SaveAllPrinters();
4226
4227 if (dtype == CUPS_PRINTER_CLASS)
4228 LogMessage(L_INFO, "Class \'%s\' rejecting jobs (\'%s\').", name,
4229 con->username);
4230 else
4231 LogMessage(L_INFO, "Printer \'%s\' rejecting jobs (\'%s\').", name,
4232 con->username);
4233
4234 /*
4235 * Everything was ok, so return OK status...
4236 */
4237
4238 con->response->request.status.status_code = IPP_OK;
4239 }
4240
4241
4242 /*
4243 * 'release_job()' - Release a held print job.
4244 */
4245
4246 static void
4247 release_job(client_t *con, /* I - Client connection */
4248 ipp_attribute_t *uri) /* I - Job or Printer URI */
4249 {
4250 ipp_attribute_t *attr; /* Current attribute */
4251 int jobid; /* Job ID */
4252 char method[HTTP_MAX_URI],
4253 /* Method portion of URI */
4254 username[HTTP_MAX_URI],
4255 /* Username portion of URI */
4256 host[HTTP_MAX_URI],
4257 /* Host portion of URI */
4258 resource[HTTP_MAX_URI];
4259 /* Resource portion of URI */
4260 int port; /* Port portion of URI */
4261 job_t *job; /* Job information */
4262
4263
4264 LogMessage(L_DEBUG2, "release_job(%p[%d], %s)\n", con, con->http.fd,
4265 uri->values[0].string.text);
4266
4267 /*
4268 * Verify that the POST operation was done to a valid URI.
4269 */
4270
4271 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4272 strncmp(con->uri, "/jobs/", 5) != 0 &&
4273 strncmp(con->uri, "/printers/", 10) != 0)
4274 {
4275 LogMessage(L_ERROR, "release_job: release request on bad resource \'%s\'!",
4276 con->uri);
4277 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4278 return;
4279 }
4280
4281 /*
4282 * See if we have a job URI or a printer URI...
4283 */
4284
4285 if (strcmp(uri->name, "printer-uri") == 0)
4286 {
4287 /*
4288 * Got a printer URI; see if we also have a job-id attribute...
4289 */
4290
4291 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4292 {
4293 LogMessage(L_ERROR, "release_job: got a printer-uri attribute but no job-id!");
4294 send_ipp_error(con, IPP_BAD_REQUEST);
4295 return;
4296 }
4297
4298 jobid = attr->values[0].integer;
4299 }
4300 else
4301 {
4302 /*
4303 * Got a job URI; parse it to get the job ID...
4304 */
4305
4306 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4307
4308 if (strncmp(resource, "/jobs/", 6) != 0)
4309 {
4310 /*
4311 * Not a valid URI!
4312 */
4313
4314 LogMessage(L_ERROR, "release_job: bad job-uri attribute \'%s\'!",
4315 uri->values[0].string.text);
4316 send_ipp_error(con, IPP_BAD_REQUEST);
4317 return;
4318 }
4319
4320 jobid = atoi(resource + 6);
4321 }
4322
4323 /*
4324 * See if the job exists...
4325 */
4326
4327 if ((job = FindJob(jobid)) == NULL)
4328 {
4329 /*
4330 * Nope - return a "not found" error...
4331 */
4332
4333 LogMessage(L_ERROR, "release_job: job #%d doesn't exist!", jobid);
4334 send_ipp_error(con, IPP_NOT_FOUND);
4335 return;
4336 }
4337
4338 /*
4339 * See if job is "held"...
4340 */
4341
4342 if (job->state->values[0].integer != IPP_JOB_HELD)
4343 {
4344 /*
4345 * Nope - return a "not possible" error...
4346 */
4347
4348 LogMessage(L_ERROR, "release_job: job #%d is not held!", jobid);
4349 send_ipp_error(con, IPP_NOT_POSSIBLE);
4350 return;
4351 }
4352
4353 /*
4354 * See if the job is owned by the requesting user...
4355 */
4356
4357 if (!validate_user(con, job->username, username, sizeof(username)))
4358 {
4359 LogMessage(L_ERROR, "release_job: \"%s\" not authorized to release job id %d owned by \"%s\"!",
4360 username, jobid, job->username);
4361 send_ipp_error(con, IPP_FORBIDDEN);
4362 return;
4363 }
4364
4365 /*
4366 * Reset the job-hold-until value to "no-hold"...
4367 */
4368
4369 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4370 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4371
4372 if (attr != NULL)
4373 {
4374 free(attr->values[0].string.text);
4375 attr->value_tag = IPP_TAG_KEYWORD;
4376 attr->values[0].string.text = strdup("no-hold");
4377 }
4378
4379 /*
4380 * Release the job and return...
4381 */
4382
4383 ReleaseJob(jobid);
4384
4385 LogMessage(L_INFO, "Job %d was released by \'%s\'.", jobid, username);
4386
4387 con->response->request.status.status_code = IPP_OK;
4388 }
4389
4390
4391 /*
4392 * 'restart_job()' - Restart an old print job.
4393 */
4394
4395 static void
4396 restart_job(client_t *con, /* I - Client connection */
4397 ipp_attribute_t *uri) /* I - Job or Printer URI */
4398 {
4399 ipp_attribute_t *attr; /* Current attribute */
4400 int jobid; /* Job ID */
4401 char method[HTTP_MAX_URI],
4402 /* Method portion of URI */
4403 username[HTTP_MAX_URI],
4404 /* Username portion of URI */
4405 host[HTTP_MAX_URI],
4406 /* Host portion of URI */
4407 resource[HTTP_MAX_URI];
4408 /* Resource portion of URI */
4409 int port; /* Port portion of URI */
4410 job_t *job; /* Job information */
4411
4412
4413 LogMessage(L_DEBUG2, "restart_job(%p[%d], %s)\n", con, con->http.fd,
4414 uri->values[0].string.text);
4415
4416 /*
4417 * Verify that the POST operation was done to a valid URI.
4418 */
4419
4420 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4421 strncmp(con->uri, "/jobs/", 5) != 0 &&
4422 strncmp(con->uri, "/printers/", 10) != 0)
4423 {
4424 LogMessage(L_ERROR, "restart_job: restart request on bad resource \'%s\'!",
4425 con->uri);
4426 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4427 return;
4428 }
4429
4430 /*
4431 * See if we have a job URI or a printer URI...
4432 */
4433
4434 if (strcmp(uri->name, "printer-uri") == 0)
4435 {
4436 /*
4437 * Got a printer URI; see if we also have a job-id attribute...
4438 */
4439
4440 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4441 {
4442 LogMessage(L_ERROR, "restart_job: got a printer-uri attribute but no job-id!");
4443 send_ipp_error(con, IPP_BAD_REQUEST);
4444 return;
4445 }
4446
4447 jobid = attr->values[0].integer;
4448 }
4449 else
4450 {
4451 /*
4452 * Got a job URI; parse it to get the job ID...
4453 */
4454
4455 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4456
4457 if (strncmp(resource, "/jobs/", 6) != 0)
4458 {
4459 /*
4460 * Not a valid URI!
4461 */
4462
4463 LogMessage(L_ERROR, "restart_job: bad job-uri attribute \'%s\'!",
4464 uri->values[0].string.text);
4465 send_ipp_error(con, IPP_BAD_REQUEST);
4466 return;
4467 }
4468
4469 jobid = atoi(resource + 6);
4470 }
4471
4472 /*
4473 * See if the job exists...
4474 */
4475
4476 if ((job = FindJob(jobid)) == NULL)
4477 {
4478 /*
4479 * Nope - return a "not found" error...
4480 */
4481
4482 LogMessage(L_ERROR, "restart_job: job #%d doesn't exist!", jobid);
4483 send_ipp_error(con, IPP_NOT_FOUND);
4484 return;
4485 }
4486
4487 /*
4488 * See if job is in any of the "completed" states...
4489 */
4490
4491 if (job->state->values[0].integer <= IPP_JOB_PROCESSING)
4492 {
4493 /*
4494 * Nope - return a "not possible" error...
4495 */
4496
4497 LogMessage(L_ERROR, "restart_job: job #%d is not complete!", jobid);
4498 send_ipp_error(con, IPP_NOT_POSSIBLE);
4499 return;
4500 }
4501
4502 /*
4503 * See if we have retained the job files...
4504 */
4505
4506 if (!JobFiles && job->state->values[0].integer > IPP_JOB_STOPPED)
4507 {
4508 /*
4509 * Nope - return a "not possible" error...
4510 */
4511
4512 LogMessage(L_ERROR, "restart_job: job #%d cannot be restarted - no files!", jobid);
4513 send_ipp_error(con, IPP_NOT_POSSIBLE);
4514 return;
4515 }
4516
4517 /*
4518 * See if the job is owned by the requesting user...
4519 */
4520
4521 if (!validate_user(con, job->username, username, sizeof(username)))
4522 {
4523 LogMessage(L_ERROR, "restart_job: \"%s\" not authorized to restart job id %d owned by \"%s\"!",
4524 username, jobid, job->username);
4525 send_ipp_error(con, IPP_FORBIDDEN);
4526 return;
4527 }
4528
4529 /*
4530 * Restart the job and return...
4531 */
4532
4533 RestartJob(jobid);
4534
4535 LogMessage(L_INFO, "Job %d was restarted by \'%s\'.", jobid, username);
4536
4537 con->response->request.status.status_code = IPP_OK;
4538 }
4539
4540
4541 /*
4542 * 'send_document()' - Send a file to a printer or class.
4543 */
4544
4545 static void
4546 send_document(client_t *con, /* I - Client connection */
4547 ipp_attribute_t *uri) /* I - Printer URI */
4548 {
4549 ipp_attribute_t *attr; /* Current attribute */
4550 ipp_attribute_t *format; /* Document-format attribute */
4551 int jobid; /* Job ID number */
4552 job_t *job; /* Current job */
4553 char job_uri[HTTP_MAX_URI],
4554 /* Job URI */
4555 method[HTTP_MAX_URI],
4556 /* Method portion of URI */
4557 username[HTTP_MAX_URI],
4558 /* Username portion of URI */
4559 host[HTTP_MAX_URI],
4560 /* Host portion of URI */
4561 resource[HTTP_MAX_URI];
4562 /* Resource portion of URI */
4563 int port; /* Port portion of URI */
4564 mime_type_t *filetype; /* Type of file */
4565 char super[MIME_MAX_SUPER],
4566 /* Supertype of file */
4567 type[MIME_MAX_TYPE],
4568 /* Subtype of file */
4569 mimetype[MIME_MAX_SUPER + MIME_MAX_TYPE + 2];
4570 /* Textual name of mime type */
4571 char filename[1024]; /* Job filename */
4572 printer_t *printer; /* Current printer */
4573 struct stat fileinfo; /* File information */
4574 int kbytes; /* Size of file */
4575
4576
4577 LogMessage(L_DEBUG2, "send_document(%p[%d], %s)\n", con, con->http.fd,
4578 uri->values[0].string.text);
4579
4580 /*
4581 * Verify that the POST operation was done to a valid URI.
4582 */
4583
4584 if (strncmp(con->uri, "/classes/", 9) != 0 &&
4585 strncmp(con->uri, "/jobs/", 6) != 0 &&
4586 strncmp(con->uri, "/printers/", 10) != 0)
4587 {
4588 LogMessage(L_ERROR, "send_document: print request on bad resource \'%s\'!",
4589 con->uri);
4590 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4591 return;
4592 }
4593
4594 /*
4595 * See if we have a job URI or a printer URI...
4596 */
4597
4598 if (strcmp(uri->name, "printer-uri") == 0)
4599 {
4600 /*
4601 * Got a printer URI; see if we also have a job-id attribute...
4602 */
4603
4604 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
4605 {
4606 LogMessage(L_ERROR, "send_document: got a printer-uri attribute but no job-id!");
4607 send_ipp_error(con, IPP_BAD_REQUEST);
4608 return;
4609 }
4610
4611 jobid = attr->values[0].integer;
4612 }
4613 else
4614 {
4615 /*
4616 * Got a job URI; parse it to get the job ID...
4617 */
4618
4619 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4620
4621 if (strncmp(resource, "/jobs/", 6) != 0)
4622 {
4623 /*
4624 * Not a valid URI!
4625 */
4626
4627 LogMessage(L_ERROR, "send_document: bad job-uri attribute \'%s\'!",
4628 uri->values[0].string.text);
4629 send_ipp_error(con, IPP_BAD_REQUEST);
4630 return;
4631 }
4632
4633 jobid = atoi(resource + 6);
4634 }
4635
4636 /*
4637 * See if the job exists...
4638 */
4639
4640 if ((job = FindJob(jobid)) == NULL)
4641 {
4642 /*
4643 * Nope - return a "not found" error...
4644 */
4645
4646 LogMessage(L_ERROR, "send_document: job #%d doesn't exist!", jobid);
4647 send_ipp_error(con, IPP_NOT_FOUND);
4648 return;
4649 }
4650
4651 /*
4652 * See if the job is owned by the requesting user...
4653 */
4654
4655 if (!validate_user(con, job->username, username, sizeof(username)))
4656 {
4657 LogMessage(L_ERROR, "send_document: \"%s\" not authorized to send document for job id %d owned by \"%s\"!",
4658 username, jobid, job->username);
4659 send_ipp_error(con, IPP_FORBIDDEN);
4660 return;
4661 }
4662
4663 /*
4664 * OK, see if the client is sending the document compressed - CUPS
4665 * doesn't support compression yet...
4666 */
4667
4668 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
4669 strcmp(attr->values[0].string.text, "none") == 0)
4670 {
4671 LogMessage(L_ERROR, "send_document: Unsupported compression attribute %s!",
4672 attr->values[0].string.text);
4673 send_ipp_error(con, IPP_ATTRIBUTES);
4674 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
4675 "compression", NULL, attr->values[0].string.text);
4676 return;
4677 }
4678
4679 /*
4680 * Do we have a file to print?
4681 */
4682
4683 if (con->filename[0] == '\0')
4684 {
4685 LogMessage(L_ERROR, "send_document: No file!?!");
4686 send_ipp_error(con, IPP_BAD_REQUEST);
4687 return;
4688 }
4689
4690 /*
4691 * Is it a format we support?
4692 */
4693
4694 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
4695 {
4696 /*
4697 * Grab format from client...
4698 */
4699
4700 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
4701 {
4702 LogMessage(L_ERROR, "send_document: could not scan type \'%s\'!",
4703 format->values[0].string.text);
4704 send_ipp_error(con, IPP_BAD_REQUEST);
4705 return;
4706 }
4707 }
4708 else
4709 {
4710 /*
4711 * No document format attribute? Auto-type it!
4712 */
4713
4714 strcpy(super, "application");
4715 strcpy(type, "octet-stream");
4716 }
4717
4718 if (strcmp(super, "application") == 0 &&
4719 strcmp(type, "octet-stream") == 0)
4720 {
4721 /*
4722 * Auto-type the file...
4723 */
4724
4725 LogMessage(L_DEBUG, "send_document: auto-typing file...");
4726
4727 filetype = mimeFileType(MimeDatabase, con->filename);
4728
4729 if (filetype != NULL)
4730 {
4731 /*
4732 * Replace the document-format attribute value with the auto-typed one.
4733 */
4734
4735 snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
4736 filetype->type);
4737
4738 if (format != NULL)
4739 {
4740 free(format->values[0].string.text);
4741 format->values[0].string.text = strdup(mimetype);
4742 }
4743 else
4744 ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
4745 "document-format", NULL, mimetype);
4746 }
4747 else
4748 filetype = mimeType(MimeDatabase, super, type);
4749 }
4750 else
4751 filetype = mimeType(MimeDatabase, super, type);
4752
4753 if (filetype == NULL)
4754 {
4755 LogMessage(L_ERROR, "send_document: Unsupported format \'%s/%s\'!",
4756 super, type);
4757 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
4758
4759 if (format)
4760 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
4761 "document-format", NULL, format->values[0].string.text);
4762
4763 return;
4764 }
4765
4766 LogMessage(L_DEBUG, "send_document: request file type is %s/%s.",
4767 filetype->super, filetype->type);
4768
4769 /*
4770 * Add the file to the job...
4771 */
4772
4773 if (add_file(con, job, filetype))
4774 return;
4775
4776 if (job->dtype & CUPS_PRINTER_CLASS)
4777 printer = FindClass(job->dest);
4778 else
4779 printer = FindPrinter(job->dest);
4780
4781 if (stat(con->filename, &fileinfo))
4782 kbytes = 0;
4783 else
4784 kbytes = (fileinfo.st_size + 1023) / 1024;
4785
4786 UpdateQuota(printer, job->username, 0, kbytes);
4787
4788 if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
4789 attr->values[0].integer += kbytes;
4790
4791 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, job->id,
4792 job->num_files);
4793 rename(con->filename, filename);
4794
4795 con->filename[0] = '\0';
4796
4797 LogMessage(L_INFO, "File of type %s/%s queued in job #%d by \'%s\'.",
4798 filetype->super, filetype->type, job->id, job->username);
4799
4800 /*
4801 * Start the job if this is the last document...
4802 */
4803
4804 if ((attr = ippFindAttribute(con->request, "last-document", IPP_TAG_BOOLEAN)) != NULL &&
4805 attr->values[0].boolean)
4806 {
4807 /*
4808 * See if we need to add the ending sheet...
4809 */
4810
4811 if (printer != NULL && !(printer->type & CUPS_PRINTER_REMOTE) &&
4812 (attr = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL &&
4813 attr->num_values > 1)
4814 {
4815 /*
4816 * Yes...
4817 */
4818
4819 kbytes = copy_banner(con, job, attr->values[1].string.text);
4820 UpdateQuota(printer, job->username, 0, kbytes);
4821 }
4822
4823 if (job->state->values[0].integer == IPP_JOB_STOPPED)
4824 job->state->values[0].integer = IPP_JOB_PENDING;
4825 else if (job->state->values[0].integer == IPP_JOB_HELD)
4826 {
4827 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4828 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4829
4830 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
4831 job->state->values[0].integer = IPP_JOB_PENDING;
4832 }
4833
4834 SaveJob(job->id);
4835 CheckJobs();
4836 }
4837 else
4838 {
4839 if ((attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_KEYWORD)) == NULL)
4840 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
4841
4842 if (attr == NULL || strcmp(attr->values[0].string.text, "no-hold") == 0)
4843 {
4844 job->state->values[0].integer = IPP_JOB_HELD;
4845 job->hold_until = time(NULL) + 60;
4846 SaveJob(job->id);
4847 }
4848 }
4849
4850 /*
4851 * Fill in the response info...
4852 */
4853
4854 snprintf(job_uri, sizeof(job_uri), "http://%s:%d/jobs/%d", ServerName,
4855 ntohs(con->http.hostaddr.sin_port), job->id);
4856 ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
4857 job_uri);
4858
4859 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
4860
4861 ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
4862 job->state->values[0].integer);
4863 add_job_state_reasons(con, job);
4864
4865 con->response->request.status.status_code = IPP_OK;
4866 }
4867
4868
4869 /*
4870 * 'send_ipp_error()' - Send an error status back to the IPP client.
4871 */
4872
4873 static void
4874 send_ipp_error(client_t *con, /* I - Client connection */
4875 ipp_status_t status) /* I - IPP status code */
4876 {
4877 LogMessage(L_DEBUG2, "send_ipp_error(%p[%d], %x)\n", con, con->http.fd,
4878 status);
4879
4880 LogMessage(L_DEBUG, "Sending error: %s", ippErrorString(status));
4881
4882 con->response->request.status.status_code = status;
4883
4884 if (ippFindAttribute(con->response, "attributes-charset", IPP_TAG_ZERO) == NULL)
4885 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
4886 "attributes-charset", NULL, DefaultCharset);
4887
4888 if (ippFindAttribute(con->response, "attributes-natural-language",
4889 IPP_TAG_ZERO) == NULL)
4890 ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
4891 "attributes-natural-language", NULL, DefaultLanguage);
4892 }
4893
4894
4895 /*
4896 * 'set_default()' - Set the default destination...
4897 */
4898
4899 static void
4900 set_default(client_t *con, /* I - Client connection */
4901 ipp_attribute_t *uri) /* I - Printer URI */
4902 {
4903 cups_ptype_t dtype; /* Destination type (printer or class) */
4904 char method[HTTP_MAX_URI],
4905 /* Method portion of URI */
4906 username[HTTP_MAX_URI],
4907 /* Username portion of URI */
4908 host[HTTP_MAX_URI],
4909 /* Host portion of URI */
4910 resource[HTTP_MAX_URI];
4911 /* Resource portion of URI */
4912 int port; /* Port portion of URI */
4913 const char *name; /* Printer name */
4914
4915
4916 LogMessage(L_DEBUG2, "set_default(%p[%d], %s)\n", con, con->http.fd,
4917 uri->values[0].string.text);
4918
4919 /*
4920 * Was this operation called from the correct URI?
4921 */
4922
4923 if (strncmp(con->uri, "/admin/", 7) != 0)
4924 {
4925 LogMessage(L_ERROR, "set_default: admin request on bad resource \'%s\'!",
4926 con->uri);
4927 send_ipp_error(con, IPP_NOT_AUTHORIZED);
4928 return;
4929 }
4930
4931 /*
4932 * Is the destination valid?
4933 */
4934
4935 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
4936
4937 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
4938 {
4939 /*
4940 * Bad URI...
4941 */
4942
4943 LogMessage(L_ERROR, "set_default: resource name \'%s\' no good!", resource);
4944 send_ipp_error(con, IPP_NOT_FOUND);
4945 return;
4946 }
4947
4948 /*
4949 * Set it as the default...
4950 */
4951
4952 if (dtype == CUPS_PRINTER_CLASS)
4953 DefaultPrinter = FindClass(name);
4954 else
4955 DefaultPrinter = FindPrinter(name);
4956
4957 SaveAllPrinters();
4958 SaveAllClasses();
4959
4960 LogMessage(L_INFO, "Default destination set to \'%s\' by \'%s\'.", name,
4961 con->username);
4962
4963 /*
4964 * Everything was ok, so return OK status...
4965 */
4966
4967 con->response->request.status.status_code = IPP_OK;
4968 }
4969
4970
4971 /*
4972 * 'set_job_attrs()' - Set job attributes.
4973 */
4974
4975 static void
4976 set_job_attrs(client_t *con, /* I - Client connection */
4977 ipp_attribute_t *uri) /* I - Job URI */
4978 {
4979 ipp_attribute_t *attr, /* Current attribute */
4980 *attr2, /* Job attribute */
4981 *prev2; /* Previous job attribute */
4982 int jobid; /* Job ID */
4983 job_t *job; /* Current job */
4984 char method[HTTP_MAX_URI],
4985 /* Method portion of URI */
4986 username[HTTP_MAX_URI],
4987 /* Username portion of URI */
4988 host[HTTP_MAX_URI],
4989 /* Host portion of URI */
4990 resource[HTTP_MAX_URI];
4991 /* Resource portion of URI */
4992 int port; /* Port portion of URI */
4993
4994
4995 LogMessage(L_DEBUG2, "set_job_attrs(%p[%d], %s)\n", con, con->http.fd,
4996 uri->values[0].string.text);
4997
4998 /*
4999 * See if we have a job URI or a printer URI...
5000 */
5001
5002 if (strcmp(uri->name, "printer-uri") == 0)
5003 {
5004 /*
5005 * Got a printer URI; see if we also have a job-id attribute...
5006 */
5007
5008 if ((attr = ippFindAttribute(con->request, "job-id", IPP_TAG_INTEGER)) == NULL)
5009 {
5010 LogMessage(L_ERROR, "set_job_attrs: got a printer-uri attribute but no job-id!");
5011 send_ipp_error(con, IPP_BAD_REQUEST);
5012 return;
5013 }
5014
5015 jobid = attr->values[0].integer;
5016 }
5017 else
5018 {
5019 /*
5020 * Got a job URI; parse it to get the job ID...
5021 */
5022
5023 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5024
5025 if (strncmp(resource, "/jobs/", 6) != 0)
5026 {
5027 /*
5028 * Not a valid URI!
5029 */
5030
5031 LogMessage(L_ERROR, "set_job_attrs: bad job-uri attribute \'%s\'!\n",
5032 uri->values[0].string.text);
5033 send_ipp_error(con, IPP_BAD_REQUEST);
5034 return;
5035 }
5036
5037 jobid = atoi(resource + 6);
5038 }
5039
5040 /*
5041 * See if the job exists...
5042 */
5043
5044 if ((job = FindJob(jobid)) == NULL)
5045 {
5046 /*
5047 * Nope - return a "not found" error...
5048 */
5049
5050 LogMessage(L_ERROR, "set_job_attrs: job #%d doesn't exist!", jobid);
5051 send_ipp_error(con, IPP_NOT_FOUND);
5052 return;
5053 }
5054
5055 /*
5056 * See if the job has been completed...
5057 */
5058
5059 if (job->state->values[0].integer > IPP_JOB_STOPPED)
5060 {
5061 /*
5062 * Return a "not-possible" error...
5063 */
5064
5065 LogMessage(L_ERROR, "set_job_attrs: job #%d is finished and cannot be altered!", jobid);
5066 send_ipp_error(con, IPP_NOT_POSSIBLE);
5067 return;
5068 }
5069
5070 /*
5071 * See if the job is owned by the requesting user...
5072 */
5073
5074 if (!validate_user(con, job->username, username, sizeof(username)))
5075 {
5076 LogMessage(L_ERROR, "set_job_attrs: \"%s\" not authorized to alter job id %d owned by \"%s\"!",
5077 username, jobid, job->username);
5078 send_ipp_error(con, IPP_FORBIDDEN);
5079 return;
5080 }
5081
5082 /*
5083 * See what the user wants to change.
5084 */
5085
5086 for (attr = con->request->attrs; attr != NULL; attr = attr->next)
5087 {
5088 if (attr->group_tag != IPP_TAG_JOB || !attr->name)
5089 continue;
5090
5091 if (strcmp(attr->name, "job-originating-host-name") == 0 ||
5092 strcmp(attr->name, "job-originating-user-name") == 0 ||
5093 strcmp(attr->name, "job-media-sheets-completed") == 0 ||
5094 strcmp(attr->name, "job-k-octets") == 0 ||
5095 strcmp(attr->name, "job-id") == 0 ||
5096 strcmp(attr->name, "job-sheets") == 0 ||
5097 strncmp(attr->name, "time-at-", 8) == 0)
5098 continue; /* Read-only attrs */
5099
5100 if (strcmp(attr->name, "job-priority") == 0 &&
5101 attr->value_tag == IPP_TAG_INTEGER &&
5102 job->state->values[0].integer != IPP_JOB_PROCESSING)
5103 {
5104 /*
5105 * Change the job priority
5106 */
5107
5108 SetJobPriority(jobid, attr->values[0].integer);
5109 }
5110 else if ((attr2 = ippFindAttribute(job->attrs, attr->name, IPP_TAG_ZERO)) != NULL)
5111 {
5112 /*
5113 * Some other value; first free the old value...
5114 */
5115
5116 for (prev2 = job->attrs->attrs; prev2 != NULL; prev2 = prev2->next)
5117 if (prev2->next == attr2)
5118 break;
5119
5120 if (prev2)
5121 prev2->next = attr2->next;
5122 else
5123 job->attrs->attrs = attr2->next;
5124
5125 _ipp_free_attr(attr2);
5126
5127 /*
5128 * Then copy the attribute...
5129 */
5130
5131 copy_attribute(job->attrs, attr, 0);
5132
5133 /*
5134 * See if the job-name or job-hold-until is being changed.
5135 */
5136
5137 if (strcmp(attr->name, "job-name") == 0)
5138 strncpy(job->title, attr->values[0].string.text, sizeof(job->title) - 1);
5139 else if (strcmp(attr->name, "job-hold-until") == 0)
5140 {
5141 SetJobHoldUntil(job->id, attr->values[0].string.text);
5142
5143 if (strcmp(attr->values[0].string.text, "no-hold") == 0)
5144 ReleaseJob(job->id);
5145 else
5146 HoldJob(job->id);
5147 }
5148 }
5149 else if (attr->value_tag == IPP_TAG_DELETEATTR)
5150 {
5151 /*
5152 * Delete the attribute...
5153 */
5154
5155 for (attr2 = job->attrs->attrs, prev2 = NULL;
5156 attr2 != NULL;
5157 prev2 = attr2, attr2 = attr2->next)
5158 if (attr2->name && strcmp(attr2->name, attr->name) == 0)
5159 break;
5160
5161 if (attr2)
5162 {
5163 if (prev2)
5164 prev2->next = attr2->next;
5165 else
5166 job->attrs->attrs = attr2->next;
5167
5168 _ipp_free_attr(attr2);
5169 }
5170 }
5171 else
5172 {
5173 /*
5174 * Add new option by copying it...
5175 */
5176
5177 copy_attribute(job->attrs, attr, 0);
5178 }
5179 }
5180
5181 /*
5182 * Save the job...
5183 */
5184
5185 SaveJob(job->id);
5186
5187 /*
5188 * Start jobs if possible...
5189 */
5190
5191 CheckJobs();
5192
5193 /*
5194 * Return with "everything is OK" status...
5195 */
5196
5197 con->response->request.status.status_code = IPP_OK;
5198 }
5199
5200
5201 /*
5202 * 'start_printer()' - Start a printer.
5203 */
5204
5205 static void
5206 start_printer(client_t *con, /* I - Client connection */
5207 ipp_attribute_t *uri) /* I - Printer URI */
5208 {
5209 cups_ptype_t dtype; /* Destination type (printer or class) */
5210 char method[HTTP_MAX_URI],
5211 /* Method portion of URI */
5212 username[HTTP_MAX_URI],
5213 /* Username portion of URI */
5214 host[HTTP_MAX_URI],
5215 /* Host portion of URI */
5216 resource[HTTP_MAX_URI];
5217 /* Resource portion of URI */
5218 int port; /* Port portion of URI */
5219 const char *name; /* Printer name */
5220 printer_t *printer; /* Printer data */
5221
5222
5223 LogMessage(L_DEBUG2, "start_printer(%p[%d], %s)\n", con, con->http.fd,
5224 uri->values[0].string.text);
5225
5226 /*
5227 * Was this operation called from the correct URI?
5228 */
5229
5230 if (strncmp(con->uri, "/admin/", 7) != 0)
5231 {
5232 LogMessage(L_ERROR, "start_printer: admin request on bad resource \'%s\'!",
5233 con->uri);
5234 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5235 return;
5236 }
5237
5238 /*
5239 * Is the destination valid?
5240 */
5241
5242 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5243
5244 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
5245 {
5246 /*
5247 * Bad URI...
5248 */
5249
5250 LogMessage(L_ERROR, "start_printer: resource name \'%s\' no good!", resource);
5251 send_ipp_error(con, IPP_NOT_FOUND);
5252 return;
5253 }
5254
5255 /*
5256 * Start the printer...
5257 */
5258
5259 if (dtype == CUPS_PRINTER_CLASS)
5260 printer = FindClass(name);
5261 else
5262 printer = FindPrinter(name);
5263
5264 StartPrinter(printer);
5265
5266 if (dtype == CUPS_PRINTER_CLASS)
5267 SaveAllClasses();
5268 else
5269 SaveAllPrinters();
5270
5271 if (dtype == CUPS_PRINTER_CLASS)
5272 LogMessage(L_INFO, "Class \'%s\' started by \'%s\'.", name,
5273 con->username);
5274 else
5275 LogMessage(L_INFO, "Printer \'%s\' started by \'%s\'.", name,
5276 con->username);
5277
5278 printer->state_message[0] = '\0';
5279
5280 CheckJobs();
5281
5282 /*
5283 * Everything was ok, so return OK status...
5284 */
5285
5286 con->response->request.status.status_code = IPP_OK;
5287 }
5288
5289
5290 /*
5291 * 'stop_printer()' - Stop a printer.
5292 */
5293
5294 static void
5295 stop_printer(client_t *con, /* I - Client connection */
5296 ipp_attribute_t *uri) /* I - Printer URI */
5297 {
5298 cups_ptype_t dtype; /* Destination type (printer or class) */
5299 char method[HTTP_MAX_URI],
5300 /* Method portion of URI */
5301 username[HTTP_MAX_URI],
5302 /* Username portion of URI */
5303 host[HTTP_MAX_URI],
5304 /* Host portion of URI */
5305 resource[HTTP_MAX_URI];
5306 /* Resource portion of URI */
5307 int port; /* Port portion of URI */
5308 const char *name; /* Printer name */
5309 printer_t *printer; /* Printer data */
5310 ipp_attribute_t *attr; /* printer-state-message attribute */
5311
5312
5313 LogMessage(L_DEBUG2, "stop_printer(%p[%d], %s)\n", con, con->http.fd,
5314 uri->values[0].string.text);
5315
5316 /*
5317 * Was this operation called from the correct URI?
5318 */
5319
5320 if (strncmp(con->uri, "/admin/", 7) != 0)
5321 {
5322 LogMessage(L_ERROR, "stop_printer: admin request on bad resource \'%s\'!",
5323 con->uri);
5324 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5325 return;
5326 }
5327
5328 /*
5329 * Is the destination valid?
5330 */
5331
5332 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5333
5334 if ((name = ValidateDest(host, resource, &dtype)) == NULL)
5335 {
5336 /*
5337 * Bad URI...
5338 */
5339
5340 LogMessage(L_ERROR, "stop_printer: resource name \'%s\' no good!", resource);
5341 send_ipp_error(con, IPP_NOT_FOUND);
5342 return;
5343 }
5344
5345 /*
5346 * Stop the printer...
5347 */
5348
5349 if (dtype == CUPS_PRINTER_CLASS)
5350 printer = FindClass(name);
5351 else
5352 printer = FindPrinter(name);
5353
5354 StopPrinter(printer);
5355
5356 if (dtype == CUPS_PRINTER_CLASS)
5357 SaveAllClasses();
5358 else
5359 SaveAllPrinters();
5360
5361 if ((attr = ippFindAttribute(con->request, "printer-state-message",
5362 IPP_TAG_TEXT)) == NULL)
5363 strcpy(printer->state_message, "Paused");
5364 else
5365 {
5366 strncpy(printer->state_message, attr->values[0].string.text,
5367 sizeof(printer->state_message) - 1);
5368 printer->state_message[sizeof(printer->state_message) - 1] = '\0';
5369 }
5370
5371 if (dtype == CUPS_PRINTER_CLASS)
5372 LogMessage(L_INFO, "Class \'%s\' stopped by \'%s\'.", name,
5373 con->username);
5374 else
5375 LogMessage(L_INFO, "Printer \'%s\' stopped by \'%s\'.", name,
5376 con->username);
5377
5378 /*
5379 * Everything was ok, so return OK status...
5380 */
5381
5382 con->response->request.status.status_code = IPP_OK;
5383 }
5384
5385
5386 /*
5387 * 'validate_job()' - Validate printer options and destination.
5388 */
5389
5390 static void
5391 validate_job(client_t *con, /* I - Client connection */
5392 ipp_attribute_t *uri) /* I - Printer URI */
5393 {
5394 ipp_attribute_t *attr; /* Current attribute */
5395 ipp_attribute_t *format; /* Document-format attribute */
5396 cups_ptype_t dtype; /* Destination type (printer or class) */
5397 char method[HTTP_MAX_URI],
5398 /* Method portion of URI */
5399 username[HTTP_MAX_URI],
5400 /* Username portion of URI */
5401 host[HTTP_MAX_URI],
5402 /* Host portion of URI */
5403 resource[HTTP_MAX_URI];
5404 /* Resource portion of URI */
5405 int port; /* Port portion of URI */
5406 char super[MIME_MAX_SUPER],
5407 /* Supertype of file */
5408 type[MIME_MAX_TYPE];
5409 /* Subtype of file */
5410
5411
5412 LogMessage(L_DEBUG2, "validate_job(%p[%d], %s)\n", con, con->http.fd,
5413 uri->values[0].string.text);
5414
5415 /*
5416 * Verify that the POST operation was done to a valid URI.
5417 */
5418
5419 if (strncmp(con->uri, "/classes/", 9) != 0 &&
5420 strncmp(con->uri, "/printers/", 10) != 0)
5421 {
5422 LogMessage(L_ERROR, "validate_job: request on bad resource \'%s\'!",
5423 con->uri);
5424 send_ipp_error(con, IPP_NOT_AUTHORIZED);
5425 return;
5426 }
5427
5428 /*
5429 * OK, see if the client is sending the document compressed - CUPS
5430 * doesn't support compression yet...
5431 */
5432
5433 if ((attr = ippFindAttribute(con->request, "compression", IPP_TAG_KEYWORD)) != NULL &&
5434 strcmp(attr->values[0].string.text, "none") == 0)
5435 {
5436 LogMessage(L_ERROR, "validate_job: Unsupported compression attribute %s!",
5437 attr->values[0].string.text);
5438 send_ipp_error(con, IPP_ATTRIBUTES);
5439 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
5440 "compression", NULL, attr->values[0].string.text);
5441 return;
5442 }
5443
5444 /*
5445 * Is it a format we support?
5446 */
5447
5448 if ((format = ippFindAttribute(con->request, "document-format", IPP_TAG_MIMETYPE)) != NULL)
5449 {
5450 if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
5451 {
5452 LogMessage(L_ERROR, "validate_job: could not scan type \'%s\'!\n",
5453 format->values[0].string.text);
5454 send_ipp_error(con, IPP_BAD_REQUEST);
5455 return;
5456 }
5457
5458 if ((strcmp(super, "application") != 0 ||
5459 strcmp(type, "octet-stream") != 0) &&
5460 mimeType(MimeDatabase, super, type) == NULL)
5461 {
5462 LogMessage(L_ERROR, "validate_job: Unsupported format \'%s\'!\n",
5463 format->values[0].string.text);
5464 send_ipp_error(con, IPP_DOCUMENT_FORMAT);
5465 ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
5466 "document-format", NULL, format->values[0].string.text);
5467 return;
5468 }
5469 }
5470
5471 /*
5472 * Is the destination valid?
5473 */
5474
5475 httpSeparate(uri->values[0].string.text, method, username, host, &port, resource);
5476
5477 if (ValidateDest(host, resource, &dtype) == NULL)
5478 {
5479 /*
5480 * Bad URI...
5481 */
5482
5483 LogMessage(L_ERROR, "validate_job: resource name \'%s\' no good!", resource);
5484 send_ipp_error(con, IPP_NOT_FOUND);
5485 return;
5486 }
5487
5488 /*
5489 * Everything was ok, so return OK status...
5490 */
5491
5492 con->response->request.status.status_code = IPP_OK;
5493 }
5494
5495
5496 /*
5497 * 'validate_user()' - Validate the user for the request.
5498 */
5499
5500 static int /* O - 1 if permitted, 0 otherwise */
5501 validate_user(client_t *con, /* I - Client connection */
5502 const char *owner, /* I - Owner of job/resource */
5503 char *username, /* O - Authenticated username */
5504 int userlen) /* I - Length of username */
5505 {
5506 int i, j; /* Looping vars */
5507 ipp_attribute_t *attr; /* requesting-user-name attribute */
5508 struct passwd *user; /* User info */
5509 struct group *group; /* System group info */
5510
5511
5512 LogMessage(L_DEBUG2, "validate_user(%p[%d], \"%s\", %p, %d)\n",
5513 con, con->http.fd, owner, username, userlen);
5514
5515 /*
5516 * Validate input...
5517 */
5518
5519 if (con == NULL || owner == NULL || username == NULL || userlen <= 0)
5520 return (0);
5521
5522 /*
5523 * Get the best authenticated username that is available.
5524 */
5525
5526 if (con->username[0])
5527 strncpy(username, con->username, userlen - 1);
5528 else if ((attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME)) != NULL)
5529 strncpy(username, attr->values[0].string.text, userlen - 1);
5530 else
5531 strncpy(username, "anonymous", userlen - 1);
5532
5533 username[userlen - 1] = '\0';
5534
5535 /*
5536 * Check the username against the owner...
5537 */
5538
5539 if (strcasecmp(username, owner) != 0 && strcasecmp(username, "root") != 0)
5540 {
5541 /*
5542 * Not the owner or root; check to see if the user is a member of the
5543 * system group...
5544 */
5545
5546 user = getpwnam(username);
5547 endpwent();
5548
5549 for (i = 0, j = 0, group = NULL; i < NumSystemGroups; i ++)
5550 {
5551 group = getgrnam(SystemGroups[i]);
5552 endgrent();
5553
5554 if (group != NULL)
5555 {
5556 for (j = 0; group->gr_mem[j]; j ++)
5557 if (strcasecmp(username, group->gr_mem[j]) == 0)
5558 break;
5559
5560 if (group->gr_mem[j])
5561 break;
5562 }
5563 else
5564 j = 0;
5565 }
5566
5567 if (user == NULL || group == NULL ||
5568 (group->gr_mem[j] == NULL && group->gr_gid != user->pw_gid))
5569 {
5570 /*
5571 * Username not found, group not found, or user is not part of the
5572 * system group...
5573 */
5574
5575 return (0);
5576 }
5577 }
5578
5579 return (1);
5580 }
5581
5582
5583 /*
5584 * End of "$Id: ipp.c,v 1.146 2001/10/22 21:04:08 mike Exp $".
5585 */