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