]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/pap.c
Merge CUPS 1.4svn-r7319.
[thirdparty/cups.git] / backend / pap.c
1 /*
2 * "$Id: pap.c 7010 2007-10-10 21:08:51Z mike $"
3 *
4 * Copyright 2004-2008 Apple Inc. All rights reserved.
5 *
6 * IMPORTANT: This Apple software is supplied to you by Apple Computer,
7 * Inc. ("Apple") in consideration of your agreement to the following
8 * terms, and your use, installation, modification or redistribution of
9 * this Apple software constitutes acceptance of these terms. If you do
10 * not agree with these terms, please do not use, install, modify or
11 * redistribute this Apple software.
12 *
13 * In consideration of your agreement to abide by the following terms, and
14 * subject to these terms, Apple grants you a personal, non-exclusive
15 * license, under Appleƕs copyrights in this original Apple software (the
16 * "Apple Software"), to use, reproduce, modify and redistribute the Apple
17 * Software, with or without modifications, in source and/or binary forms;
18 * provided that if you redistribute the Apple Software in its entirety and
19 * without modifications, you must retain this notice and the following
20 * text and disclaimers in all such redistributions of the Apple Software.
21 * Neither the name, trademarks, service marks or logos of Apple Computer,
22 * Inc. may be used to endorse or promote products derived from the Apple
23 * Software without specific prior written permission from Apple. Except
24 * as expressly stated in this notice, no other rights or licenses, express
25 * or implied, are granted by Apple herein, including but not limited to
26 * any patent rights that may be infringed by your derivative works or by
27 * other works in which the Apple Software may be incorporated.
28 *
29 * The Apple Software is provided by Apple on an "AS IS" basis. APPLE
30 * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
31 * THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
32 * FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
33 * OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
34 *
35 * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
36 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
37 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38 * INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
39 * MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
40 * AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
41 * STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGE.
43 *
44 *
45 * This program implements the Printer Access Protocol (PAP) on top of AppleTalk
46 * Transaction Protocol (ATP). If it were to use the blocking pap functions of
47 * the AppleTalk library it would need seperate threads for reading, writing
48 * and status.
49 *
50 * Contents:
51 *
52 * main() - Send a file to the specified Appletalk printer.
53 * listDevices() - List all LaserWriter printers in the local zone.
54 * printFile() - Print file.
55 * papOpen() - Open a pap session to a printer.
56 * papClose() - Close a pap session.
57 * papWrite() - Write bytes to a printer.
58 * papCloseResp() - Send a pap close response.
59 * papSendRequest() - Fomrat and send a pap packet.
60 * papCancelRequest() - Cancel a pending pap request.
61 * sidechannel_request() - Handle side-channel requests.
62 * statusUpdate() - Print printer status to stderr.
63 * parseUri() - Extract the print name and zone from a uri.
64 * addPercentEscapes() - Encode a string with percent escapes.
65 * removePercentEscapes - Remove percent escape sequences from a string.
66 * nbptuple_compare() - Compare routine for qsort.
67 * okayToUseAppleTalk() - Returns true if AppleTalk is available and enabled.
68 * packet_name() - Returns packet name string.
69 * connectTimeout() - Returns the connect timeout preference value.
70 * signalHandler() - handle SIGINT to close the session before quiting.
71 */
72
73 /*
74 * This backend uses deprecated APIs for AppleTalk; we know this, so
75 * silence any warnings about it...
76 */
77
78 #ifdef MAC_OS_X_VERSION_MIN_REQUIRED
79 # undef MAC_OS_X_VERSION_MIN_REQUIRED
80 #endif /* MAX_OS_X_VERSION_MIN_REQUIRED */
81 #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_0
82
83 #include <config.h>
84
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <ctype.h>
88 #include <termios.h>
89 #include <unistd.h>
90 #include <assert.h>
91 #include <signal.h>
92
93 #include <sys/fcntl.h>
94 #include <sys/param.h>
95 #include <sys/time.h>
96 #include <sys/errno.h>
97
98 #include <cups/cups.h>
99 #include <cups/backend.h>
100 #include <cups/sidechannel.h>
101 #include <cups/i18n.h>
102
103 #include <netat/appletalk.h>
104 #include <netat/atp.h>
105 #include <netat/ddp.h>
106 #include <netat/nbp.h>
107 #include <netat/pap.h>
108
109 #include <libkern/OSByteOrder.h>
110
111 #ifdef HAVE_APPLETALK_AT_PROTO_H
112 # include <AppleTalk/at_proto.h>
113 #else
114 /* These definitions come from at_proto.h... */
115 # define ZIP_DEF_INTERFACE NULL
116 enum { RUNNING, NOTLOADED, LOADED, OTHERERROR };
117
118 extern int atp_abort(int fd, at_inet_t *dest, u_short tid);
119 extern int atp_close(int fd);
120 extern int atp_getreq(int fd, at_inet_t *src, char *buf, int *len, int *userdata,
121 int *xo, u_short *tid, u_char *bitmap, int nowait);
122 extern int atp_getresp(int fd, u_short *tid, at_resp_t *resp);
123 extern int atp_look(int fd);
124 extern int atp_open(at_socket *sock);
125 extern int atp_sendreq(int fd, at_inet_t *dest, char *buf, int len,
126 int userdata, int xo, int xo_relt, u_short *tid,
127 at_resp_t *resp, at_retry_t *retry, int nowait);
128 extern int atp_sendrsp(int fd, at_inet_t *dest, int xo, u_short tid,
129 at_resp_t *resp);
130 extern int checkATStack();
131 extern int nbp_lookup(at_entity_t *entity, at_nbptuple_t *buf, int max,
132 at_retry_t *retry);
133 extern int nbp_make_entity(at_entity_t *entity, char *obj, char *type,
134 char *zone);
135 extern int zip_getmyzone(char *ifName, at_nvestr_t *zone);
136 #endif /* HAVE_APPLETALK_AT_PROTO_H */
137
138 #include <CoreFoundation/CFURL.h>
139 #include <CoreFoundation/CFNumber.h>
140 #include <CoreFoundation/CFPreferences.h>
141
142 /* Defines */
143 #define MAX_PRINTERS 500 /* Max number of printers we can lookup */
144 #define PAP_CONNID 0
145 #define PAP_TYPE 1
146 #define PAP_EOF 2
147
148 #define CONNID_OF(p) (((u_char *)&p)[0])
149 #define TYPE_OF(p) (((u_char *)&p)[1])
150 #define SEQUENCE_NUM(p) (((u_char *)&p)[2])
151 #define IS_PAP_EOF(p) (((u_char *)&p)[2])
152
153 #ifndef true
154 #define true 1
155 #define false 0
156 #endif
157
158 /* Globals */
159 int gSockfd = 0; /* Socket descriptor */
160 at_inet_t gSessionAddr = { 0 }; /* Address of the session responding socket */
161 u_char gConnID = 0; /* PAP session connection id */
162 u_short gSendDataID = 0; /* Transaction id of pending send-data request */
163 u_short gTickleID = 0; /* Transaction id of outstanding tickle request*/
164 int gWaitEOF = false; /* Option: wait for a remote's EOF */
165 int gStatusInterval= 5; /* Option: 0=off else seconds between status requests*/
166 int gErrorlogged = false; /* If an error was logged don't send any more INFO messages */
167 int gDebug = 0; /* Option: emit debugging info */
168
169 /* Local functions */
170 static int listDevices(void);
171 static int printFile(char* name, char* type, char* zone, int fdin, int fdout,
172 int fderr, int copies, int argc);
173 static int papOpen(at_nbptuple_t* tuple, u_char* connID, int* fd,
174 at_inet_t* pap_to, u_char* flowQuantum);
175 static int papClose();
176 static int papWrite(int sockfd, at_inet_t* dest, u_short tid, u_char connID,
177 u_char flowQuantum, char* data, int len, int eof);
178 static int papCloseResp(int sockfd, at_inet_t* dest, int xo, u_short tid,
179 u_char connID);
180 static int papSendRequest(int sockfd, at_inet_t* dest, u_char connID,
181 int function, u_char bitmap, int xo, int seqno);
182 static int papCancelRequest(int sockfd, u_short tid);
183 static void sidechannel_request();
184 static void statusUpdate(char* status, u_char statusLen);
185 static int parseUri(const char* argv0, char* name, char* type, char* zone);
186 static int addPercentEscapes(const char* src, char* dst, int dstMax);
187 static int removePercentEscapes(const char* src, char* dst, int dstMax);
188 static int nbptuple_compare(const void *p1, const void *p2);
189 static int okayToUseAppleTalk(void);
190 static const char *packet_name(u_char x);
191 static int connectTimeout(void);
192 static void signalHandler(int sigraised);
193
194
195 /*!
196 * @function main
197 * @abstract Send a file to the specified AppleTalk PAP address.
198 *
199 * Usage: printer-uri job-id user title copies options [file]
200 *
201 * @param argc # of arguments
202 * @param argv array of arguments
203 *
204 * @result A non-zero return value for errors
205 */
206 int main (int argc, const char * argv[])
207 {
208 int err = 0;
209 FILE *fp; /* Print file */
210 int copies; /* Number of copies to print */
211 char name[NBP_NVE_STR_SIZE + 1]; /* +1 for a nul */
212 char type[NBP_NVE_STR_SIZE + 1]; /* +1 for a nul */
213 char zone[NBP_NVE_STR_SIZE + 1]; /* +1 for a nul */
214
215 /* Make sure status messages are not buffered... */
216 setbuf(stderr, NULL);
217
218 if (argc == 1 || (argc == 2 && strcmp(argv[1], "-discover") == 0))
219 {
220 listDevices();
221
222 return 0;
223 }
224
225 if (argc < 6 || argc > 7)
226 {
227 _cupsLangPrintf(stderr,
228 _("Usage: %s job-id user title copies options [file]\n"),
229 argv[0]);
230 return (CUPS_BACKEND_FAILED);
231 }
232
233 /* If we have 7 arguments, print the file named on the command-line.
234 * Otherwise, send stdin instead...
235 */
236 if (argc == 6)
237 {
238 fp = stdin;
239 copies = 1;
240 }
241 else
242 {
243 fprintf(stderr, "DEBUG: opening print file \"%s\"\n", argv[6]);
244
245 /* Try to open the print file... */
246 if ((fp = fopen(argv[6], "rb")) == NULL)
247 {
248 _cupsLangPrintf(stderr,
249 _("ERROR: Unable to open print file \"%s\": %s\n"),
250 argv[6], strerror(errno));
251 return (CUPS_BACKEND_FAILED);
252 }
253
254 copies = atoi(argv[4]);
255 }
256
257 /* Extract the device name and options from the URI... */
258 parseUri(cupsBackendDeviceURI((char **)argv), name, type, zone);
259
260 err = printFile(name, type, zone, fileno(fp), STDOUT_FILENO, STDERR_FILENO, copies, argc);
261
262 if (fp != stdin)
263 fclose(fp);
264
265 /* Only clear the last status if there wasn't an error */
266 if (err == noErr && !gErrorlogged)
267 fprintf(stderr, "INFO:\n");
268
269 return err;
270 }
271
272
273 /*!
274 * @function listDevices
275 * @abstract Print a list of all LaserWriter type devices registered in the default zone.
276 *
277 * @result A non-zero return value for errors
278 */
279 static int listDevices(void)
280 {
281 int err = noErr;
282 int i;
283 int numberFound;
284
285 at_nvestr_t at_zone;
286 at_entity_t entity;
287 at_nbptuple_t buf[MAX_PRINTERS];
288 at_retry_t retry;
289 char name[NBP_NVE_STR_SIZE+1];
290 char encodedName[(3 * NBP_NVE_STR_SIZE) + 1];
291 char zone[NBP_NVE_STR_SIZE+1];
292 char encodedZone[(3 * NBP_NVE_STR_SIZE) + 1];
293
294 /* Make sure it's okay to use appletalk */
295 if (!okayToUseAppleTalk())
296 {
297 _cupsLangPuts(stderr, _("INFO: AppleTalk disabled in System Preferences\n"));
298 return -1; /* Network is down */
299 }
300
301 if ((err = zip_getmyzone(ZIP_DEF_INTERFACE, &at_zone)) != 0)
302 {
303 _cupsLangPrintError(_("ERROR: Unable to get default AppleTalk zone"));
304 return -2;
305 }
306
307 memcpy(zone, at_zone.str, MIN(at_zone.len, sizeof(zone)-1));
308 zone[MIN(at_zone.len, sizeof(zone)-1)] = '\0';
309
310 _cupsLangPrintf(stderr, _("INFO: Using default AppleTalk zone \"%s\"\n"),
311 zone);
312
313 addPercentEscapes(zone, encodedZone, sizeof(encodedZone));
314
315 /* Look up all the printers in our zone */
316 nbp_make_entity(&entity, "=", "LaserWriter", zone);
317 retry.retries = 1;
318 retry.interval = 1;
319 retry.backoff = 1;
320
321 if ((numberFound = nbp_lookup(&entity, buf, MAX_PRINTERS, &retry)) < 0)
322 {
323 _cupsLangPrintError(_("ERROR: Unable to lookup AppleTalk printers"));
324 return numberFound;
325 }
326
327 if (numberFound >= MAX_PRINTERS)
328 _cupsLangPrintf(stderr,
329 _("WARNING: Adding only the first %d printers found"),
330 MAX_PRINTERS);
331
332 /* Not required but sort them so they look nice */
333 qsort(buf, numberFound, sizeof(at_nbptuple_t), nbptuple_compare);
334
335 for (i = 0; i < numberFound; i++)
336 {
337 memcpy(name, buf[i].enu_entity.object.str, MIN(buf[i].enu_entity.object.len, sizeof(name)-1));
338 name[MIN(buf[i].enu_entity.object.len, sizeof(name)-1)] = '\0';
339
340 if (addPercentEscapes(name, encodedName, sizeof(encodedName)) == 0)
341 {
342 /* Each line is of the form: "class URI "make model" "info" */
343 char make_model[128], /* Make and model */
344 *ptr;
345
346
347 if ((ptr = strchr(name, ' ')) != NULL)
348 {
349 /*
350 * If the printer name contains spaces, it is probably a make and
351 * model...
352 */
353
354 if (!strncmp(name, "ET00", 4))
355 {
356 /*
357 * Drop leading ethernet address info...
358 */
359
360 strlcpy(make_model, ptr + 1, sizeof(make_model));
361 }
362 else
363 strlcpy(make_model, name, sizeof(make_model));
364 }
365 else
366 strcpy(make_model, "Unknown");
367
368 printf("network pap://%s/%s/LaserWriter \"%s\" \"%s AppleTalk\"\n",
369 encodedZone, encodedName, make_model, name);
370 }
371 }
372 return numberFound;
373 }
374
375
376 /*!
377 * @function printFile
378 * @abstract Open a PAP session and send the data from the input socket to the printer.
379 *
380 * @param name NBP name
381 * @param zone NBP zone
382 * @param type NBP type
383 * @param fdin File descriptor to read data from
384 * @param fdout File descriptor to write printer responses to
385 * @param fderr File descriptor to write printer status to
386 * @param copies # of copies to send (in case in the converter couldn't handle this for us).
387 * @param argc # of command line arguments.
388 *
389 * @result A non-zero return value for errors
390 */
391 static int printFile(char* name, char* type, char* zone, int fdin, int fdout, int fderr, int copies, int argc)
392 {
393 int err;
394 int rc;
395 int val;
396 int len, i;
397
398 char fileBuffer[4096]; /* File buffer */
399 int fileBufferNbytes;
400 off_t fileTbytes;
401 int fileEOFRead;
402 int fileEOFSent;
403
404 char sockBuffer[4096 + 1]; /* Socket buffer with room for nul */
405 char atpReqBuf[AT_PAP_DATA_SIZE];
406 fd_set readSet;
407 int use_sidechannel; /* Use side channel? */
408
409 at_nbptuple_t tuple;
410 at_inet_t sendDataAddr;
411 at_inet_t src;
412 at_resp_t resp;
413 int userdata, xo, reqlen;
414 u_short tid;
415 u_char bitmap;
416 int maxfdp1,
417 nbp_failures = 0;
418 struct timeval timeout, *timeoutPtr;
419 u_char flowQuantum = 1;
420 u_short recvSequence = 0;
421 time_t now,
422 start_time,
423 elasped_time,
424 sleep_time,
425 connect_timeout = -1,
426 nextStatusTime = 0;
427 at_entity_t entity;
428 at_retry_t retry;
429
430 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
431 struct sigaction action; /* Actions for POSIX signals */
432 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
433
434 /*
435 * Test the side channel descriptor before calling papOpen() since it may open
436 * an unused fd 4 (a.k.a. CUPS_SC_FD)...
437 */
438
439 FD_ZERO(&readSet);
440 FD_SET(CUPS_SC_FD, &readSet);
441
442 timeout.tv_sec = 0;
443 timeout.tv_usec = 0;
444
445 if ((select(CUPS_SC_FD+1, &readSet, NULL, NULL, &timeout)) >= 0)
446 use_sidechannel = 1;
447 else
448 use_sidechannel = 0;
449
450 /* try to find our printer */
451 if ((err = nbp_make_entity(&entity, name, type, zone)) != noErr)
452 {
453 _cupsLangPrintError(_("ERROR: Unable to make AppleTalk address"));
454 goto Exit;
455 }
456
457 /*
458 * Remember when we started looking for the printer.
459 */
460
461 start_time = time(NULL);
462
463 retry.interval = 1;
464 retry.retries = 5;
465 retry.backoff = 0;
466
467 fprintf(stderr, "STATE: +connecting-to-device\n");
468
469 /* Loop forever trying to get an open session with the printer. */
470 for (;;)
471 {
472 /* Make sure it's okay to use appletalk */
473 if (okayToUseAppleTalk())
474 {
475 /* Clear this printer-state-reason in case we've set it */
476 fprintf(stderr, "STATE: -apple-appletalk-disabled-warning\n");
477
478 /* Resolve the name into an address. Returns the number found or an error */
479 if ((err = nbp_lookup(&entity, &tuple, 1, &retry)) > 0)
480 {
481 if (err > 1)
482 fprintf(stderr, "DEBUG: Found more than one printer with the name \"%s\"\n", name);
483
484 if (nbp_failures)
485 {
486 fprintf(stderr, "STATE: -apple-nbp-lookup-warning\n");
487 nbp_failures = 0;
488 }
489
490 /* Open a connection to the device */
491 if ((err = papOpen(&tuple, &gConnID, &gSockfd, &gSessionAddr, &flowQuantum)) == 0)
492 break;
493
494 _cupsLangPrintf(stderr, _("WARNING: Unable to open \"%s:%s\": %s\n"),
495 name, zone, strerror(err));
496 }
497 else
498 {
499 /* It's not unusual to have to call nbp_lookup() twice before it's sucessful... */
500 if (++nbp_failures > 2)
501 {
502 retry.interval = 2;
503 retry.retries = 3;
504 fprintf(stderr, "STATE: +apple-nbp-lookup-warning\n");
505 _cupsLangPuts(stderr, _("WARNING: Printer not responding\n"));
506 }
507 }
508 }
509 else
510 {
511 fprintf(stderr, "STATE: +apple-appletalk-disabled-warning\n");
512 _cupsLangPuts(stderr,
513 _("INFO: AppleTalk disabled in System Preferences.\n"));
514 }
515
516 elasped_time = time(NULL) - start_time;
517
518 if (connect_timeout == -1)
519 connect_timeout = connectTimeout();
520
521 if (connect_timeout && elasped_time > connect_timeout)
522 {
523 _cupsLangPuts(stderr, _("ERROR: Printer not responding\n"));
524 err = ETIMEDOUT;
525 goto Exit; /* Waiting too long... */
526 }
527 else if (elasped_time < (30 * 60))
528 sleep_time = 10; /* Waiting < 30 minutes */
529 else if (elasped_time < (24 * 60 * 60))
530 sleep_time = 30; /* Waiting < 24 hours */
531 else
532 sleep_time = 60; /* Waiting > 24 hours */
533
534 fprintf(stderr, "DEBUG: sleeping %d seconds...\n", (int)sleep_time);
535 sleep(sleep_time);
536 }
537
538 fprintf(stderr, "STATE: -connecting-to-device\n");
539
540 /*
541 * Now that we are connected to the printer ignore SIGTERM so that we
542 * can finish out any page data the driver sends (e.g. to eject the
543 * current page... if we are printing data from a file then catch the
544 * signal so we can send a PAP Close packet (otherwise you can't cancel
545 * raw jobs...)
546 */
547
548 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
549 sigset(SIGTERM, (argc < 7) ? SIG_IGN : signalHandler);
550 #elif defined(HAVE_SIGACTION)
551 memset(&action, 0, sizeof(action));
552
553 sigemptyset(&action.sa_mask);
554 action.sa_handler = (argc < 7) ? SIG_IGN : signalHandler;
555 sigaction(SIGTERM, &action, NULL);
556 #else
557 signal(SIGTERM, (argc < 7) ? SIG_IGN : signalHandler);
558
559 #ifdef DEBUG
560 /* Makes debugging easier; otherwise printer will be busy for several minutes */
561 signal(SIGINT, signalHandler);
562 #endif /* DEBUG */
563
564 #endif /* HAVE_SIGSET */
565
566 _cupsLangPuts(stderr, _("INFO: Sending data\n"));
567
568 sendDataAddr = tuple.enu_addr;
569
570 /* Start the tickle packets and set a timeout alarm */
571 if ((err = papSendRequest(gSockfd, &gSessionAddr, gConnID, AT_PAP_TYPE_TICKLE, 0, false, false)) < 0)
572 {
573 _cupsLangPrintError(_("ERROR: Unable to send PAP tickle request"));
574 goto Exit;
575 }
576 signal(SIGALRM, signalHandler);
577 alarm(PAP_TIMEOUT);
578
579 /* Prime the pump with an initial send-data packet */
580 if ((err = papSendRequest(gSockfd, &gSessionAddr, gConnID, AT_PAP_TYPE_SEND_DATA, 0xFF, true, true)) < 0)
581 {
582 _cupsLangPrintError(_("ERROR: Unable to send initial PAP send data request"));
583 goto Exit;
584 }
585
586 /* Set non-blocking mode on our data source descriptor */
587 val = fcntl(fdin, F_GETFL, 0);
588 fcntl(fdin, F_SETFL, val | O_NONBLOCK);
589
590 fileBufferNbytes = 0;
591 fileTbytes = 0;
592 fileEOFRead = fileEOFSent = false;
593
594 maxfdp1 = MAX(fdin, gSockfd) + 1;
595
596 if (use_sidechannel && CUPS_SC_FD >= maxfdp1)
597 maxfdp1 = CUPS_SC_FD + 1;
598
599 if (gStatusInterval != 0)
600 {
601 timeout.tv_usec = 0;
602 nextStatusTime = time(NULL) + gStatusInterval;
603 timeoutPtr = &timeout;
604 }
605 else
606 timeoutPtr = NULL;
607
608
609 for (;;)
610 {
611 /* Set up our descriptors for the select */
612 FD_ZERO(&readSet);
613 FD_SET(gSockfd, &readSet);
614
615 if (fileBufferNbytes == 0 && fileEOFRead == false)
616 FD_SET(fdin, &readSet);
617
618 if (use_sidechannel)
619 FD_SET(CUPS_SC_FD, &readSet);
620
621 /* Set the select timeout value based on the next status interval */
622 if (gStatusInterval != 0)
623 {
624 now = time(NULL);
625 timeout.tv_sec = (nextStatusTime > now) ? nextStatusTime - now : 1;
626 }
627
628 /* Wait here for something interesting to happen */
629 if ((err = select(maxfdp1, &readSet, 0, 0, timeoutPtr)) < 0)
630 {
631 _cupsLangPrintError(_("ERROR: select() failed"));
632 break;
633 }
634
635 if (err == 0 || (gStatusInterval != 0 && time(NULL) >= nextStatusTime))
636 {
637 /* Time to send a status request */
638 if ((err = papSendRequest(gSockfd, &tuple.enu_addr, 0, AT_PAP_TYPE_SEND_STATUS, 0x01, false, false)) < 0)
639 _cupsLangPrintError(_("WARNING: Unable to send PAP status request"));
640
641 if (gStatusInterval)
642 nextStatusTime = time(NULL) + gStatusInterval;
643 }
644
645 /*
646 * Check if we have a side-channel request ready...
647 */
648
649 if (use_sidechannel && FD_ISSET(CUPS_SC_FD, &readSet))
650 sidechannel_request();
651
652 /* Was there an event on the input stream? */
653 if (FD_ISSET(fdin, &readSet))
654 {
655 FD_CLR(fdin, &readSet);
656
657 assert(fileBufferNbytes == 0);
658 fileBufferNbytes = read(fdin, fileBuffer, MIN(sizeof(fileBuffer), AT_PAP_DATA_SIZE * flowQuantum));
659 if (fileBufferNbytes == 0)
660 fileEOFRead = true;
661
662 if (fileEOFSent == false && fileBufferNbytes >= 0 && gSendDataID != 0)
663 {
664 fprintf(stderr, "DEBUG: -> PAP_DATA %d bytes %s\n", fileBufferNbytes, fileEOFRead ? "with EOF" : "");
665 papWrite(gSockfd, &sendDataAddr, gSendDataID, gConnID, flowQuantum, fileBuffer, fileBufferNbytes, fileEOFRead);
666
667 fileTbytes += fileBufferNbytes;
668 if (argc > 6 && !gErrorlogged)
669 fprintf(stderr, "DEBUG: Sending print file, %qd bytes\n", (off_t)fileTbytes);
670
671 fileBufferNbytes = 0;
672 gSendDataID = 0;
673 if (fileEOFRead)
674 {
675 fileEOFSent = true;
676 if (gWaitEOF == false || fileTbytes == 0)
677 {
678 err = 0;
679 goto Exit;
680 }
681 }
682 }
683 }
684
685 /* Was there an event on the output stream? */
686 if (FD_ISSET(gSockfd, &readSet))
687 {
688 if ((rc = atp_look(gSockfd)) < 0)
689 {
690 _cupsLangPrintError(_("ERROR: Unable to look for PAP response"));
691 break;
692 }
693
694 if (rc > 0)
695 {
696 /* It's an ATP response */
697 resp.resp[0].iov_base = sockBuffer;
698 resp.resp[0].iov_len = sizeof(sockBuffer) - 1;
699 resp.bitmap = 0x01;
700
701 if ((err = atp_getresp(gSockfd, &tid, &resp)) < 0)
702 {
703 _cupsLangPrintError(_("ERROR: Unable to get PAP response"));
704 break;
705 }
706 userdata = resp.userdata[0];
707 }
708 else
709 {
710 /* It's an ATP request */
711 reqlen = sizeof(atpReqBuf);
712 if ((err = atp_getreq(gSockfd, &src, atpReqBuf, &reqlen, &userdata, &xo, &tid, &bitmap, 0)) < 0)
713 {
714 _cupsLangPrintError(_("ERROR: Unable to get PAP request"));
715 break;
716 }
717 }
718
719 fprintf(stderr, "DEBUG: <- %s\n", packet_name(TYPE_OF(userdata)));
720
721 switch (TYPE_OF(userdata))
722 {
723 case AT_PAP_TYPE_SEND_STS_REPLY: /* Send-Status-Reply packet */
724 if (resp.bitmap & 1)
725 {
726 char *iov_base = (char *)resp.resp[0].iov_base;
727 statusUpdate(&iov_base[5], iov_base[4]);
728 }
729 break;
730
731 case AT_PAP_TYPE_SEND_DATA: /* Send-Data packet */
732 sendDataAddr.socket = src.socket;
733 gSendDataID = tid;
734 recvSequence = OSReadBigInt16(&SEQUENCE_NUM(userdata), 0);
735
736 if ((fileBufferNbytes > 0 || fileEOFRead) && fileEOFSent == false)
737 {
738 fprintf(stderr, "DEBUG: -> PAP_DATA %d bytes %s\n", fileBufferNbytes, fileEOFRead ? "with EOF" : "");
739 papWrite(gSockfd, &sendDataAddr, gSendDataID, gConnID, flowQuantum, fileBuffer, fileBufferNbytes, fileEOFRead);
740
741 fileTbytes += fileBufferNbytes;
742 if (argc > 6 && !gErrorlogged)
743 fprintf(stderr, "DEBUG: Sending print file, %qd bytes\n", (off_t)fileTbytes);
744
745 fileBufferNbytes = 0;
746 gSendDataID = 0;
747 if (fileEOFRead)
748 {
749 fileEOFSent = true;
750 if (gWaitEOF == false)
751 {
752 err = 0;
753 goto Exit;
754 }
755 }
756 }
757 break;
758
759 case AT_PAP_TYPE_DATA: /* Data packet */
760 for (len=0, i=0; i < ATP_TRESP_MAX; i++)
761 {
762 if (resp.bitmap & (1 << i))
763 len += resp.resp[i].iov_len;
764 }
765
766 fprintf(stderr, "DEBUG: <- PAP_DATA %d bytes %s\n", len, IS_PAP_EOF(userdata) ? "with EOF" : "");
767
768 if (len > 0)
769 {
770 char *pLineBegin, *pCommentEnd, *pChar;
771 char *logLevel;
772 char logstr[512];
773 int logstrlen;
774
775 cupsBackChannelWrite(sockBuffer, len, 1.0);
776
777 sockBuffer[len] = '\0'; /* We always reserve room for the nul so we can use strstr() below*/
778 pLineBegin = sockBuffer;
779
780 /* If there are PostScript status comments in the buffer log them.
781 *
782 * This logic shouldn't be in the backend but until we get backchannel
783 * data in CUPS 1.2 it has to live here.
784 */
785 while (pLineBegin < sockBuffer + len &&
786 (pLineBegin = strstr(pLineBegin, "%%[")) != NULL &&
787 (pCommentEnd = strstr(pLineBegin, "]%%")) != NULL)
788 {
789 pCommentEnd += 3; /* Skip past "]%%" */
790 *pCommentEnd = '\0'; /* There's always room for the nul */
791
792 /* Strip the CRs & LFs before writing it to stderr */
793 for (pChar = pLineBegin; pChar < pCommentEnd; pChar++)
794 if (*pChar == '\r' || *pChar == '\n')
795 *pChar = ' ';
796
797 if (strncasecmp(pLineBegin, "%%[ Error:", 10) == 0)
798 {
799 /* logLevel should be "ERROR" here but this causes PrintCenter
800 * to pause the queue which in turn clears this error, which
801 * restarts the job. So the job ends up in an infinite loop with
802 * the queue being held/un-held. Just make it DEBUG for now until
803 * we fix notifications later.
804 */
805 logLevel = "DEBUG";
806 gErrorlogged = true;
807 }
808 else if (strncasecmp(pLineBegin, "%%[ Flushing", 12) == 0)
809 logLevel = "DEBUG";
810 else
811 logLevel = "INFO";
812
813 if ((logstrlen = snprintf(logstr, sizeof(logstr), "%s: %s\n", logLevel, pLineBegin)) >= sizeof(logstr))
814 {
815 /* If the string was trucnated make sure it has a linefeed before the nul */
816 logstrlen = sizeof(logstr) - 1;
817 logstr[logstrlen - 1] = '\n';
818 }
819
820 write(fderr, logstr, logstrlen);
821
822 pLineBegin = pCommentEnd + 1;
823 }
824 }
825
826 if (IS_PAP_EOF(userdata) != 0)
827 {
828 /* If this is EOF then were we expecting it? */
829 if (fileEOFSent == true)
830 goto Exit;
831 else
832 {
833 _cupsLangPuts(stderr, _("WARNING: Printer sent unexpected EOF\n"));
834 }
835 }
836
837 if ((err = papSendRequest(gSockfd, &gSessionAddr, gConnID, AT_PAP_TYPE_SEND_DATA, 0xFF, true, true)) < 0)
838 {
839 _cupsLangPrintf(stderr,
840 _("ERROR: Error %d sending PAPSendData request: %s\n"),
841 err, strerror(errno));
842 goto Exit;
843 }
844 break;
845
846 case AT_PAP_TYPE_TICKLE: /* Tickle packet */
847 break;
848
849 case AT_PAP_TYPE_CLOSE_CONN: /* Close-Connection packet */
850 /* We shouldn't normally see this. */
851 papCloseResp(gSockfd, &gSessionAddr, xo, tid, gConnID);
852
853 /* If this is EOF then were we expecting it? */
854 if (fileEOFSent == true)
855 {
856 _cupsLangPuts(stderr, _("WARNING: Printer sent unexpected EOF\n"));
857 }
858 else
859 {
860 _cupsLangPuts(stderr, _("ERROR: Printer sent unexpected EOF\n"));
861 }
862 goto Exit;
863 break;
864
865 case AT_PAP_TYPE_OPEN_CONN: /* Open-Connection packet */
866 case AT_PAP_TYPE_OPEN_CONN_REPLY: /* Open-Connection-Reply packet */
867 case AT_PAP_TYPE_SEND_STATUS: /* Send-Status packet */
868 case AT_PAP_TYPE_CLOSE_CONN_REPLY: /* Close-Connection-Reply packet */
869 _cupsLangPrintf(stderr, _("WARNING: Unexpected PAP packet of type %d\n"),
870 TYPE_OF(userdata));
871 break;
872
873 default:
874 _cupsLangPrintf(stderr, _("WARNING: Unknown PAP packet of type %d\n"),
875 TYPE_OF(userdata));
876 break;
877 }
878
879 if (CONNID_OF(userdata) == gConnID)
880 {
881 /* Reset tickle timer */
882 alarm(0);
883 alarm(PAP_TIMEOUT);
884 }
885 }
886 }
887
888 Exit:
889 /*
890 * Close the socket and return...
891 */
892 papClose();
893
894 return err;
895 }
896
897
898 #pragma mark -
899 /*!
900 * @function papOpen
901 * @abstract Open a pap session to a printer.
902 *
903 * @param tuple nbp address of printer
904 * @param connID returned pap connection id
905 * @param fd returned socket descriptor
906 * @param sessionAddr returned session address
907 * @param flowQuantum returned flow quantum (usually 8)
908 *
909 * @result A non-zero return value for errors
910 */
911 static int papOpen(at_nbptuple_t* tuple, u_char* connID, int* fd,
912 at_inet_t* sessionAddr, u_char* flowQuantum)
913 {
914 int result,
915 open_result,
916 userdata,
917 atp_err;
918 time_t tm,
919 waitTime;
920 char data[10],
921 rdata[ATP_DATA_SIZE];
922 u_char *puserdata;
923 at_socket socketfd;
924 at_resp_t resp;
925 at_retry_t retry;
926
927 result = 0;
928 socketfd = 0;
929 puserdata = (u_char *)&userdata;
930
931 _cupsLangPuts(stderr, _("INFO: Opening connection\n"));
932
933 if ((*fd = atp_open(&socketfd)) < 0)
934 return -1;
935
936 /*
937 * Build the open connection request packet.
938 */
939
940 tm = time(NULL);
941 srand(tm);
942
943 *connID = (rand()&0xff) | 0x01;
944 puserdata[0] = *connID;
945 puserdata[1] = AT_PAP_TYPE_OPEN_CONN;
946 puserdata[2] = 0;
947 puserdata[3] = 0;
948
949 retry.interval = 2;
950 retry.retries = 5;
951
952 resp.bitmap = 0x01;
953 resp.resp[0].iov_base = rdata;
954 resp.resp[0].iov_len = sizeof(rdata);
955
956 data[0] = socketfd;
957 data[1] = 8;
958
959 for (;;)
960 {
961 waitTime = time(NULL) - tm;
962 OSWriteBigInt16(&data[2], 0, (u_short)waitTime);
963
964 fprintf(stderr, "DEBUG: -> %s\n", packet_name(AT_PAP_TYPE_OPEN_CONN));
965
966 if ((atp_err = atp_sendreq(*fd, &tuple->enu_addr, data, 4, userdata, 1, 0,
967 0, &resp, &retry, 0)) < 0)
968 {
969 statusUpdate("Destination unreachable", 23);
970 result = EHOSTUNREACH;
971 break;
972 }
973
974 puserdata = (u_char *)&resp.userdata[0];
975 open_result = OSReadBigInt16(&rdata[2], 0);
976
977 fprintf(stderr, "DEBUG: <- %s, status %d\n", packet_name(puserdata[1]),
978 open_result);
979
980 /*
981 * Just for the sake of our sanity check the other fields in the packet
982 */
983
984 if (puserdata[1] != AT_PAP_TYPE_OPEN_CONN_REPLY ||
985 (open_result == 0 && (puserdata[0] & 0xff) != *connID))
986 {
987 result = EINVAL;
988 break;
989 }
990
991 statusUpdate(&rdata[5], rdata[4] & 0xff);
992
993 /*
994 * if the connection established okay exit from the loop
995 */
996
997 if (open_result == 0)
998 break;
999
1000 sleep(1);
1001 }
1002
1003 if (result == 0)
1004 {
1005 /* Update the session address
1006 */
1007 sessionAddr->net = tuple->enu_addr.net;
1008 sessionAddr->node = tuple->enu_addr.node;
1009 sessionAddr->socket = rdata[0];
1010 *flowQuantum = rdata[1];
1011 }
1012 else
1013 {
1014 atp_close(*fd);
1015 *fd = 0;
1016 sleep(1);
1017 }
1018
1019 return result;
1020 }
1021
1022
1023 /*!
1024 * @function papClose
1025 * @abstract End a PAP session by canceling outstanding send-data & tickle
1026 * transactions and sending a PAP close request.
1027 *
1028 * @result A non-zero return value for errors
1029 */
1030 static int papClose()
1031 {
1032 int fd;
1033 u_short tmpID;
1034 int result;
1035 unsigned char rdata[ATP_DATA_SIZE];
1036 int userdata;
1037 u_char *puserdata = (u_char *)&userdata;
1038 at_resp_t resp;
1039 at_retry_t retry;
1040
1041 if (gSockfd != 0)
1042 {
1043 fd = gSockfd;
1044 gSockfd = 0;
1045
1046 alarm(0);
1047
1048 /* Cancel the pending send-data and tickle trnsactions
1049 */
1050 if (gSendDataID)
1051 {
1052 tmpID = gSendDataID;
1053 gSendDataID = 0;
1054 papCancelRequest(fd, tmpID);
1055 }
1056
1057 if (gTickleID)
1058 {
1059 tmpID = gTickleID;
1060 gTickleID = 0;
1061 papCancelRequest(fd, tmpID);
1062 }
1063
1064 /* This is a workaround for bug #2735145. The problem is papWrite()
1065 * returns before the ATP TRel arrives for it. If we send the pap close packet
1066 * before this release then the printer can drop the last data packets.
1067 * The effect on an Epson printer is the last page doesn't print, on HP it
1068 * doesn't close the pap session.
1069 */
1070 if (gWaitEOF == false)
1071 sleep(2);
1072
1073 fprintf(stderr, "DEBUG: -> %s\n", packet_name(AT_PAP_TYPE_CLOSE_CONN));
1074
1075 puserdata[0] = gConnID;
1076 puserdata[1] = AT_PAP_TYPE_CLOSE_CONN;
1077 puserdata[2] = 0;
1078 puserdata[3] = 0;
1079
1080 retry.interval = 2;
1081 retry.retries = 5;
1082
1083 resp.bitmap = 0x01;
1084 resp.resp[0].iov_base = rdata;
1085 resp.resp[0].iov_len = sizeof(rdata);
1086
1087 result = atp_sendreq(fd, &gSessionAddr, 0, 0, userdata, 1, 0, 0, &resp, &retry, 0);
1088
1089 result = close(fd);
1090 }
1091 return noErr;
1092 }
1093
1094
1095 /*!
1096 * @function papWrite
1097 * @abstract Write bytes to a printer.
1098 *
1099 * @param sockfd socket descriptor
1100 * @param dest destination address
1101 * @param tid transaction id
1102 * @param connID connection id
1103 * @param flowQuantum returned flow quantum (usually 8)
1104 * @param data pointer to the data
1105 * @param len number of bytes to send
1106 * @param eof pap eof flag
1107 *
1108 * @result A non-zero return value for errors
1109 */
1110 static int papWrite(int sockfd, at_inet_t* dest, u_short tid, u_char connID, u_char flowQuantum, char* data, int len, int eof)
1111 {
1112 int result;
1113 int i;
1114 u_char* puserdata;
1115 at_resp_t resp;
1116
1117 /* fprintf(stderr, "DEBUG: papWrite(%d%s) to %d,%d,%d; %d\n", len, eof ? " EOF":"", dest->net, dest->node, dest->socket, connID); */
1118
1119 if (len > AT_PAP_DATA_SIZE * flowQuantum)
1120 {
1121 fprintf(stderr, "DEBUG: papWrite() len of %d is too big!\n", len);
1122 errno = E2BIG;
1123 return -1;
1124 }
1125
1126 /*
1127 * Break up the outgoing data into a set of
1128 * response packets to reply to an incoming
1129 * PAP 'SENDDATA' request
1130 */
1131 for (i = 0; i < flowQuantum; i++)
1132 {
1133 resp.userdata[i] = 0;
1134 puserdata = (u_char *)&resp.userdata[i];
1135
1136 puserdata[PAP_CONNID] = connID;
1137 puserdata[PAP_TYPE] = AT_PAP_TYPE_DATA;
1138 puserdata[PAP_EOF] = eof ? 1 : 0;
1139
1140 resp.resp[i].iov_base = (caddr_t)data;
1141
1142 if (data)
1143 data += AT_PAP_DATA_SIZE;
1144
1145 resp.resp[i].iov_len = MIN((int)len, (int)AT_PAP_DATA_SIZE);
1146 len -= resp.resp[i].iov_len;
1147 if (len == 0)
1148 break;
1149 }
1150 resp.bitmap = (1 << (i + 1)) - 1;
1151
1152 /*
1153 * Write out the data as a PAP 'DATA' response
1154 */
1155 errno = 0;
1156 if ((result = atp_sendrsp(sockfd, dest, true, tid, &resp)) < 0)
1157 {
1158 fprintf(stderr, "DEBUG: atp_sendrsp() returns %d, errno %d \"%s\"\n", result, errno, strerror(errno));
1159 return -1;
1160 }
1161 return(0);
1162 }
1163
1164
1165 /*!
1166 * @function papCloseResp
1167 * @abstract Send a pap close response in the rare case we receive a close connection request.
1168 *
1169 * @param sockfd socket descriptor
1170 * @param dest destination address
1171 * @param tid transaction id
1172 * @param connID connection id
1173 *
1174 * @result A non-zero return value for errors
1175 */
1176 static int papCloseResp(int sockfd, at_inet_t* dest, int xo, u_short tid, u_char connID)
1177 {
1178 int result;
1179 at_resp_t resp;
1180
1181 resp.bitmap = 1;
1182 resp.userdata[0] = 0;
1183
1184 ((u_char*)&resp.userdata[0])[PAP_CONNID] = connID;
1185 ((u_char*)&resp.userdata[0])[PAP_TYPE] = AT_PAP_TYPE_CLOSE_CONN_REPLY;
1186
1187 resp.resp[0].iov_base = NULL;
1188 resp.resp[0].iov_len = 0;
1189
1190 if ((result = atp_sendrsp(sockfd, dest, xo, tid, &resp)) < 0)
1191 {
1192 fprintf(stderr, "DEBUG: atp_sendrsp() returns %d, errno %d \"%s\"\n", result, errno, strerror(errno));
1193 return -1;
1194 }
1195 return 0;
1196 }
1197
1198
1199 /*!
1200 * @function papSendRequest
1201 * @abstract Send a pap close response in the rare case we receive a close connection request.
1202 *
1203 * @param sockfd socket descriptor
1204 * @param dest destination address
1205 * @param function pap function
1206 * @param bitmap bitmap
1207 * @param xo exactly once
1208 * @param seqno sequence number
1209 *
1210 * @result A non-zero return value for errors
1211 */
1212 static int papSendRequest(int sockfd, at_inet_t* dest, u_char connID, int function, u_char bitmap, int xo, int seqno)
1213 {
1214 u_short tid;
1215 int err;
1216 sigset_t sv, osv;
1217 int userdata;
1218 u_char *puserdata = (u_char *)&userdata;
1219 at_retry_t retry;
1220 at_resp_t resp;
1221 static u_short pap_send_count = 0;
1222
1223 fprintf(stderr, "DEBUG: -> %s\n", packet_name(function));
1224
1225 puserdata[0] = connID;
1226 puserdata[1] = function;
1227 resp.bitmap = bitmap;
1228 retry.interval = 10;
1229 retry.retries = -1; /* was ATP_INFINITE_RETRIES */
1230 if (seqno)
1231 {
1232 pap_send_count++;
1233 if (pap_send_count == 0)
1234 pap_send_count = 1;
1235
1236 OSWriteBigInt16(&puserdata[2], 0, pap_send_count);
1237 }
1238 else
1239 OSWriteBigInt16(&puserdata[2], 0, 0);
1240
1241 sigemptyset(&sv);
1242 sigaddset(&sv, SIGIO);
1243 sigprocmask(SIG_SETMASK, &sv, &osv);
1244
1245 err = atp_sendreq(sockfd, dest, 0, 0, userdata, xo, 0, &tid, &resp, &retry, 1);
1246
1247 sigprocmask(SIG_SETMASK, &osv, NULL);
1248
1249 return err;
1250 }
1251
1252
1253 /*!
1254 * @function papCancelRequest
1255 * @abstract Cancel a pending pap request.
1256 *
1257 * @param sockfd socket descriptor
1258 * @param tid transaction ID
1259 *
1260 * @result A non-zero return value for errors
1261 */
1262 int papCancelRequest(int sockfd, u_short tid)
1263 {
1264 sigset_t sv, osv;
1265
1266 sigemptyset(&sv);
1267 sigaddset(&sv, SIGIO);
1268 sigprocmask(SIG_SETMASK, &sv, &osv);
1269
1270 if (atp_abort(sockfd, NULL, tid) < 0)
1271 {
1272 sigprocmask(SIG_SETMASK, &osv, NULL);
1273 return -1;
1274 }
1275 sigprocmask(SIG_SETMASK, &osv, NULL);
1276
1277 return 0;
1278 }
1279
1280
1281 /*
1282 * 'sidechannel_request()' - Handle side-channel requests.
1283 */
1284
1285 static void
1286 sidechannel_request()
1287 {
1288 cups_sc_command_t command; /* Request command */
1289 cups_sc_status_t status; /* Request/response status */
1290 char data[2048]; /* Request/response data */
1291 int datalen; /* Request/response data size */
1292
1293 datalen = sizeof(data);
1294
1295 if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
1296 {
1297 fputs(_("WARNING: Failed to read side-channel request!\n"), stderr);
1298 return;
1299 }
1300
1301 switch (command)
1302 {
1303 case CUPS_SC_CMD_GET_BIDI: /* Is the connection bidirectional? */
1304 data[0] = 1;
1305 cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
1306 break;
1307
1308 case CUPS_SC_CMD_GET_STATE: /* Return device state */
1309 data[0] = CUPS_SC_STATE_ONLINE;
1310 cupsSideChannelWrite(command, CUPS_SC_STATUS_OK, data, 1, 1.0);
1311 break;
1312
1313 case CUPS_SC_CMD_DRAIN_OUTPUT: /* Drain all pending output */
1314 case CUPS_SC_CMD_SOFT_RESET: /* Do a soft reset */
1315 case CUPS_SC_CMD_GET_DEVICE_ID: /* Return IEEE-1284 device ID */
1316 default:
1317 cupsSideChannelWrite(command, CUPS_SC_STATUS_NOT_IMPLEMENTED,
1318 NULL, 0, 1.0);
1319 break;
1320 }
1321 return;
1322 }
1323
1324
1325 #pragma mark -
1326 /*!
1327 * @function statusUpdate
1328 * @abstract Format and print a PAP status response to stderr.
1329 *
1330 * @param status The status response string
1331 * @param statusLen The length of the status response string
1332 */
1333 void statusUpdate(char* status, u_char statusLen)
1334 {
1335 static char status_str[255];
1336 static u_char last_statusLen = 0xFF;
1337
1338 /* Only send this if the status has changed */
1339 if (statusLen != last_statusLen || memcmp(status, status_str, statusLen) != 0)
1340 {
1341 if (statusLen > sizeof(status_str)-1)
1342 statusLen = sizeof(status_str)-1;
1343 last_statusLen = statusLen;
1344 memcpy(status_str, status, statusLen);
1345 status_str[(int)statusLen] = '\0';
1346
1347 /*
1348 * Make sure the status string is in the form of a PostScript comment.
1349 */
1350
1351 if (statusLen > 3 && memcmp(status, "%%[", 3) == 0)
1352 fprintf(stderr, "INFO: %s\n", status_str);
1353 else
1354 fprintf(stderr, "INFO: %%%%[ %s ]%%%%\n", status_str);
1355 }
1356 return;
1357 }
1358
1359
1360 /*!
1361 * @function parseUri
1362 * @abstract Parse a PAP URI into it's NBP components.
1363 *
1364 * @param argv0 The PAP URI to parse
1365 * @param name NBP name
1366 * @param zone NBP zone
1367 * @param type NBP type
1368 *
1369 * @result A non-zero return value for errors
1370 */
1371 static int parseUri(const char* argv0, char* name, char* type, char* zone)
1372 {
1373 char method[255], /* Method in URI */
1374 hostname[1024], /* Hostname */
1375 username[255], /* Username info (not used) */
1376 resource[1024], /* Resource info (device and options) */
1377 *resourcePtr,
1378 *typePtr,
1379 *options, /* Pointer to options */
1380 *optionName, /* Name of option */
1381 *value, /* Value of option */
1382 sep; /* Separator character */
1383 int port; /* Port number (not used) */
1384 int statusInterval; /* */
1385
1386 /*
1387 * Extract the device name and options from the URI...
1388 */
1389 method[0] = username[0] = hostname[0] = resource[0] = '\0';
1390 port = 0;
1391
1392 httpSeparateURI(HTTP_URI_CODING_NONE, argv0, method, sizeof(method),
1393 username, sizeof(username),
1394 hostname, sizeof(hostname), &port,
1395 resource, sizeof(resource));
1396
1397 /*
1398 * See if there are any options...
1399 */
1400 if ((options = strchr(resource, '?')) != NULL)
1401 {
1402 /*
1403 * Yup, terminate the device name string and move to the first
1404 * character of the options...
1405 */
1406 *options++ = '\0';
1407
1408 while (*options != '\0')
1409 {
1410 /*
1411 * Get the name...
1412 */
1413
1414 optionName = options;
1415
1416 while (*options && *options != '=' && *options != '+' && *options != '&')
1417 options ++;
1418
1419 if ((sep = *options) != '\0')
1420 *options++ = '\0';
1421
1422 if (sep == '=')
1423 {
1424 /*
1425 * Get the value...
1426 */
1427
1428 value = options;
1429
1430 while (*options && *options != '+' && *options != '&')
1431 options ++;
1432
1433 if (*options)
1434 *options++ = '\0';
1435 }
1436 else
1437 value = (char *)"";
1438
1439 /*
1440 * Process the option...
1441 */
1442
1443 if (!strcasecmp(optionName, "waiteof"))
1444 {
1445 /*
1446 * Wait for the end of the print file?
1447 */
1448
1449 if (!strcasecmp(value, "on") ||
1450 !strcasecmp(value, "yes") ||
1451 !strcasecmp(value, "true"))
1452 {
1453 gWaitEOF = true;
1454 }
1455 else if (!strcasecmp(value, "off") ||
1456 !strcasecmp(value, "no") ||
1457 !strcasecmp(value, "false"))
1458 {
1459 gWaitEOF = false;
1460 }
1461 else
1462 {
1463 _cupsLangPrintf(stderr,
1464 _("WARNING: Boolean expected for waiteof option \"%s\"\n"),
1465 value);
1466 }
1467 }
1468 else if (!strcasecmp(optionName, "status"))
1469 {
1470 /*
1471 * Set status reporting interval...
1472 */
1473
1474 statusInterval = atoi(value);
1475 if (value[0] < '0' || value[0] > '9' || statusInterval < 0)
1476 {
1477 _cupsLangPrintf(stderr,
1478 _("WARNING: number expected for status option \"%s\"\n"),
1479 value);
1480 }
1481 else
1482 {
1483 gStatusInterval = statusInterval;
1484 }
1485 }
1486 }
1487 }
1488
1489 resourcePtr = resource;
1490
1491 if (*resourcePtr == '/')
1492 resourcePtr++;
1493
1494 /* If the resource has a slash we assume the slash seperates the AppleTalk object
1495 * name from the AppleTalk type. If the slash is not present we assume the AppleTalk
1496 * type is LaserWriter.
1497 */
1498
1499 typePtr = strchr(resourcePtr, '/');
1500 if (typePtr != NULL)
1501 {
1502 *typePtr++ = '\0';
1503 }
1504 else
1505 {
1506 typePtr = "LaserWriter";
1507 }
1508
1509 removePercentEscapes(hostname, zone, NBP_NVE_STR_SIZE + 1);
1510 removePercentEscapes(resourcePtr, name, NBP_NVE_STR_SIZE + 1);
1511 removePercentEscapes(typePtr, type, NBP_NVE_STR_SIZE + 1);
1512
1513 return 0;
1514 }
1515
1516
1517 /*!
1518 * @function addPercentEscapes
1519 * @abstract Encode a string with percent escapes
1520 *
1521 * @param src The source C string
1522 * @param dst Desination buffer
1523 * @param dstMax Size of desination buffer
1524 *
1525 * @result A non-zero return value for errors
1526 */
1527 static int addPercentEscapes(const char* src, char* dst, int dstMax)
1528 {
1529 char c;
1530 char *dstEnd = dst + dstMax - 1; /* -1 to leave room for the NUL */
1531
1532 while (*src)
1533 {
1534 c = *src++;
1535
1536 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1537 (c >= '0' && c <= '9') || (c == '.' || c == '-' || c == '*' || c == '_'))
1538 {
1539 if (dst >= dstEnd)
1540 return -1;
1541
1542 *dst++ = c;
1543 }
1544 else
1545 {
1546 if (dst >= dstEnd - 2)
1547 return -1;
1548
1549 snprintf(dst, dstEnd - dst, "%%%02x", c);
1550 dst += 3;
1551 }
1552 }
1553
1554 *dst = '\0';
1555 return 0;
1556 }
1557
1558
1559 /*!
1560 * @function removePercentEscapes
1561 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1562 *
1563 * @param src Source buffer
1564 * @param srclen Number of bytes in source buffer
1565 * @param dst Desination buffer
1566 * @param dstMax Size of desination buffer
1567 *
1568 * @result A non-zero return value for errors
1569 */
1570 static int removePercentEscapes(const char* src, char* dst, int dstMax)
1571 {
1572 int c;
1573 const char *dstEnd = dst + dstMax;
1574
1575 while (*src && dst < dstEnd)
1576 {
1577 c = *src++;
1578
1579 if (c == '%')
1580 {
1581 sscanf(src, "%02x", &c);
1582 src += 2;
1583 }
1584 *dst++ = (char)c;
1585 }
1586
1587 if (dst >= dstEnd)
1588 return -1;
1589
1590 *dst = '\0';
1591 return 0;
1592 }
1593
1594
1595 /*!
1596 * @function nbptuple_compare
1597 * @abstract An NBP comparator for qsort.
1598 *
1599 * @result p1<p2: -1, p1=p2: 0, p1>p2: 1
1600 */
1601 int nbptuple_compare(const void *p1, const void *p2)
1602 {
1603 int result;
1604 int len = MIN(((at_nbptuple_t*)p1)->enu_entity.object.len,
1605 ((at_nbptuple_t*)p2)->enu_entity.object.len);
1606
1607 if ((result = memcmp(((at_nbptuple_t*)p1)->enu_entity.object.str, ((at_nbptuple_t*)p2)->enu_entity.object.str, len)) == 0)
1608 {
1609 if (((at_nbptuple_t*)p1)->enu_entity.object.len < ((at_nbptuple_t*)p2)->enu_entity.object.len)
1610 result = -1;
1611 else if (((at_nbptuple_t*)p1)->enu_entity.object.len > ((at_nbptuple_t*)p2)->enu_entity.object.len)
1612 result = 1;
1613 else
1614 result = 0;
1615 }
1616 return result;
1617 }
1618
1619
1620 /*!
1621 * @function okayToUseAppleTalk
1622 * @abstract Returns true if AppleTalk is available and enabled.
1623 *
1624 * @result non-zero if AppleTalk is enabled
1625 */
1626 static int okayToUseAppleTalk()
1627 {
1628 int atStatus = checkATStack();
1629
1630 /* I think the test should be:
1631 * return atStatus == RUNNING || atStatus == LOADED;
1632 * but when I disable AppleTalk from the network control panel and
1633 * reboot, AppleTalk shows up as loaded. The test empirically becomes
1634 * the following:
1635 */
1636 return atStatus == RUNNING;
1637 }
1638
1639
1640 /*!
1641 * @function packet_name
1642 * @abstract Returns packet name string.
1643 *
1644 * @result A string
1645 */
1646 static const char *packet_name(u_char x)
1647 {
1648 switch (x)
1649 {
1650 case AT_PAP_TYPE_OPEN_CONN: return "PAP_OPEN_CONN";
1651 case AT_PAP_TYPE_OPEN_CONN_REPLY: return "PAP_OPEN_CONN_REPLY";
1652 case AT_PAP_TYPE_SEND_DATA: return "PAP_SEND_DATA";
1653 case AT_PAP_TYPE_DATA: return "PAP_DATA";
1654 case AT_PAP_TYPE_TICKLE: return "PAP_TICKLE";
1655 case AT_PAP_TYPE_CLOSE_CONN: return "PAP_CLOSE_CONN";
1656 case AT_PAP_TYPE_CLOSE_CONN_REPLY: return "PAP_CLOSE_CONN_REPLY";
1657 case AT_PAP_TYPE_SEND_STATUS: return "PAP_SEND_STATUS";
1658 case AT_PAP_TYPE_SEND_STS_REPLY: return "PAP_SEND_STS_REPLY";
1659 case AT_PAP_TYPE_READ_LW: return "PAP_READ_LW";
1660 }
1661 return "<Unknown>";
1662 }
1663
1664
1665 /*!
1666 * @function connectTimeout
1667 * @abstract Returns the connect timeout preference value.
1668 */
1669 static int connectTimeout()
1670 {
1671 CFPropertyListRef value;
1672 SInt32 connect_timeout = (7 * 24 * 60 * 60); /* Default timeout is one week... */
1673
1674 value = CFPreferencesCopyValue(CFSTR("timeout"), CFSTR("com.apple.print.backends"),
1675 kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
1676 if (value != NULL)
1677 {
1678 if (CFGetTypeID(value) == CFNumberGetTypeID())
1679 CFNumberGetValue(value, kCFNumberSInt32Type, &connect_timeout);
1680
1681 CFRelease(value);
1682 }
1683
1684 return connect_timeout;
1685 }
1686
1687
1688 /*!
1689 * @function signalHandler
1690 * @abstract A signal handler so we can clean up the pap session before exiting.
1691 *
1692 * @param sigraised The signal raised
1693 *
1694 * @result Never returns
1695 */
1696 static void signalHandler(int sigraised)
1697 {
1698 _cupsLangPuts(stderr, _("ERROR: There was a timeout error while sending data to the printer\n"));
1699
1700 papClose();
1701
1702 _exit(1);
1703 }