]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/client.c
Updated job data to use IPP attributes - simplifies processing.
[thirdparty/cups.git] / scheduler / client.c
1 /*
2 * "$Id: client.c,v 1.5 1999/02/26 15:11:11 mike Exp $"
3 *
4 * Client routines for the Common UNIX Printing System (CUPS) scheduler.
5 *
6 * Copyright 1997-1999 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 * 44145 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 * AcceptClient() - Accept a new client.
27 * CloseAllClients() - Close all remote clients immediately.
28 * CloseClient() - Close a remote client.
29 * ReadClient() - Read data from a client.
30 * SendCommand() - Send output from a command via HTTP.
31 * SendError() - Send an error message via HTTP.
32 * SendFile() - Send a file via HTTP.
33 * SendHeader() - Send an HTTP request.
34 * StartListening() - Create all listening sockets...
35 * StopListening() - Close all listening sockets...
36 * WriteClient() - Write data to a client as needed.
37 * check_if_modified() - Decode an "If-Modified-Since" line.
38 * decode_basic_auth() - Decode a Basic authorization string.
39 * get_file() - Get a filename and state info.
40 * pipe_command() - Pipe the output of a command to the remote client.
41 * process_ipp_request() - Process an incoming IPP request...
42 * send_ipp_error() - Send an error status back to the IPP client.
43 * sigpipe_handler() - Handle 'broken pipe' signals from lost network
44 * clients.
45 */
46
47 /*
48 * Include necessary headers...
49 */
50
51 #include "cupsd.h"
52 #include <cups/debug.h>
53
54
55 /*
56 * Local functions...
57 */
58
59 static int check_if_modified(client_t *con, struct stat *filestats);
60 static void decode_basic_auth(client_t *con);
61 static char *get_file(client_t *con, struct stat *filestats);
62 static int pipe_command(client_t *con, int infile, int *outfile, char *command, char *options);
63 static void process_ipp_request(client_t *con);
64 static void send_ipp_error(client_t *con, ipp_status_t status);
65 static void sigpipe_handler(int sig);
66
67 /**** OBSOLETE ****/
68 static char *get_extension(char *filename);
69 static char *get_type(char *extension);
70
71
72 /*
73 * 'AcceptClient()' - Accept a new client.
74 */
75
76 void
77 AcceptClient(listener_t *lis) /* I - Listener socket */
78 {
79 int i; /* Looping var */
80 int val; /* Parameter value */
81 client_t *con; /* New client pointer */
82 unsigned address;/* Address of client */
83 struct hostent *host; /* Host entry for address */
84
85
86 /*
87 * Get a pointer to the next available client...
88 */
89
90 con = Clients + NumClients;
91
92 memset(con, 0, sizeof(client_t));
93 con->http.activity = time(NULL);
94
95 /*
96 * Accept the client and get the remote address...
97 */
98
99 val = sizeof(struct sockaddr_in);
100
101 if ((con->http.fd = accept(lis->fd, (struct sockaddr *)&(con->http.hostaddr), &val)) < 0)
102 {
103 LogMessage(LOG_ERROR, "accept() failed - %s.", strerror(errno));
104 return;
105 }
106
107 /*
108 * Get the hostname or format the IP address as needed...
109 */
110
111 address = ntohl(con->http.hostaddr.sin_addr.s_addr);
112
113 if (HostNameLookups)
114 host = gethostbyaddr(&address, sizeof(address), AF_INET);
115 else
116 host = NULL;
117
118 if (host == NULL)
119 sprintf(con->http.hostname, "%d.%d.%d.%d", (address >> 24) & 255,
120 (address >> 16) & 255, (address >> 8) & 255, address & 255);
121 else
122 strncpy(con->http.hostname, host->h_name, sizeof(con->http.hostname) - 1);
123
124 LogMessage(LOG_INFO, "accept() %d from %s.", con->http.fd, con->http.hostname);
125
126 /*
127 * Add the socket to the select() input mask.
128 */
129
130 fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
131
132 FD_SET(con->http.fd, &InputSet);
133
134 NumClients ++;
135
136 /*
137 * Temporarily suspend accept()'s until we lose a client...
138 */
139
140 if (NumClients == MAX_CLIENTS)
141 for (i = 0; i < NumListeners; i ++)
142 FD_CLR(Listeners[i].fd, &InputSet);
143 }
144
145
146 /*
147 * 'CloseAllClients()' - Close all remote clients immediately.
148 */
149
150 void
151 CloseAllClients(void)
152 {
153 while (NumClients > 0)
154 CloseClient(Clients);
155 }
156
157
158 /*
159 * 'CloseClient()' - Close a remote client.
160 */
161
162 void
163 CloseClient(client_t *con) /* I - Client to close */
164 {
165 int i; /* Looping var */
166 int status; /* Exit status of pipe command */
167
168
169 LogMessage(LOG_INFO, "CloseClient() %d", con->http.fd);
170
171 /*
172 * Close the socket and clear the file from the input set for select()...
173 */
174
175 #if defined(WIN32) || defined(__EMX__)
176 /*
177 * Microsoft Windows and IBM OS/2 don't have a unified IO system...
178 */
179
180 closesocket(con->http.fd);
181 #else
182 /*
183 * UNIX *does*....
184 */
185
186 close(con->http.fd);
187 #endif /* WIN32 || __EMX__ */
188
189 for (i = 0; i < NumListeners; i ++)
190 FD_SET(Listeners[i].fd, &InputSet);
191
192 FD_CLR(con->http.fd, &InputSet);
193 if (con->pipe_pid != 0)
194 FD_CLR(con->file, &InputSet);
195 FD_CLR(con->http.fd, &OutputSet);
196
197 /*
198 * If we have a data file open, close it...
199 */
200
201 if (con->file > 0)
202 {
203 if (con->pipe_pid)
204 {
205 kill(con->pipe_pid, SIGKILL);
206 waitpid(con->pipe_pid, &status, WNOHANG);
207 }
208
209 close(con->file);
210 }
211
212 /*
213 * Compact the list of clients as necessary...
214 */
215
216 NumClients --;
217
218 if (con < (Clients + NumClients))
219 memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
220 }
221
222
223 /*
224 * 'ReadClient()' - Read data from a client.
225 */
226
227 int /* O - 1 on success, 0 on error */
228 ReadClient(client_t *con) /* I - Client to read from */
229 {
230 char line[8192], /* Line from client... */
231 operation[64], /* Operation code from socket */
232 version[64]; /* HTTP version number string */
233 int major, minor; /* HTTP version numbers */
234 http_status_t status; /* Transfer status */
235 int bytes; /* Number of bytes to POST */
236 char *filename, /* Name of file for GET/HEAD */
237 *extension, /* Extension of file */
238 *type; /* MIME type */
239 struct stat filestats; /* File information */
240 char command[1024], /* Command to run */
241 *options; /* Options/CGI data */
242
243
244 status = HTTP_CONTINUE;
245
246 switch (con->http.state)
247 {
248 case HTTP_WAITING :
249 /*
250 * See if we've received a request line...
251 */
252
253 if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
254 {
255 CloseClient(con);
256 return (0);
257 }
258
259 /*
260 * Ignore blank request lines...
261 */
262
263 if (line[0] == '\0')
264 break;
265
266 /*
267 * Clear other state variables...
268 */
269
270 httpClearFields(HTTP(con));
271
272 con->http.activity = time(NULL);
273 con->http.version = HTTP_1_0;
274 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
275 con->http.data_encoding = HTTP_ENCODE_LENGTH;
276 con->http.data_remaining = 0;
277 con->operation = HTTP_WAITING;
278 con->bytes = 0;
279 con->file = 0;
280 con->pipe_pid = 0;
281 con->username[0] = '\0';
282 con->password[0] = '\0';
283 con->uri[0] = '\0';
284
285 if (con->language != NULL)
286 {
287 cupsLangFree(con->language);
288 con->language = NULL;
289 }
290
291 /*
292 * Grab the request line...
293 */
294
295 switch (sscanf(line, "%63s%1023s%s", operation, con->uri, version))
296 {
297 case 1 :
298 SendError(con, HTTP_BAD_REQUEST);
299 CloseClient(con);
300 return (0);
301 case 2 :
302 con->http.version = HTTP_0_9;
303 break;
304 case 3 :
305 if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
306 {
307 SendError(con, HTTP_BAD_REQUEST);
308 CloseClient(con);
309 return (0);
310 }
311
312 if (major < 2)
313 {
314 con->http.version = (http_version_t)(major * 100 + minor);
315 if (con->http.version == HTTP_1_1)
316 con->http.keep_alive = HTTP_KEEPALIVE_ON;
317 else
318 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
319 }
320 else
321 {
322 SendError(con, HTTP_NOT_SUPPORTED);
323 CloseClient(con);
324 return (0);
325 }
326 break;
327 }
328
329 /*
330 * Process the request...
331 */
332
333 if (strcmp(operation, "GET") == 0)
334 con->http.state = HTTP_GET;
335 else if (strcmp(operation, "PUT") == 0)
336 con->http.state = HTTP_PUT;
337 else if (strcmp(operation, "POST") == 0)
338 con->http.state = HTTP_POST;
339 else if (strcmp(operation, "DELETE") == 0)
340 con->http.state = HTTP_DELETE;
341 else if (strcmp(operation, "TRACE") == 0)
342 con->http.state = HTTP_TRACE;
343 else if (strcmp(operation, "CLOSE") == 0)
344 con->http.state = HTTP_CLOSE;
345 else if (strcmp(operation, "OPTIONS") == 0)
346 con->http.state = HTTP_OPTIONS;
347 else if (strcmp(operation, "HEAD") == 0)
348 con->http.state = HTTP_HEAD;
349 else
350 {
351 SendError(con, HTTP_BAD_REQUEST);
352 CloseClient(con);
353 return (0);
354 }
355
356 con->start = time(NULL);
357 con->operation = con->http.state;
358
359 LogMessage(LOG_INFO, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
360 operation, con->uri,
361 con->http.version / 100, con->http.version % 100);
362
363 con->http.status = HTTP_OK;
364 break;
365
366 case HTTP_CLOSE :
367 case HTTP_DELETE :
368 case HTTP_GET :
369 case HTTP_HEAD :
370 case HTTP_POST :
371 case HTTP_PUT :
372 case HTTP_TRACE :
373 /*
374 * Parse incoming parameters until the status changes...
375 */
376
377 status = httpUpdate(HTTP(con));
378
379 if (status != HTTP_OK && status != HTTP_CONTINUE)
380 {
381 SendError(con, HTTP_BAD_REQUEST);
382 CloseClient(con);
383 return (0);
384 }
385 break;
386 }
387
388 /*
389 * Handle new transfers...
390 */
391
392 if (status == HTTP_OK)
393 {
394 con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
395
396 decode_basic_auth(con);
397
398 if (con->http.fields[HTTP_FIELD_HOST][0] == '\0' &&
399 con->http.version >= HTTP_1_0)
400 {
401 if (!SendError(con, HTTP_BAD_REQUEST))
402 {
403 CloseClient(con);
404 return (0);
405 }
406 }
407 else if (strncmp(con->uri, "..", 2) == 0)
408 {
409 /*
410 * Protect against malicious users!
411 */
412
413 if (!SendError(con, HTTP_FORBIDDEN))
414 {
415 CloseClient(con);
416 return (0);
417 }
418 }
419 else if (con->uri[0] != '/')
420 {
421 /*
422 * Don't allow proxying (yet)...
423 */
424
425 if (!SendError(con, HTTP_METHOD_NOT_ALLOWED))
426 {
427 CloseClient(con);
428 return (0);
429 }
430 }
431 else if ((status = IsAuthorized(con)) != HTTP_OK)
432 {
433 if (!SendError(con, status))
434 {
435 CloseClient(con);
436 return (0);
437 }
438 }
439 else switch (con->http.state)
440 {
441 case HTTP_GET_SEND :
442 if (strncmp(con->uri, "/printers", 9) == 0 ||
443 strncmp(con->uri, "/classes", 8) == 0 ||
444 strncmp(con->uri, "/jobs", 5) == 0)
445 {
446 /*
447 * Send CGI output...
448 */
449
450 if (strncmp(con->uri, "/printers", 9) == 0)
451 {
452 sprintf(command, "%s/cgi-bin/printers", ServerRoot);
453 options = con->uri + 9;
454 }
455 else if (strncmp(con->uri, "/classes", 8) == 0)
456 {
457 sprintf(command, "%s/cgi-bin/classes", ServerRoot);
458 options = con->uri + 8;
459 }
460 else
461 {
462 sprintf(command, "%s/cgi-bin/jobs", ServerRoot);
463 options = con->uri + 5;
464 }
465
466 if (*options == '/')
467 options ++;
468
469 if (!SendCommand(con, command, options))
470 {
471 if (!SendError(con, HTTP_NOT_FOUND))
472 {
473 CloseClient(con);
474 return (0);
475 }
476 }
477 else
478 LogRequest(con, HTTP_OK);
479
480 if (con->http.version <= HTTP_1_0)
481 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
482 }
483 else
484 {
485 /*
486 * Serve a file...
487 */
488
489 if ((filename = get_file(con, &filestats)) == NULL)
490 {
491 if (!SendError(con, HTTP_NOT_FOUND))
492 {
493 CloseClient(con);
494 return (0);
495 }
496 }
497 else if (!check_if_modified(con, &filestats))
498 {
499 if (!SendError(con, HTTP_NOT_MODIFIED))
500 {
501 CloseClient(con);
502 return (0);
503 }
504 }
505 else
506 {
507 extension = get_extension(filename);
508 type = get_type(extension);
509
510 if (!SendFile(con, HTTP_OK, filename, type, &filestats))
511 {
512 CloseClient(con);
513 return (0);
514 }
515 }
516 }
517 break;
518
519 case HTTP_POST_RECV :
520 /*
521 * See what kind of POST request this is; for IPP requests the
522 * content-type field will be "application/ipp"...
523 */
524
525 httpGetLength(&(con->http));
526
527 if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0)
528 con->request = ippNew();
529 else if (strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "application/ipp") == 0 &&
530 (strncmp(con->uri, "/printers", 9) == 0 ||
531 strncmp(con->uri, "/classes", 8) == 0 ||
532 strncmp(con->uri, "/jobs", 5) == 0))
533 {
534 /*
535 * CGI request...
536 */
537
538 if (strncmp(con->uri, "/printers", 9) == 0)
539 {
540 sprintf(command, "%s/cgi-bin/printers", ServerRoot);
541 options = con->uri + 9;
542 }
543 else if (strncmp(con->uri, "/classes", 8) == 0)
544 {
545 sprintf(command, "%s/cgi-bin/classes", ServerRoot);
546 options = con->uri + 8;
547 }
548 else
549 {
550 sprintf(command, "%s/cgi-bin/jobs", ServerRoot);
551 options = con->uri + 5;
552 }
553
554 if (*options == '/')
555 options ++;
556
557 if (!SendCommand(con, command, options))
558 {
559 if (!SendError(con, HTTP_NOT_FOUND))
560 {
561 CloseClient(con);
562 return (0);
563 }
564 }
565 else
566 LogRequest(con, HTTP_OK);
567
568 if (con->http.version <= HTTP_1_0)
569 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
570 }
571 else if (!SendError(con, HTTP_UNAUTHORIZED))
572 {
573 CloseClient(con);
574 return (0);
575 }
576 break;
577
578 case HTTP_PUT_RECV :
579 case HTTP_DELETE :
580 case HTTP_TRACE :
581 SendError(con, HTTP_NOT_IMPLEMENTED);
582
583 case HTTP_CLOSE :
584 CloseClient(con);
585 return (0);
586
587 case HTTP_HEAD :
588 if (strncmp(con->uri, "/printers/", 10) == 0 ||
589 strncmp(con->uri, "/classes/", 9) == 0 ||
590 strncmp(con->uri, "/jobs/", 6) == 0)
591 {
592 /*
593 * CGI output...
594 */
595
596 if (!SendHeader(con, HTTP_OK, "text/html"))
597 {
598 CloseClient(con);
599 return (0);
600 }
601
602 if (httpPrintf(HTTP(con), "\r\n") < 0)
603 {
604 CloseClient(con);
605 return (0);
606 }
607
608 LogRequest(con, HTTP_OK);
609 }
610 else if ((filename = get_file(con, &filestats)) == NULL)
611 {
612 if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
613 {
614 CloseClient(con);
615 return (0);
616 }
617
618 LogRequest(con, HTTP_NOT_FOUND);
619 }
620 else if (!check_if_modified(con, &filestats))
621 {
622 if (!SendError(con, HTTP_NOT_MODIFIED))
623 {
624 CloseClient(con);
625 return (0);
626 }
627
628 LogRequest(con, HTTP_NOT_MODIFIED);
629 }
630 else
631 {
632 /*
633 * Serve a file...
634 */
635
636 extension = get_extension(filename);
637 type = get_type(extension);
638
639 if (!SendHeader(con, HTTP_OK, type))
640 {
641 CloseClient(con);
642 return (0);
643 }
644
645 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
646 httpGetDateString(filestats.st_mtime)) < 0)
647 {
648 CloseClient(con);
649 return (0);
650 }
651
652 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n",
653 filestats.st_size) < 0)
654 {
655 CloseClient(con);
656 return (0);
657 }
658
659 LogRequest(con, HTTP_OK);
660 }
661
662 if (send(con->http.fd, "\r\n", 2, 0) < 0)
663 {
664 CloseClient(con);
665 return (0);
666 }
667
668 con->http.state = HTTP_WAITING;
669 break;
670 }
671 }
672
673 /*
674 * Handle any incoming data...
675 */
676
677 switch (con->http.state)
678 {
679 case HTTP_PUT_RECV :
680 break;
681
682 case HTTP_POST_RECV :
683 LogMessage(LOG_DEBUG, "ReadClient() %d con->data_encoding = %s con->data_remaining = %d\n",
684 con->http.fd,
685 con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
686 con->http.data_remaining);
687
688 if (con->request != NULL)
689 {
690 /*
691 * Grab any request data from the connection...
692 */
693
694 if (ippRead(&(con->http), con->request) != IPP_DATA)
695 break;
696
697 /*
698 * Then create a file as needed for the request data...
699 */
700
701 if (con->file == 0 &&
702 (con->http.data_remaining > 0 ||
703 con->http.data_encoding == HTTP_ENCODE_CHUNKED))
704 {
705 sprintf(con->filename, "%s/requests/XXXXXX", ServerRoot);
706 con->file = mkstemp(con->filename);
707
708 LogMessage(LOG_INFO, "ReadClient() %d REQUEST %s", con->http.fd,
709 con->filename);
710
711 if (con->file < 0)
712 {
713 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
714 {
715 CloseClient(con);
716 return (0);
717 }
718 }
719 }
720 }
721
722 if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
723 {
724 CloseClient(con);
725 return (0);
726 }
727 else if (bytes > 0)
728 {
729 con->bytes += bytes;
730
731 LogMessage(LOG_DEBUG, "ReadClient() %d writing %d bytes", bytes);
732
733 if (write(con->file, line, bytes) < bytes)
734 {
735 close(con->file);
736 unlink(con->filename);
737
738 if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
739 {
740 CloseClient(con);
741 return (0);
742 }
743 }
744 }
745
746 if (con->http.state == HTTP_POST_SEND)
747 {
748 close(con->file);
749
750 if (con->request)
751 process_ipp_request(con);
752 }
753 break;
754 }
755
756 if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
757 {
758 CloseClient(con);
759 return (0);
760 }
761 else
762 return (1);
763 }
764
765
766 /*
767 * 'SendCommand()' - Send output from a command via HTTP.
768 */
769
770 int
771 SendCommand(client_t *con,
772 char *command,
773 char *options)
774 {
775 con->pipe_pid = pipe_command(con, 0, &(con->file), command, options);
776
777 LogMessage(LOG_DEBUG, "SendCommand() %d command=\"%s\" file=%d pipe_pid=%d",
778 con->http.fd, command, con->file, con->pipe_pid);
779
780 if (con->pipe_pid == 0)
781 return (0);
782
783 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
784
785 FD_SET(con->file, &InputSet);
786 FD_SET(con->http.fd, &OutputSet);
787
788 if (!SendHeader(con, HTTP_OK, "text/html"))
789 return (0);
790
791 if (con->http.version == HTTP_1_1)
792 {
793 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
794
795 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
796 return (0);
797 }
798
799 if (httpPrintf(HTTP(con), "\r\n") < 0)
800 return (0);
801
802 return (1);
803 }
804
805
806 /*
807 * 'SendError()' - Send an error message via HTTP.
808 */
809
810 int /* O - 1 if successful, 0 otherwise */
811 SendError(client_t *con, /* I - Connection */
812 http_status_t code) /* I - Error code */
813 {
814 char message[1024]; /* Text version of error code */
815
816
817 /*
818 * Put the request in the access_log file...
819 */
820
821 LogRequest(con, code);
822
823 /*
824 * To work around bugs in some proxies, don't use Keep-Alive for some
825 * error messages...
826 */
827
828 if (code >= HTTP_BAD_REQUEST)
829 con->http.keep_alive = HTTP_KEEPALIVE_OFF;
830
831 /*
832 * Send an error message back to the client. If the error code is a
833 * 400 or 500 series, make sure the message contains some text, too!
834 */
835
836 if (!SendHeader(con, code, NULL))
837 return (0);
838
839 if (code == HTTP_UNAUTHORIZED)
840 {
841 if (httpPrintf(HTTP(con), "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
842 return (0);
843 }
844
845 if (con->http.version >= HTTP_1_1 && !con->http.keep_alive)
846 {
847 if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
848 return (0);
849 }
850
851 if (code >= HTTP_BAD_REQUEST)
852 {
853 /*
854 * Send a human-readable error message.
855 */
856
857 sprintf(message, "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>"
858 "<BODY><H1>%s</H1>%s</BODY></HTML>\n",
859 code, httpStatus(code), httpStatus(code),
860 con->language ? (char *)con->language->messages[code] : httpStatus(code));
861
862 if (httpPrintf(HTTP(con), "Content-Type: text/html\r\n") < 0)
863 return (0);
864 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", strlen(message)) < 0)
865 return (0);
866 if (httpPrintf(HTTP(con), "\r\n") < 0)
867 return (0);
868 if (send(con->http.fd, message, strlen(message), 0) < 0)
869 return (0);
870 }
871 else if (httpPrintf(HTTP(con), "\r\n") < 0)
872 return (0);
873
874 con->http.state = HTTP_WAITING;
875
876 return (1);
877 }
878
879
880 /*
881 * 'SendFile()' - Send a file via HTTP.
882 */
883
884 int
885 SendFile(client_t *con,
886 http_status_t code,
887 char *filename,
888 char *type,
889 struct stat *filestats)
890 {
891 con->file = open(filename, O_RDONLY);
892
893 LogMessage(LOG_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
894
895 if (con->file < 0)
896 return (0);
897
898 fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
899
900 con->pipe_pid = 0;
901
902 if (!SendHeader(con, code, type))
903 return (0);
904
905 if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n", httpGetDateString(filestats->st_mtime)) < 0)
906 return (0);
907 if (httpPrintf(HTTP(con), "Content-Length: %d\r\n", filestats->st_size) < 0)
908 return (0);
909 if (httpPrintf(HTTP(con), "\r\n") < 0)
910 return (0);
911
912 FD_SET(con->http.fd, &OutputSet);
913
914 return (1);
915 }
916
917
918 /*
919 * 'SendHeader()' - Send an HTTP request.
920 */
921
922 int /* O - 1 on success, 0 on failure */
923 SendHeader(client_t *con, /* I - Client to send to */
924 http_status_t code, /* I - HTTP status code */
925 char *type) /* I - MIME type of document */
926 {
927 if (httpPrintf(HTTP(con), "HTTP/%d.%d %d %s\r\n", con->http.version / 100,
928 con->http.version % 100, code, httpStatus(code)) < 0)
929 return (0);
930 if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
931 return (0);
932 if (httpPrintf(HTTP(con), "Server: CUPS/1.0\r\n") < 0)
933 return (0);
934 if (con->http.keep_alive && con->http.version >= HTTP_1_0)
935 {
936 if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
937 return (0);
938 if (httpPrintf(HTTP(con), "Keep-Alive: timeout=%d\r\n", KeepAliveTimeout) < 0)
939 return (0);
940 }
941 if (con->language != NULL)
942 {
943 if (httpPrintf(HTTP(con), "Content-Language: %s\r\n",
944 con->language->language) < 0)
945 return (0);
946
947 if (type != NULL)
948 if (httpPrintf(HTTP(con), "Content-Type: %s; charset=%s\r\n", type,
949 cupsLangEncoding(con->language)) < 0)
950 return (0);
951 }
952 else if (type != NULL)
953 if (httpPrintf(HTTP(con), "Content-Type: %s\r\n", type) < 0)
954 return (0);
955
956 return (1);
957 }
958
959
960 /*
961 * 'StartListening()' - Create all listening sockets...
962 */
963
964 void
965 StartListening(void)
966 {
967 int i, /* Looping var */
968 val; /* Parameter value */
969 listener_t *lis; /* Current listening socket */
970
971
972 #if defined(WIN32) || defined(__EMX__)
973 /*
974 * Initialize Windows sockets... This handles loading the DLL, etc. since
975 * Windows doesn't have built-in support for sockets like UNIX...
976 */
977
978 WSADATA wsadata; /* Socket data for application from DLL */
979 static int initialized = 0;/* Whether or not we've initialized things */
980
981
982 if (!initialized)
983 {
984 initialized = 1;
985
986 if (WSAStartup(MAKEWORD(1,1), &wsadata) != 0)
987 exit(1);
988 }
989 #else
990 /*
991 * Setup a 'broken pipe' signal handler for lost clients.
992 */
993
994 sigset(SIGPIPE, sigpipe_handler);
995 #endif /* !WIN32 */
996
997 /*
998 * Setup socket listeners...
999 */
1000
1001 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
1002 {
1003 LogMessage(LOG_INFO, "StartListening() address=%08x port=%d",
1004 ntohl(lis->address.sin_addr.s_addr),
1005 ntohs(lis->address.sin_port));
1006
1007 if ((lis->fd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) == -1)
1008 {
1009 LogMessage(LOG_ERROR, "StartListening() Unable to open listen socket - %s.",
1010 strerror(errno));
1011 exit(errno);
1012 }
1013
1014 fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC);
1015
1016 /*
1017 * Set things up to reuse the local address for this port.
1018 */
1019
1020 val = 1;
1021 setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
1022
1023 /*
1024 * Bind to the port we found...
1025 */
1026
1027 if (bind(lis->fd, (struct sockaddr *)&(lis->address), sizeof(lis->address)) < 0)
1028 {
1029 LogMessage(LOG_ERROR, "StartListening() Unable to bind socket - %s.", strerror(errno));
1030 exit(errno);
1031 }
1032
1033 /*
1034 * Listen for new clients.
1035 */
1036
1037 if (listen(lis->fd, SOMAXCONN) < 0)
1038 {
1039 LogMessage(LOG_ERROR, "StartListening() Unable to listen for clients - %s.",
1040 strerror(errno));
1041 exit(errno);
1042 }
1043
1044 /*
1045 * Setup the select() input mask to contain the listening socket we have.
1046 */
1047
1048 FD_SET(lis->fd, &InputSet);
1049 }
1050
1051 LogMessage(LOG_INFO, "StartListening() NumListeners=%d", NumListeners);
1052 }
1053
1054
1055 /*
1056 * 'StopListening()' - Close all listening sockets...
1057 */
1058
1059 void
1060 StopListening(void)
1061 {
1062 int i; /* Looping var */
1063 listener_t *lis; /* Current listening socket */
1064
1065
1066 for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
1067 {
1068 #if defined(WIN32) || defined(__EMX__)
1069 closesocket(lis->fd);
1070 #else
1071 close(lis->fd);
1072 #endif /* WIN32 || __EMX__ */
1073
1074 FD_CLR(lis->fd, &InputSet);
1075 }
1076
1077 LogMessage(LOG_INFO, "StopListening()");
1078 }
1079
1080
1081 /*
1082 * 'WriteClient()' - Write data to a client as needed.
1083 */
1084
1085 int
1086 WriteClient(client_t *con)
1087 {
1088 int bytes;
1089 int status;
1090 char buf[HTTP_MAX_BUFFER];
1091
1092
1093 if (con->http.state != HTTP_GET_SEND &&
1094 con->http.state != HTTP_POST_SEND)
1095 return (1);
1096
1097 if (con->response != NULL)
1098 bytes = ippWrite(&(con->http), con->response) != IPP_DATA;
1099 else if ((bytes = read(con->file, buf, sizeof(buf))) > 0)
1100 {
1101 if (httpWrite(HTTP(con), buf, bytes) < 0)
1102 {
1103 CloseClient(con);
1104 return (0);
1105 }
1106
1107 con->bytes += bytes;
1108 }
1109
1110 if (bytes <= 0)
1111 {
1112 LogRequest(con, HTTP_OK);
1113
1114 if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
1115 {
1116 if (httpPrintf(HTTP(con), "0\r\n\r\n") < 0)
1117 {
1118 CloseClient(con);
1119 return (0);
1120 }
1121 }
1122
1123 FD_CLR(con->http.fd, &OutputSet);
1124 FD_CLR(con->file, &InputSet);
1125
1126 if (con->pipe_pid)
1127 {
1128 kill(con->pipe_pid, SIGKILL);
1129 waitpid(con->pipe_pid, &status, WNOHANG);
1130 }
1131
1132 close(con->file);
1133
1134 if (!con->http.keep_alive)
1135 {
1136 CloseClient(con);
1137 return (0);
1138 }
1139
1140 con->http.state = HTTP_WAITING;
1141 con->file = 0;
1142 con->pipe_pid = 0;
1143
1144 if (con->request != NULL)
1145 {
1146 ippDelete(con->request);
1147 con->request = NULL;
1148 }
1149
1150 if (con->response != NULL)
1151 {
1152 ippDelete(con->response);
1153 con->response = NULL;
1154 }
1155 }
1156
1157 LogMessage(LOG_DEBUG, "WriteClient() %d %d bytes", con->http.fd, bytes);
1158
1159 con->http.activity = time(NULL);
1160
1161 return (1);
1162 }
1163
1164
1165 /*
1166 * 'check_if_modified()' - Decode an "If-Modified-Since" line.
1167 */
1168
1169 static int /* O - 1 if modified since */
1170 check_if_modified(client_t *con, /* I - Client connection */
1171 struct stat *filestats) /* I - File information */
1172 {
1173 char *ptr; /* Pointer into field */
1174 time_t date; /* Time/date value */
1175 int size; /* Size/length value */
1176
1177
1178 size = 0;
1179 date = 0;
1180 ptr = con->http.fields[HTTP_FIELD_IF_MODIFIED_SINCE];
1181
1182 if (*ptr == '\0')
1183 return (1);
1184
1185 LogMessage(LOG_DEBUG, "check_if_modified() %d If-Modified-Since=\"%s\"",
1186 con->http.fd, ptr);
1187
1188 while (*ptr != '\0')
1189 {
1190 while (isspace(*ptr) || *ptr == ';')
1191 ptr ++;
1192
1193 if (strncasecmp(ptr, "length=", 7) == 0)
1194 {
1195 ptr += 7;
1196 size = atoi(ptr);
1197
1198 while (isdigit(*ptr))
1199 ptr ++;
1200 }
1201 else if (isalpha(*ptr))
1202 {
1203 date = httpGetDateTime(ptr);
1204 while (*ptr != '\0' && *ptr != ';')
1205 ptr ++;
1206 }
1207 }
1208
1209 LogMessage(LOG_DEBUG, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
1210 con->http.fd, size, filestats->st_size, date, filestats->st_mtime);
1211
1212 return ((size != filestats->st_size && size != 0) ||
1213 (date < filestats->st_mtime && date != 0) ||
1214 (size == 0 && date == 0));
1215 }
1216
1217
1218 /*
1219 * 'decode_basic_auth()' - Decode a Basic authorization string.
1220 */
1221
1222 static void
1223 decode_basic_auth(client_t *con) /* I - Client to decode to */
1224 {
1225 char value[1024]; /* Value string */
1226
1227
1228 /*
1229 * Decode the string and pull the username and password out...
1230 */
1231
1232 httpDecode64(value, con->http.fields[HTTP_FIELD_AUTHORIZATION]);
1233
1234 LogMessage(LOG_DEBUG, "decode_basic_auth() %d Authorization=\"%s\"",
1235 con->http.fd, value);
1236
1237 sscanf(value, "%[^:]:%[^\n]", con->username, con->password);
1238 }
1239
1240
1241 /*
1242 * 'pipe_command()' - Pipe the output of a command to the remote client.
1243 */
1244
1245 static int /* O - Process ID */
1246 pipe_command(client_t *con, /* I - Client connection */
1247 int infile, /* I - Standard input for command */
1248 int *outfile, /* O - Standard output for command */
1249 char *command, /* I - Command to run */
1250 char *options) /* I - Options for command */
1251 {
1252 int pid; /* Process ID */
1253 char *commptr; /* Command string pointer */
1254 int fds[2]; /* Pipe FDs */
1255 int argc; /* Number of arguments */
1256 char argbuf[1024], /* Argument buffer */
1257 *argv[100], /* Argument strings */
1258 *envp[100]; /* Environment variables */
1259 static char lang[1024]; /* LANG env variable */
1260 static char content_length[1024]; /* CONTENT_LENGTH env variable */
1261 static char content_type[1024]; /* CONTENT_TYPE env variable */
1262 static char server_port[1024]; /* Default listen port */
1263 static char remote_host[1024]; /* REMOTE_HOST env variable */
1264 static char remote_user[1024]; /* REMOTE_HOST env variable */
1265
1266
1267 /*
1268 * Copy the command string...
1269 */
1270
1271 strncpy(argbuf, options, sizeof(argbuf) - 1);
1272 argbuf[sizeof(argbuf) - 1] = '\0';
1273
1274 /*
1275 * Parse the string; arguments can be separated by spaces or by ? or +...
1276 */
1277
1278 argv[0] = argbuf;
1279
1280 for (commptr = argbuf, argc = 1; *commptr != '\0'; commptr ++)
1281 if (*commptr == ' ' || *commptr == '?' || *commptr == '+')
1282 {
1283 *commptr++ = '\0';
1284
1285 while (*commptr == ' ')
1286 commptr ++;
1287
1288 if (*commptr != '\0')
1289 {
1290 argv[argc] = commptr;
1291 argc ++;
1292 }
1293
1294 commptr --;
1295 }
1296 else if (*commptr == '%')
1297 {
1298 if (commptr[1] >= '0' && commptr[1] <= '9')
1299 *commptr = (commptr[1] - '0') << 4;
1300 else
1301 *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
1302
1303 if (commptr[2] >= '0' && commptr[2] <= '9')
1304 *commptr |= commptr[2] - '0';
1305 else
1306 *commptr |= tolower(commptr[2]) - 'a' + 10;
1307
1308 strcpy(commptr + 1, commptr + 3);
1309 }
1310
1311 argv[argc] = NULL;
1312
1313 if (argv[1][0] == '\0')
1314 argv[1] = strrchr(command, '/') + 1;
1315
1316 /*
1317 * Setup the environment variables as needed...
1318 */
1319
1320 sprintf(lang, "LANG=%s", con->language ? con->language->language : "C");
1321 sprintf(server_port, "SERVER_PORT=%d", ntohs(con->http.hostaddr.sin_port));
1322 sprintf(remote_host, "REMOTE_HOST=%s", con->http.hostname);
1323 sprintf(remote_user, "REMOTE_USER=%s", con->username);
1324
1325 envp[0] = "PATH=/bin:/usr/bin";
1326 envp[1] = "SERVER_SOFTWARE=CUPS/1.0";
1327 envp[2] = "SERVER_NAME=localhost"; /* This isn't 100% correct... */
1328 envp[3] = "GATEWAY_INTERFACE=CGI/1.1";
1329 envp[4] = "SERVER_PROTOCOL=HTTP/1.1";
1330 envp[5] = server_port;
1331 envp[6] = remote_host;
1332 envp[7] = remote_user;
1333 envp[8] = lang;
1334 envp[9] = "TZ=GMT";
1335
1336 if (con->operation == HTTP_GET)
1337 {
1338 envp[10] = "REQUEST_METHOD=GET";
1339 envp[11] = NULL;
1340 }
1341 else
1342 {
1343 sprintf(content_length, "CONTENT_LENGTH=%d", con->http.data_remaining);
1344 sprintf(content_type, "CONTENT_TYPE=%s",
1345 con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
1346
1347 envp[10] = "REQUEST_METHOD=POST";
1348 envp[11] = content_length;
1349 envp[12] = content_type;
1350 envp[13] = NULL;
1351 }
1352
1353 /*
1354 * Create a pipe for the output...
1355 */
1356
1357 if (pipe(fds))
1358 return (0);
1359
1360 /*
1361 * Then execute the pipe command...
1362 */
1363
1364 if ((pid = fork()) == 0)
1365 {
1366 /*
1367 * Child comes here... Close stdin if necessary and dup the pipe to stdout.
1368 */
1369
1370 setuid(User);
1371 setgid(Group);
1372
1373 if (infile)
1374 {
1375 close(0);
1376 dup(infile);
1377 }
1378
1379 close(1);
1380 dup(fds[1]);
1381
1382 close(fds[0]);
1383 close(fds[1]);
1384
1385 /*
1386 * Execute the pipe program; if an error occurs, exit with status 1...
1387 */
1388
1389 execve(command, argv, envp);
1390 exit(1);
1391 return (0);
1392 }
1393 else if (pid < 0)
1394 {
1395 /*
1396 * Error - can't fork!
1397 */
1398
1399 close(fds[0]);
1400 close(fds[1]);
1401 return (0);
1402 }
1403 else
1404 {
1405 /*
1406 * Fork successful - return the PID...
1407 */
1408
1409 *outfile = fds[0];
1410 close(fds[1]);
1411
1412 return (pid);
1413 }
1414 }
1415
1416
1417 /*
1418 * 'process_ipp_request()' - Process an incoming IPP request...
1419 */
1420
1421 static void
1422 process_ipp_request(client_t *con) /* I - Client connection */
1423 {
1424 ipp_tag_t group; /* Current group tag */
1425 ipp_attribute_t *attr; /* Current attribute */
1426 ipp_attribute_t *charset; /* Character set attribute */
1427 ipp_attribute_t *language; /* Language attribute */
1428 ipp_attribute_t *uri; /* Printer URI attribute */
1429 char *dest; /* Destination */
1430 cups_ptype_t dtype; /* Destination type (printer or class) */
1431 int priority; /* Job priority */
1432 job_t *job; /* Current job */
1433 char job_uri[HTTP_MAX_URI],
1434 /* Job URI */
1435 method[HTTP_MAX_URI],
1436 /* Method portion of URI */
1437 username[HTTP_MAX_URI],
1438 /* Username portion of URI */
1439 host[HTTP_MAX_URI],
1440 /* Host portion of URI */
1441 resource[HTTP_MAX_URI];
1442 /* Resource portion of URI */
1443 int port; /* Port portion of URI */
1444
1445
1446 DEBUG_printf(("process_ipp_request(%08x)\n", con));
1447 DEBUG_printf(("process_ipp_request: operation_id = %04x\n",
1448 con->request->request.op.operation_id));
1449
1450 /*
1451 * First build an empty response message for this request...
1452 */
1453
1454 con->response = ippNew();
1455
1456 con->response->request.status.version[0] = 1;
1457 con->response->request.status.version[1] = 0;
1458 con->response->request.status.request_id = con->request->request.op.request_id;
1459
1460 /*
1461 * Then validate the request header and required attributes...
1462 */
1463
1464 if (con->request->request.any.version[0] != 1)
1465 {
1466 /*
1467 * Return an error, since we only support IPP 1.x.
1468 */
1469
1470 send_ipp_error(con, IPP_VERSION_NOT_SUPPORTED);
1471
1472 return;
1473 }
1474
1475 /*
1476 * Make sure that the attributes are provided in the correct order and
1477 * don't repeat groups...
1478 */
1479
1480 for (attr = con->request->attrs, group = attr->group_tag;
1481 attr != NULL;
1482 attr = attr->next)
1483 if (attr->group_tag < group)
1484 {
1485 /*
1486 * Out of order; return an error...
1487 */
1488
1489 DEBUG_puts("process_ipp_request: attribute groups are out of order!");
1490 send_ipp_error(con, IPP_BAD_REQUEST);
1491 return;
1492 }
1493 else
1494 group = attr->group_tag;
1495
1496 /*
1497 * Then make sure that the first three attributes are:
1498 *
1499 * attributes-charset
1500 * attributes-natural-language
1501 * printer-uri
1502 */
1503
1504 attr = con->request->attrs;
1505 if (attr != NULL && strcmp(attr->name, "attributes-charset") == 0)
1506 charset = attr;
1507 else
1508 charset = NULL;
1509
1510 attr = attr->next;
1511 if (attr != NULL && strcmp(attr->name, "attributes-natural-language") == 0)
1512 language = attr;
1513 else
1514 language = NULL;
1515
1516 attr = attr->next;
1517 if (attr != NULL && strcmp(attr->name, "printer-uri") == 0)
1518 uri = attr;
1519 else if (attr != NULL && strcmp(attr->name, "job-uri") == 0)
1520 uri = attr;
1521 else
1522 uri = NULL;
1523
1524 if (charset == NULL || language == NULL || uri == NULL)
1525 {
1526 /*
1527 * Return an error, since attributes-charset,
1528 * attributes-natural-language, and printer-uri/job-uri are required
1529 * for all operations.
1530 */
1531
1532 DEBUG_printf(("process_ipp_request: missing attributes (%08x, %08x, %08x)!\n",
1533 charset, language, uri));
1534 send_ipp_error(con, IPP_BAD_REQUEST);
1535 return;
1536 }
1537
1538 attr = ippAddString(con->response, IPP_TAG_OPERATION, "attributes-charset",
1539 charset->values[0].string);
1540 attr->value_tag = IPP_TAG_CHARSET;
1541
1542 attr = ippAddString(con->response, IPP_TAG_OPERATION,
1543 "attributes-natural-language", language->values[0].string);
1544 attr->value_tag = IPP_TAG_LANGUAGE;
1545
1546 httpSeparate(uri->values[0].string, method, username, host, &port, resource);
1547
1548 /*
1549 * OK, all the checks pass so far; try processing the operation...
1550 */
1551
1552 switch (con->request->request.op.operation_id)
1553 {
1554 case IPP_PRINT_JOB :
1555 /*
1556 * OK, see if the client is sending the document compressed - CUPS
1557 * doesn't support compression yet...
1558 */
1559
1560 if ((attr = ippFindAttribute(con->request, "compression")) != NULL)
1561 {
1562 DEBUG_puts("process_ipp_request: Unsupported compression attribute!");
1563 send_ipp_error(con, IPP_ATTRIBUTES);
1564 attr = ippAddString(con->response, IPP_TAG_UNSUPPORTED,
1565 "compression", attr->values[0].string);
1566 attr->value_tag = IPP_TAG_KEYWORD;
1567
1568 return;
1569 }
1570
1571 /*
1572 * Do we have a file to print?
1573 */
1574
1575 if (con->filename[0] == '\0')
1576 {
1577 DEBUG_puts("process_ipp_request: No filename!?!");
1578 send_ipp_error(con, IPP_BAD_REQUEST);
1579 return;
1580 }
1581
1582 /*
1583 * Is the destination valid?
1584 */
1585
1586 if (strncmp(resource, "/classes/", 9) == 0)
1587 {
1588 /*
1589 * Print to a class...
1590 */
1591
1592 dest = resource + 9;
1593 dtype = CUPS_PRINTER_CLASS;
1594
1595 if (FindClass(dest) == NULL)
1596 {
1597 send_ipp_error(con, IPP_NOT_FOUND);
1598 return;
1599 }
1600 }
1601 else if (strncmp(resource, "/printers/", 10) == 0)
1602 {
1603 /*
1604 * Print to a specific printer...
1605 */
1606
1607 dest = resource + 10;
1608 dtype = (cups_ptype_t)0;
1609
1610 if (FindPrinter(dest) == NULL && FindClass(dest) == NULL)
1611 {
1612 send_ipp_error(con, IPP_NOT_FOUND);
1613 return;
1614 }
1615 }
1616 else
1617 {
1618 /*
1619 * Bad URI...
1620 */
1621
1622 DEBUG_printf(("process_ipp_request: resource name \'%s\' no good!\n",
1623 resource));
1624 send_ipp_error(con, IPP_BAD_REQUEST);
1625 return;
1626 }
1627
1628 /*
1629 * Create the job and set things up...
1630 */
1631
1632 if ((attr = ippFindAttribute(con->request, "job-priority")) != NULL)
1633 priority = attr->values[0].integer;
1634 else
1635 priority = 50;
1636
1637 if ((job = AddJob(priority, dest)) == NULL)
1638 {
1639 send_ipp_error(con, IPP_INTERNAL_ERROR);
1640 return;
1641 }
1642
1643 job->dtype = dtype;
1644
1645 job->attrs = con->request;
1646 con->request = NULL;
1647
1648 strcpy(con->filename, job->filename);
1649 con->filename[0] = '\0';
1650
1651 job->state = IPP_JOB_PENDING;
1652
1653 /*
1654 * Start the job if possible...
1655 */
1656
1657 CheckJobs();
1658
1659 /*
1660 * Fill in the response info...
1661 */
1662
1663 sprintf(job_uri, "http://%s:%d/jobs/%d", ServerName,
1664 ntohs(con->http.hostaddr.sin_port), job->id);
1665 attr = ippAddString(con->response, IPP_TAG_JOB, "job-uri", job_uri);
1666 attr->value_tag = IPP_TAG_URI;
1667
1668 attr = ippAddInteger(con->response, IPP_TAG_JOB, "job-id", job->id);
1669 attr->value_tag = IPP_TAG_INTEGER;
1670
1671 attr = ippAddInteger(con->response, IPP_TAG_JOB, "job-state", job->state);
1672 attr->value_tag = IPP_TAG_ENUM;
1673
1674 con->response->request.status.status_code = IPP_OK;
1675 break;
1676
1677 case IPP_VALIDATE_JOB :
1678 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1679 break;
1680
1681 case IPP_CANCEL_JOB :
1682 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1683 break;
1684
1685 case IPP_GET_JOB_ATTRIBUTES :
1686 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1687 break;
1688
1689 case IPP_GET_JOBS :
1690 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1691 break;
1692
1693 case IPP_GET_PRINTER_ATTRIBUTES :
1694 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1695 break;
1696
1697 case CUPS_GET_DEFAULT :
1698 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1699 break;
1700
1701 case CUPS_GET_PRINTERS :
1702 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1703 break;
1704
1705 case CUPS_ADD_PRINTER :
1706 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1707 break;
1708
1709 case CUPS_DELETE_PRINTER :
1710 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1711 break;
1712
1713 case CUPS_GET_CLASSES :
1714 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1715 break;
1716
1717 case CUPS_ADD_CLASS :
1718 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1719 break;
1720
1721 case CUPS_DELETE_CLASS :
1722 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1723 break;
1724
1725 default :
1726 send_ipp_error(con, IPP_OPERATION_NOT_SUPPORTED);
1727 return;
1728 }
1729
1730 FD_SET(con->http.fd, &OutputSet);
1731 }
1732
1733
1734 /*
1735 * 'send_ipp_error()' - Send an error status back to the IPP client.
1736 */
1737
1738 static void
1739 send_ipp_error(client_t *con, /* I - Client connection */
1740 ipp_status_t status) /* I - IPP status code */
1741 {
1742 ipp_attribute_t *attr; /* Current attribute */
1743
1744
1745 if (con->filename[0])
1746 unlink(con->filename);
1747
1748 con->response->request.status.status_code = status;
1749
1750 attr = ippAddString(con->response, IPP_TAG_OPERATION, "attributes-charset",
1751 DefaultCharset);
1752 attr->value_tag = IPP_TAG_CHARSET;
1753
1754 attr = ippAddString(con->response, IPP_TAG_OPERATION,
1755 "attributes-natural-language", DefaultLanguage);
1756 attr->value_tag = IPP_TAG_LANGUAGE;
1757
1758 FD_SET(con->http.fd, &OutputSet);
1759 }
1760
1761
1762 /*
1763 * 'sigpipe_handler()' - Handle 'broken pipe' signals from lost network
1764 * clients.
1765 */
1766
1767 static void
1768 sigpipe_handler(int sig) /* I - Signal number */
1769 {
1770 (void)sig;
1771 /* IGNORE */
1772 }
1773
1774
1775
1776
1777 /**** OBSOLETE ****/
1778
1779 /*
1780 * 'get_extension()' - Get the extension for a filename.
1781 */
1782
1783 static char * /* O - Pointer to extension */
1784 get_extension(char *filename) /* I - Filename or URI */
1785 {
1786 char *ext; /* Pointer to extension */
1787
1788
1789 ext = filename + strlen(filename) - 1;
1790
1791 while (ext > filename && *ext != '/')
1792 if (*ext == '.')
1793 return (ext + 1);
1794 else
1795 ext --;
1796
1797 return ("");
1798 }
1799
1800
1801 /*
1802 * 'get_file()' - Get a filename and state info.
1803 */
1804
1805 static char * /* O - Real filename */
1806 get_file(client_t *con, /* I - Client connection */
1807 struct stat *filestats)/* O - File information */
1808 {
1809 int status; /* Status of filesystem calls */
1810 char *params; /* Pointer to parameters in URI */
1811 static char filename[1024]; /* Filename buffer */
1812
1813
1814 /*
1815 * Need to add DocumentRoot global...
1816 */
1817
1818 if (con->language != NULL)
1819 sprintf(filename, "%s/%s%s", DocumentRoot, con->language->language,
1820 con->uri);
1821 else
1822 sprintf(filename, "%s%s", DocumentRoot, con->uri);
1823
1824 if ((params = strchr(filename, '?')) != NULL)
1825 *params = '\0';
1826
1827 /*
1828 * Grab the status for this language; if there isn't a language-specific file
1829 * then fallback to the default one...
1830 */
1831
1832 if ((status = stat(filename, filestats)) != 0 && con->language != NULL)
1833 {
1834 /*
1835 * Drop the language prefix and try the current directory...
1836 */
1837
1838 sprintf(filename, "%s%s", DocumentRoot, con->uri);
1839
1840 status = stat(filename, filestats);
1841 }
1842
1843 /*
1844 * If we're found a directory, get the index.html file instead...
1845 */
1846
1847 if (!status && S_ISDIR(filestats->st_mode))
1848 {
1849 if (filename[strlen(filename) - 1] == '/')
1850 strcat(filename, "index.html");
1851 else
1852 strcat(filename, "/index.html");
1853
1854 status = stat(filename, filestats);
1855 }
1856
1857 LogMessage(LOG_DEBUG, "get_filename() %d filename=%s size=%d",
1858 con->http.fd, filename, filestats->st_size);
1859
1860 if (status)
1861 return (NULL);
1862 else
1863 return (filename);
1864 }
1865
1866
1867 /*
1868 * 'get_type()' - Get MIME type from the given extension.
1869 */
1870
1871 static char *
1872 get_type(char *extension)
1873 {
1874 if (strcmp(extension, "html") == 0 || strcmp(extension, "htm") == 0)
1875 return ("text/html");
1876 else if (strcmp(extension, "txt") == 0)
1877 return ("text/plain");
1878 else if (strcmp(extension, "gif") == 0)
1879 return ("image/gif");
1880 else if (strcmp(extension, "jpg") == 0)
1881 return ("image/jpg");
1882 else if (strcmp(extension, "png") == 0)
1883 return ("image/png");
1884 else if (strcmp(extension, "ps") == 0)
1885 return ("application/postscript");
1886 else if (strcmp(extension, "pdf") == 0)
1887 return ("application/pdf");
1888 else if (strcmp(extension, "gz") == 0)
1889 return ("application/gzip");
1890 else
1891 return ("application/unknown");
1892 }
1893
1894
1895 /*
1896 * 'show_printer_status()' - Show the current printer status.
1897 */
1898
1899 static int
1900 show_printer_status(client_t *con)
1901 {
1902 printer_t *p;
1903 char buffer[HTTP_MAX_BUFFER];
1904 static char *states[] =
1905 {
1906 "", "", "", "Idle", "Processing", "Stopped"
1907 };
1908
1909
1910 p = NULL;
1911 if (con->uri[10] != '\0')
1912 if ((p = FindPrinter(con->uri + 10)) == NULL)
1913 return (0);
1914
1915 if (!SendHeader(con, HTTP_OK, "text/html"))
1916 return (0);
1917
1918 if (con->http.version == HTTP_1_1)
1919 {
1920 con->http.data_encoding = HTTP_ENCODE_CHUNKED;
1921
1922 if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
1923 return (0);
1924 }
1925
1926 if (httpPrintf(HTTP(con), "\r\n") < 0)
1927 return (0);
1928
1929 if (p != NULL)
1930 {
1931 /*
1932 * Send printer information...
1933 */
1934
1935 sprintf(buffer, "<HTML>\n"
1936 "<HEAD>\n"
1937 "\t<TITLE>Printer Status for %s</TITLE>\n"
1938 "\t<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10\">\n"
1939 "</HEAD>\n"
1940 "<BODY BGCOLOR=#ffffff>\n"
1941 "<TABLE WIDTH=100%%><TR>\n"
1942 "\t<TD ALIGN=LEFTV ALIGN=MIDDLE><IMG SRC=\"/images/cups-small.gif\"></TD>\n"
1943 "\t<TD ALIGN=CENTER VALIGN=MIDDLE><H1>Printer Status for %s</H1></TD>\n"
1944 "\t<TD ALIGN=RIGHT VALIGN=MIDDLE><IMG SRC=\"/images/vendor.gif\"></TD>\n"
1945 "</TR></TABLE>\n"
1946 "<CENTER><TABLE BORDER=1 WIDTH=80%%>\n"
1947 "<TR><TH>Name</TH><TH>Value</TH></TR>\n"
1948 "<TR><TD>Info</TD><TD>%s</TD></TR>\n"
1949 "<TR><TD>MoreInfo</TD><TD>%s</TD></TR>\n"
1950 "<TR><TD>Location</TD><TD>%s</TD></TR>\n"
1951 "<TR><TD>Device URI</TD><TD>%s</TD></TR>\n"
1952 "<TR><TD>PPDFile</TD><TD>%s</TD></TR>\n",
1953 p->name, p->name, p->info, p->more_info,
1954 p->location, p->device_uri, p->ppd);
1955
1956 con->bytes += httpWrite(HTTP(con), buffer, strlen(buffer));
1957 }
1958 else
1959 {
1960 strcpy(buffer, "<HTML>\n"
1961 "<HEAD>\n"
1962 "\t<TITLE>Available Printers</TITLE>\n"
1963 "\t<META HTTP-EQUIV=\"Refresh\" CONTENT=\"10\">\n"
1964 "</HEAD>\n"
1965 "<BODY BGCOLOR=#ffffff>\n"
1966 "<TABLE WIDTH=100%%><TR>\n"
1967 "\t<TD ALIGN=LEFT VALIGN=MIDDLE><IMG SRC=\"/images/cups-small.gif\"></TD>\n"
1968 "\t<TD ALIGN=CENTER VALIGN=MIDDLE><H1>Available Printers</H1></TD>\n"
1969 "\t<TD ALIGN=RIGHT VALIGN=MIDDLE><IMG SRC=\"/images/vendor.gif\"></TD>\n"
1970 "</TR></TABLE>\n"
1971 "<CENTER><TABLE BORDER=1 WIDTH=80%%>\n"
1972 "<TR><TH>Name</TH><TH>State</TH><TH>Info</TH></TR>\n");
1973
1974 con->bytes += httpWrite(HTTP(con), buffer, strlen(buffer));
1975
1976 for (p = Printers; p != NULL; p = p->next)
1977 {
1978 sprintf(buffer, "<TR><TD><A HREF=/printers/%s>%s</A></TD><TD>%s</TD><TD>%s</TD></TR>\n",
1979 p->name, p->name, states[p->state], p->info);
1980 con->bytes += httpWrite(HTTP(con), buffer, strlen(buffer));
1981 }
1982 }
1983
1984 strcpy(buffer, "</TABLE></CENTER>\n"
1985 "<HR>\n"
1986 "CUPS Copyright 1997-1999 by Easy Software Products, "
1987 "All Rights Reserved. CUPS and the CUPS logo are the "
1988 "trademark property of Easy Software Products.\n"
1989 "</BODY>\n"
1990 "</HTML>\n");
1991 con->bytes += httpWrite(HTTP(con), buffer, strlen(buffer));
1992 httpWrite(HTTP(con), buffer, 0);
1993
1994 return (1);
1995 }
1996
1997
1998 /*
1999 * End of "$Id: client.c,v 1.5 1999/02/26 15:11:11 mike Exp $".
2000 */