2 * "$Id: pap.c 7720 2008-07-11 22:46:21Z mike $"
4 * Copyright 2004-2008 Apple Inc. All rights reserved.
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.
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.
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.
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.
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
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.
74 * This backend uses deprecated APIs for AppleTalk; we know this, so
75 * silence any warnings about it...
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
93 #include <sys/fcntl.h>
94 #include <sys/param.h>
96 #include <sys/errno.h>
98 #include <cups/cups.h>
99 #include <cups/backend.h>
100 #include <cups/sidechannel.h>
101 #include <cups/i18n.h>
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>
109 #include <libkern/OSByteOrder.h>
111 #ifdef HAVE_APPLETALK_AT_PROTO_H
112 # include <AppleTalk/at_proto.h>
114 /* These definitions come from at_proto.h... */
115 # define ZIP_DEF_INTERFACE NULL
116 enum { RUNNING
, NOTLOADED
, LOADED
, OTHERERROR
};
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
,
130 extern int checkATStack();
131 extern int nbp_lookup(at_entity_t
*entity
, at_nbptuple_t
*buf
, int max
,
133 extern int nbp_make_entity(at_entity_t
*entity
, char *obj
, char *type
,
135 extern int zip_getmyzone(char *ifName
, at_nvestr_t
*zone
);
136 #endif /* HAVE_APPLETALK_AT_PROTO_H */
138 #include <CoreFoundation/CFURL.h>
139 #include <CoreFoundation/CFNumber.h>
140 #include <CoreFoundation/CFPreferences.h>
143 #define MAX_PRINTERS 500 /* Max number of printers we can lookup */
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])
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 */
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
,
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
);
197 * @abstract Send a file to the specified AppleTalk PAP address.
199 * Usage: printer-uri job-id user title copies options [file]
201 * @param argc # of arguments
202 * @param argv array of arguments
204 * @result A non-zero return value for errors
206 int main (int argc
, const char * argv
[])
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 */
215 /* Make sure status messages are not buffered... */
216 setbuf(stderr
, NULL
);
218 if (argc
== 1 || (argc
== 2 && strcmp(argv
[1], "-discover") == 0))
225 if (argc
< 6 || argc
> 7)
227 _cupsLangPrintf(stderr
,
228 _("Usage: %s job-id user title copies options [file]\n"),
230 return (CUPS_BACKEND_FAILED
);
233 /* If we have 7 arguments, print the file named on the command-line.
234 * Otherwise, send stdin instead...
243 fprintf(stderr
, "DEBUG: opening print file \"%s\"\n", argv
[6]);
245 /* Try to open the print file... */
246 if ((fp
= fopen(argv
[6], "rb")) == NULL
)
248 _cupsLangPrintf(stderr
,
249 _("ERROR: Unable to open print file \"%s\": %s\n"),
250 argv
[6], strerror(errno
));
251 return (CUPS_BACKEND_FAILED
);
254 copies
= atoi(argv
[4]);
257 /* Extract the device name and options from the URI... */
258 parseUri(cupsBackendDeviceURI((char **)argv
), name
, type
, zone
);
260 err
= printFile(name
, type
, zone
, fileno(fp
), STDOUT_FILENO
, STDERR_FILENO
, copies
, argc
);
265 /* Only clear the last status if there wasn't an error */
266 if (err
== noErr
&& !gErrorlogged
)
267 fprintf(stderr
, "INFO:\n");
274 * @function listDevices
275 * @abstract Print a list of all LaserWriter type devices registered in the default zone.
277 * @result A non-zero return value for errors
279 static int listDevices(void)
286 at_nbptuple_t buf
[MAX_PRINTERS
];
288 char name
[NBP_NVE_STR_SIZE
+1];
289 char encodedName
[(3 * NBP_NVE_STR_SIZE
) + 1];
290 char zone
[NBP_NVE_STR_SIZE
+1];
291 char encodedZone
[(3 * NBP_NVE_STR_SIZE
) + 1];
293 /* Make sure it's okay to use appletalk */
294 if (!okayToUseAppleTalk())
296 _cupsLangPuts(stderr
, _("INFO: AppleTalk disabled in System Preferences\n"));
297 return -1; /* Network is down */
300 if (zip_getmyzone(ZIP_DEF_INTERFACE
, &at_zone
))
302 _cupsLangPrintError(_("ERROR: Unable to get default AppleTalk zone"));
306 memcpy(zone
, at_zone
.str
, MIN(at_zone
.len
, sizeof(zone
)-1));
307 zone
[MIN(at_zone
.len
, sizeof(zone
)-1)] = '\0';
309 _cupsLangPrintf(stderr
, _("INFO: Using default AppleTalk zone \"%s\"\n"),
312 addPercentEscapes(zone
, encodedZone
, sizeof(encodedZone
));
314 /* Look up all the printers in our zone */
315 nbp_make_entity(&entity
, "=", "LaserWriter", zone
);
320 if ((numberFound
= nbp_lookup(&entity
, buf
, MAX_PRINTERS
, &retry
)) < 0)
322 _cupsLangPrintError(_("ERROR: Unable to lookup AppleTalk printers"));
326 if (numberFound
>= MAX_PRINTERS
)
327 _cupsLangPrintf(stderr
,
328 _("WARNING: Adding only the first %d printers found"),
331 /* Not required but sort them so they look nice */
332 qsort(buf
, numberFound
, sizeof(at_nbptuple_t
), nbptuple_compare
);
334 for (i
= 0; i
< numberFound
; i
++)
336 memcpy(name
, buf
[i
].enu_entity
.object
.str
, MIN(buf
[i
].enu_entity
.object
.len
, sizeof(name
)-1));
337 name
[MIN(buf
[i
].enu_entity
.object
.len
, sizeof(name
)-1)] = '\0';
339 if (addPercentEscapes(name
, encodedName
, sizeof(encodedName
)) == 0)
341 /* Each line is of the form: "class URI "make model" "info" */
342 char make_model
[128], /* Make and model */
346 if ((ptr
= strchr(name
, ' ')) != NULL
)
349 * If the printer name contains spaces, it is probably a make and
353 if (!strncmp(name
, "ET00", 4))
356 * Drop leading ethernet address info...
359 strlcpy(make_model
, ptr
+ 1, sizeof(make_model
));
362 strlcpy(make_model
, name
, sizeof(make_model
));
365 strcpy(make_model
, "Unknown");
367 printf("network pap://%s/%s/LaserWriter \"%s\" \"%s AppleTalk\"\n",
368 encodedZone
, encodedName
, make_model
, name
);
376 * @function printFile
377 * @abstract Open a PAP session and send the data from the input socket to the printer.
379 * @param name NBP name
380 * @param zone NBP zone
381 * @param type NBP type
382 * @param fdin File descriptor to read data from
383 * @param fdout File descriptor to write printer responses to
384 * @param fderr File descriptor to write printer status to
385 * @param copies # of copies to send (in case in the converter couldn't handle this for us).
386 * @param argc # of command line arguments.
388 * @result A non-zero return value for errors
390 static int printFile(char* name
, char* type
, char* zone
, int fdin
, int fdout
, int fderr
, int copies
, int argc
)
397 char fileBuffer
[4096]; /* File buffer */
398 int fileBufferNbytes
;
403 char sockBuffer
[4096 + 1]; /* Socket buffer with room for nul */
404 char atpReqBuf
[AT_PAP_DATA_SIZE
];
406 int use_sidechannel
; /* Use side channel? */
409 at_inet_t sendDataAddr
;
412 int userdata
, xo
= 0, reqlen
;
417 struct timeval timeout
, *timeoutPtr
;
418 u_char flowQuantum
= 1;
423 connect_timeout
= -1,
428 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
429 struct sigaction action
; /* Actions for POSIX signals */
430 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
433 * Test the side channel descriptor before calling papOpen() since it may open
434 * an unused fd 4 (a.k.a. CUPS_SC_FD)...
438 FD_SET(CUPS_SC_FD
, &readSet
);
443 if ((select(CUPS_SC_FD
+1, &readSet
, NULL
, NULL
, &timeout
)) >= 0)
448 /* try to find our printer */
449 if ((err
= nbp_make_entity(&entity
, name
, type
, zone
)) != noErr
)
451 _cupsLangPrintError(_("ERROR: Unable to make AppleTalk address"));
456 * Remember when we started looking for the printer.
459 start_time
= time(NULL
);
465 fprintf(stderr
, "STATE: +connecting-to-device\n");
467 /* Loop forever trying to get an open session with the printer. */
470 /* Make sure it's okay to use appletalk */
471 if (okayToUseAppleTalk())
473 /* Clear this printer-state-reason in case we've set it */
474 fprintf(stderr
, "STATE: -apple-appletalk-disabled-warning\n");
476 /* Resolve the name into an address. Returns the number found or an error */
477 if ((err
= nbp_lookup(&entity
, &tuple
, 1, &retry
)) > 0)
480 fprintf(stderr
, "DEBUG: Found more than one printer with the name \"%s\"\n", name
);
484 fprintf(stderr
, "STATE: -apple-nbp-lookup-warning\n");
488 /* Open a connection to the device */
489 if ((err
= papOpen(&tuple
, &gConnID
, &gSockfd
, &gSessionAddr
, &flowQuantum
)) == 0)
492 _cupsLangPrintf(stderr
, _("WARNING: Unable to open \"%s:%s\": %s\n"),
493 name
, zone
, strerror(err
));
497 /* It's not unusual to have to call nbp_lookup() twice before it's sucessful... */
498 if (++nbp_failures
> 2)
502 fprintf(stderr
, "STATE: +apple-nbp-lookup-warning\n");
503 _cupsLangPuts(stderr
, _("WARNING: Printer not responding\n"));
509 fprintf(stderr
, "STATE: +apple-appletalk-disabled-warning\n");
510 _cupsLangPuts(stderr
,
511 _("INFO: AppleTalk disabled in System Preferences.\n"));
514 elasped_time
= time(NULL
) - start_time
;
516 if (connect_timeout
== -1)
517 connect_timeout
= connectTimeout();
519 if (connect_timeout
&& elasped_time
> connect_timeout
)
521 _cupsLangPuts(stderr
, _("ERROR: Printer not responding\n"));
523 goto Exit
; /* Waiting too long... */
525 else if (elasped_time
< (30 * 60))
526 sleep_time
= 10; /* Waiting < 30 minutes */
527 else if (elasped_time
< (24 * 60 * 60))
528 sleep_time
= 30; /* Waiting < 24 hours */
530 sleep_time
= 60; /* Waiting > 24 hours */
532 fprintf(stderr
, "DEBUG: sleeping %d seconds...\n", (int)sleep_time
);
536 fprintf(stderr
, "STATE: -connecting-to-device\n");
539 * Now that we are connected to the printer ignore SIGTERM so that we
540 * can finish out any page data the driver sends (e.g. to eject the
541 * current page... if we are printing data from a file then catch the
542 * signal so we can send a PAP Close packet (otherwise you can't cancel
546 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
547 sigset(SIGTERM
, (argc
< 7) ? SIG_IGN
: signalHandler
);
548 #elif defined(HAVE_SIGACTION)
549 memset(&action
, 0, sizeof(action
));
551 sigemptyset(&action
.sa_mask
);
552 action
.sa_handler
= (argc
< 7) ? SIG_IGN
: signalHandler
;
553 sigaction(SIGTERM
, &action
, NULL
);
555 signal(SIGTERM
, (argc
< 7) ? SIG_IGN
: signalHandler
);
558 /* Makes debugging easier; otherwise printer will be busy for several minutes */
559 signal(SIGINT
, signalHandler
);
562 #endif /* HAVE_SIGSET */
564 _cupsLangPuts(stderr
, _("INFO: Sending data\n"));
566 sendDataAddr
= tuple
.enu_addr
;
568 /* Start the tickle packets and set a timeout alarm */
569 if ((err
= papSendRequest(gSockfd
, &gSessionAddr
, gConnID
, AT_PAP_TYPE_TICKLE
, 0, false, false)) < 0)
571 _cupsLangPrintError(_("ERROR: Unable to send PAP tickle request"));
574 signal(SIGALRM
, signalHandler
);
577 /* Prime the pump with an initial send-data packet */
578 if ((err
= papSendRequest(gSockfd
, &gSessionAddr
, gConnID
, AT_PAP_TYPE_SEND_DATA
, 0xFF, true, true)) < 0)
580 _cupsLangPrintError(_("ERROR: Unable to send initial PAP send data request"));
584 /* Set non-blocking mode on our data source descriptor */
585 val
= fcntl(fdin
, F_GETFL
, 0);
586 fcntl(fdin
, F_SETFL
, val
| O_NONBLOCK
);
588 fileBufferNbytes
= 0;
590 fileEOFRead
= fileEOFSent
= false;
592 maxfdp1
= MAX(fdin
, gSockfd
) + 1;
594 if (use_sidechannel
&& CUPS_SC_FD
>= maxfdp1
)
595 maxfdp1
= CUPS_SC_FD
+ 1;
597 if (gStatusInterval
!= 0)
600 nextStatusTime
= time(NULL
) + gStatusInterval
;
601 timeoutPtr
= &timeout
;
609 /* Set up our descriptors for the select */
611 FD_SET(gSockfd
, &readSet
);
613 if (fileBufferNbytes
== 0 && fileEOFRead
== false)
614 FD_SET(fdin
, &readSet
);
617 FD_SET(CUPS_SC_FD
, &readSet
);
619 /* Set the select timeout value based on the next status interval */
620 if (gStatusInterval
!= 0)
623 timeout
.tv_sec
= (nextStatusTime
> now
) ? nextStatusTime
- now
: 1;
626 /* Wait here for something interesting to happen */
627 if ((err
= select(maxfdp1
, &readSet
, 0, 0, timeoutPtr
)) < 0)
629 _cupsLangPrintError(_("ERROR: select() failed"));
633 if (err
== 0 || (gStatusInterval
!= 0 && time(NULL
) >= nextStatusTime
))
635 /* Time to send a status request */
636 if ((err
= papSendRequest(gSockfd
, &tuple
.enu_addr
, 0, AT_PAP_TYPE_SEND_STATUS
, 0x01, false, false)) < 0)
637 _cupsLangPrintError(_("WARNING: Unable to send PAP status request"));
640 nextStatusTime
= time(NULL
) + gStatusInterval
;
644 * Check if we have a side-channel request ready...
647 if (use_sidechannel
&& FD_ISSET(CUPS_SC_FD
, &readSet
))
648 sidechannel_request();
650 /* Was there an event on the input stream? */
651 if (FD_ISSET(fdin
, &readSet
))
653 FD_CLR(fdin
, &readSet
);
655 assert(fileBufferNbytes
== 0);
656 fileBufferNbytes
= read(fdin
, fileBuffer
, MIN(sizeof(fileBuffer
), AT_PAP_DATA_SIZE
* flowQuantum
));
657 if (fileBufferNbytes
== 0)
660 if (fileEOFSent
== false && fileBufferNbytes
>= 0 && gSendDataID
!= 0)
662 fprintf(stderr
, "DEBUG: -> PAP_DATA %d bytes %s\n", fileBufferNbytes
, fileEOFRead
? "with EOF" : "");
663 papWrite(gSockfd
, &sendDataAddr
, gSendDataID
, gConnID
, flowQuantum
, fileBuffer
, fileBufferNbytes
, fileEOFRead
);
665 fileTbytes
+= fileBufferNbytes
;
666 if (argc
> 6 && !gErrorlogged
)
667 fprintf(stderr
, "DEBUG: Sending print file, %qd bytes\n", (off_t
)fileTbytes
);
669 fileBufferNbytes
= 0;
674 if (gWaitEOF
== false || fileTbytes
== 0)
683 /* Was there an event on the output stream? */
684 if (FD_ISSET(gSockfd
, &readSet
))
686 if ((rc
= atp_look(gSockfd
)) < 0)
688 _cupsLangPrintError(_("ERROR: Unable to look for PAP response"));
694 /* It's an ATP response */
695 resp
.resp
[0].iov_base
= sockBuffer
;
696 resp
.resp
[0].iov_len
= sizeof(sockBuffer
) - 1;
699 if ((err
= atp_getresp(gSockfd
, &tid
, &resp
)) < 0)
701 _cupsLangPrintError(_("ERROR: Unable to get PAP response"));
704 userdata
= resp
.userdata
[0];
708 /* It's an ATP request */
709 reqlen
= sizeof(atpReqBuf
);
710 if ((err
= atp_getreq(gSockfd
, &src
, atpReqBuf
, &reqlen
, &userdata
, &xo
, &tid
, &bitmap
, 0)) < 0)
712 _cupsLangPrintError(_("ERROR: Unable to get PAP request"));
717 fprintf(stderr
, "DEBUG: <- %s\n", packet_name(TYPE_OF(userdata
)));
719 switch (TYPE_OF(userdata
))
721 case AT_PAP_TYPE_SEND_STS_REPLY
: /* Send-Status-Reply packet */
724 char *iov_base
= (char *)resp
.resp
[0].iov_base
;
725 statusUpdate(&iov_base
[5], iov_base
[4]);
729 case AT_PAP_TYPE_SEND_DATA
: /* Send-Data packet */
730 sendDataAddr
.socket
= src
.socket
;
732 OSReadBigInt16(&SEQUENCE_NUM(userdata
), 0);
734 if ((fileBufferNbytes
> 0 || fileEOFRead
) && fileEOFSent
== false)
736 fprintf(stderr
, "DEBUG: -> PAP_DATA %d bytes %s\n", fileBufferNbytes
, fileEOFRead
? "with EOF" : "");
737 papWrite(gSockfd
, &sendDataAddr
, gSendDataID
, gConnID
, flowQuantum
, fileBuffer
, fileBufferNbytes
, fileEOFRead
);
739 fileTbytes
+= fileBufferNbytes
;
740 if (argc
> 6 && !gErrorlogged
)
741 fprintf(stderr
, "DEBUG: Sending print file, %qd bytes\n", (off_t
)fileTbytes
);
743 fileBufferNbytes
= 0;
748 if (gWaitEOF
== false)
757 case AT_PAP_TYPE_DATA
: /* Data packet */
758 for (len
=0, i
=0; i
< ATP_TRESP_MAX
; i
++)
760 if (resp
.bitmap
& (1 << i
))
761 len
+= resp
.resp
[i
].iov_len
;
764 fprintf(stderr
, "DEBUG: <- PAP_DATA %d bytes %s\n", len
, IS_PAP_EOF(userdata
) ? "with EOF" : "");
768 char *pLineBegin
, *pCommentEnd
, *pChar
;
773 cupsBackChannelWrite(sockBuffer
, len
, 1.0);
775 sockBuffer
[len
] = '\0'; /* We always reserve room for the nul so we can use strstr() below*/
776 pLineBegin
= sockBuffer
;
778 /* If there are PostScript status comments in the buffer log them.
780 * This logic shouldn't be in the backend but until we get backchannel
781 * data in CUPS 1.2 it has to live here.
783 while (pLineBegin
< sockBuffer
+ len
&&
784 (pLineBegin
= strstr(pLineBegin
, "%%[")) != NULL
&&
785 (pCommentEnd
= strstr(pLineBegin
, "]%%")) != NULL
)
787 pCommentEnd
+= 3; /* Skip past "]%%" */
788 *pCommentEnd
= '\0'; /* There's always room for the nul */
790 /* Strip the CRs & LFs before writing it to stderr */
791 for (pChar
= pLineBegin
; pChar
< pCommentEnd
; pChar
++)
792 if (*pChar
== '\r' || *pChar
== '\n')
795 if (strncasecmp(pLineBegin
, "%%[ Error:", 10) == 0)
797 /* logLevel should be "ERROR" here but this causes PrintCenter
798 * to pause the queue which in turn clears this error, which
799 * restarts the job. So the job ends up in an infinite loop with
800 * the queue being held/un-held. Just make it DEBUG for now until
801 * we fix notifications later.
806 else if (strncasecmp(pLineBegin
, "%%[ Flushing", 12) == 0)
811 if ((logstrlen
= snprintf(logstr
, sizeof(logstr
), "%s: %s\n", logLevel
, pLineBegin
)) >= sizeof(logstr
))
813 /* If the string was trucnated make sure it has a linefeed before the nul */
814 logstrlen
= sizeof(logstr
) - 1;
815 logstr
[logstrlen
- 1] = '\n';
818 write(fderr
, logstr
, logstrlen
);
820 pLineBegin
= pCommentEnd
+ 1;
824 if (IS_PAP_EOF(userdata
) != 0)
826 /* If this is EOF then were we expecting it? */
827 if (fileEOFSent
== true)
831 _cupsLangPuts(stderr
, _("WARNING: Printer sent unexpected EOF\n"));
835 if ((err
= papSendRequest(gSockfd
, &gSessionAddr
, gConnID
, AT_PAP_TYPE_SEND_DATA
, 0xFF, true, true)) < 0)
837 _cupsLangPrintf(stderr
,
838 _("ERROR: Error %d sending PAPSendData request: %s\n"),
839 err
, strerror(errno
));
844 case AT_PAP_TYPE_TICKLE
: /* Tickle packet */
847 case AT_PAP_TYPE_CLOSE_CONN
: /* Close-Connection packet */
848 /* We shouldn't normally see this. */
849 papCloseResp(gSockfd
, &gSessionAddr
, xo
, tid
, gConnID
);
851 /* If this is EOF then were we expecting it? */
852 if (fileEOFSent
== true)
854 _cupsLangPuts(stderr
, _("WARNING: Printer sent unexpected EOF\n"));
858 _cupsLangPuts(stderr
, _("ERROR: Printer sent unexpected EOF\n"));
863 case AT_PAP_TYPE_OPEN_CONN
: /* Open-Connection packet */
864 case AT_PAP_TYPE_OPEN_CONN_REPLY
: /* Open-Connection-Reply packet */
865 case AT_PAP_TYPE_SEND_STATUS
: /* Send-Status packet */
866 case AT_PAP_TYPE_CLOSE_CONN_REPLY
: /* Close-Connection-Reply packet */
867 _cupsLangPrintf(stderr
, _("WARNING: Unexpected PAP packet of type %d\n"),
872 _cupsLangPrintf(stderr
, _("WARNING: Unknown PAP packet of type %d\n"),
877 if (CONNID_OF(userdata
) == gConnID
)
879 /* Reset tickle timer */
888 * Close the socket and return...
899 * @abstract Open a pap session to a printer.
901 * @param tuple nbp address of printer
902 * @param connID returned pap connection id
903 * @param fd returned socket descriptor
904 * @param sessionAddr returned session address
905 * @param flowQuantum returned flow quantum (usually 8)
907 * @result A non-zero return value for errors
909 static int papOpen(at_nbptuple_t
* tuple
, u_char
* connID
, int* fd
,
910 at_inet_t
* sessionAddr
, u_char
* flowQuantum
)
918 rdata
[ATP_DATA_SIZE
];
926 puserdata
= (u_char
*)&userdata
;
928 _cupsLangPuts(stderr
, _("INFO: Opening connection\n"));
930 if ((*fd
= atp_open(&socketfd
)) < 0)
934 * Build the open connection request packet.
940 *connID
= (rand()&0xff) | 0x01;
941 puserdata
[0] = *connID
;
942 puserdata
[1] = AT_PAP_TYPE_OPEN_CONN
;
950 resp
.resp
[0].iov_base
= rdata
;
951 resp
.resp
[0].iov_len
= sizeof(rdata
);
958 waitTime
= time(NULL
) - tm
;
959 OSWriteBigInt16(&data
[2], 0, (u_short
)waitTime
);
961 fprintf(stderr
, "DEBUG: -> %s\n", packet_name(AT_PAP_TYPE_OPEN_CONN
));
963 if (atp_sendreq(*fd
, &tuple
->enu_addr
, data
, 4, userdata
, 1, 0,
964 0, &resp
, &retry
, 0) < 0)
966 statusUpdate("Destination unreachable", 23);
967 result
= EHOSTUNREACH
;
971 puserdata
= (u_char
*)&resp
.userdata
[0];
972 open_result
= OSReadBigInt16(&rdata
[2], 0);
974 fprintf(stderr
, "DEBUG: <- %s, status %d\n", packet_name(puserdata
[1]),
978 * Just for the sake of our sanity check the other fields in the packet
981 if (puserdata
[1] != AT_PAP_TYPE_OPEN_CONN_REPLY
||
982 (open_result
== 0 && (puserdata
[0] & 0xff) != *connID
))
988 statusUpdate(&rdata
[5], rdata
[4] & 0xff);
991 * if the connection established okay exit from the loop
994 if (open_result
== 0)
1002 /* Update the session address
1004 sessionAddr
->net
= tuple
->enu_addr
.net
;
1005 sessionAddr
->node
= tuple
->enu_addr
.node
;
1006 sessionAddr
->socket
= rdata
[0];
1007 *flowQuantum
= rdata
[1];
1021 * @function papClose
1022 * @abstract End a PAP session by canceling outstanding send-data & tickle
1023 * transactions and sending a PAP close request.
1025 * @result A non-zero return value for errors
1027 static int papClose()
1031 unsigned char rdata
[ATP_DATA_SIZE
];
1033 u_char
*puserdata
= (u_char
*)&userdata
;
1044 /* Cancel the pending send-data and tickle trnsactions
1048 tmpID
= gSendDataID
;
1050 papCancelRequest(fd
, tmpID
);
1057 papCancelRequest(fd
, tmpID
);
1060 /* This is a workaround for bug #2735145. The problem is papWrite()
1061 * returns before the ATP TRel arrives for it. If we send the pap close packet
1062 * before this release then the printer can drop the last data packets.
1063 * The effect on an Epson printer is the last page doesn't print, on HP it
1064 * doesn't close the pap session.
1066 if (gWaitEOF
== false)
1069 fprintf(stderr
, "DEBUG: -> %s\n", packet_name(AT_PAP_TYPE_CLOSE_CONN
));
1071 puserdata
[0] = gConnID
;
1072 puserdata
[1] = AT_PAP_TYPE_CLOSE_CONN
;
1080 resp
.resp
[0].iov_base
= rdata
;
1081 resp
.resp
[0].iov_len
= sizeof(rdata
);
1083 atp_sendreq(fd
, &gSessionAddr
, 0, 0, userdata
, 1, 0, 0, &resp
, &retry
, 0);
1092 * @function papWrite
1093 * @abstract Write bytes to a printer.
1095 * @param sockfd socket descriptor
1096 * @param dest destination address
1097 * @param tid transaction id
1098 * @param connID connection id
1099 * @param flowQuantum returned flow quantum (usually 8)
1100 * @param data pointer to the data
1101 * @param len number of bytes to send
1102 * @param eof pap eof flag
1104 * @result A non-zero return value for errors
1106 static int papWrite(int sockfd
, at_inet_t
* dest
, u_short tid
, u_char connID
, u_char flowQuantum
, char* data
, int len
, int eof
)
1113 /* fprintf(stderr, "DEBUG: papWrite(%d%s) to %d,%d,%d; %d\n", len, eof ? " EOF":"", dest->net, dest->node, dest->socket, connID); */
1115 if (len
> AT_PAP_DATA_SIZE
* flowQuantum
)
1117 fprintf(stderr
, "DEBUG: papWrite() len of %d is too big!\n", len
);
1123 * Break up the outgoing data into a set of
1124 * response packets to reply to an incoming
1125 * PAP 'SENDDATA' request
1127 for (i
= 0; i
< flowQuantum
; i
++)
1129 resp
.userdata
[i
] = 0;
1130 puserdata
= (u_char
*)&resp
.userdata
[i
];
1132 puserdata
[PAP_CONNID
] = connID
;
1133 puserdata
[PAP_TYPE
] = AT_PAP_TYPE_DATA
;
1134 puserdata
[PAP_EOF
] = eof
? 1 : 0;
1136 resp
.resp
[i
].iov_base
= (caddr_t
)data
;
1139 data
+= AT_PAP_DATA_SIZE
;
1141 resp
.resp
[i
].iov_len
= MIN((int)len
, (int)AT_PAP_DATA_SIZE
);
1142 len
-= resp
.resp
[i
].iov_len
;
1146 resp
.bitmap
= (1 << (i
+ 1)) - 1;
1149 * Write out the data as a PAP 'DATA' response
1152 if ((result
= atp_sendrsp(sockfd
, dest
, true, tid
, &resp
)) < 0)
1154 fprintf(stderr
, "DEBUG: atp_sendrsp() returns %d, errno %d \"%s\"\n", result
, errno
, strerror(errno
));
1162 * @function papCloseResp
1163 * @abstract Send a pap close response in the rare case we receive a close connection request.
1165 * @param sockfd socket descriptor
1166 * @param dest destination address
1167 * @param tid transaction id
1168 * @param connID connection id
1170 * @result A non-zero return value for errors
1172 static int papCloseResp(int sockfd
, at_inet_t
* dest
, int xo
, u_short tid
, u_char connID
)
1178 resp
.userdata
[0] = 0;
1180 ((u_char
*)&resp
.userdata
[0])[PAP_CONNID
] = connID
;
1181 ((u_char
*)&resp
.userdata
[0])[PAP_TYPE
] = AT_PAP_TYPE_CLOSE_CONN_REPLY
;
1183 resp
.resp
[0].iov_base
= NULL
;
1184 resp
.resp
[0].iov_len
= 0;
1186 if ((result
= atp_sendrsp(sockfd
, dest
, xo
, tid
, &resp
)) < 0)
1188 fprintf(stderr
, "DEBUG: atp_sendrsp() returns %d, errno %d \"%s\"\n", result
, errno
, strerror(errno
));
1196 * @function papSendRequest
1197 * @abstract Send a pap close response in the rare case we receive a close connection request.
1199 * @param sockfd socket descriptor
1200 * @param dest destination address
1201 * @param function pap function
1202 * @param bitmap bitmap
1203 * @param xo exactly once
1204 * @param seqno sequence number
1206 * @result A non-zero return value for errors
1208 static int papSendRequest(int sockfd
, at_inet_t
* dest
, u_char connID
, int function
, u_char bitmap
, int xo
, int seqno
)
1214 u_char
*puserdata
= (u_char
*)&userdata
;
1217 static u_short pap_send_count
= 0;
1219 fprintf(stderr
, "DEBUG: -> %s\n", packet_name(function
));
1221 puserdata
[0] = connID
;
1222 puserdata
[1] = function
;
1223 resp
.bitmap
= bitmap
;
1224 retry
.interval
= 10;
1225 retry
.retries
= -1; /* was ATP_INFINITE_RETRIES */
1229 if (pap_send_count
== 0)
1232 OSWriteBigInt16(&puserdata
[2], 0, pap_send_count
);
1235 OSWriteBigInt16(&puserdata
[2], 0, 0);
1238 sigaddset(&sv
, SIGIO
);
1239 sigprocmask(SIG_SETMASK
, &sv
, &osv
);
1241 err
= atp_sendreq(sockfd
, dest
, 0, 0, userdata
, xo
, 0, &tid
, &resp
, &retry
, 1);
1243 sigprocmask(SIG_SETMASK
, &osv
, NULL
);
1250 * @function papCancelRequest
1251 * @abstract Cancel a pending pap request.
1253 * @param sockfd socket descriptor
1254 * @param tid transaction ID
1256 * @result A non-zero return value for errors
1258 int papCancelRequest(int sockfd
, u_short tid
)
1263 sigaddset(&sv
, SIGIO
);
1264 sigprocmask(SIG_SETMASK
, &sv
, &osv
);
1266 if (atp_abort(sockfd
, NULL
, tid
) < 0)
1268 sigprocmask(SIG_SETMASK
, &osv
, NULL
);
1271 sigprocmask(SIG_SETMASK
, &osv
, NULL
);
1278 * 'sidechannel_request()' - Handle side-channel requests.
1282 sidechannel_request()
1284 cups_sc_command_t command
; /* Request command */
1285 cups_sc_status_t status
; /* Request/response status */
1286 char data
[2048]; /* Request/response data */
1287 int datalen
; /* Request/response data size */
1289 datalen
= sizeof(data
);
1291 if (cupsSideChannelRead(&command
, &status
, data
, &datalen
, 1.0))
1293 fputs(_("WARNING: Failed to read side-channel request!\n"), stderr
);
1299 case CUPS_SC_CMD_GET_BIDI
: /* Is the connection bidirectional? */
1301 cupsSideChannelWrite(command
, CUPS_SC_STATUS_OK
, data
, 1, 1.0);
1304 case CUPS_SC_CMD_GET_STATE
: /* Return device state */
1305 data
[0] = CUPS_SC_STATE_ONLINE
;
1306 cupsSideChannelWrite(command
, CUPS_SC_STATUS_OK
, data
, 1, 1.0);
1309 case CUPS_SC_CMD_DRAIN_OUTPUT
: /* Drain all pending output */
1310 case CUPS_SC_CMD_SOFT_RESET
: /* Do a soft reset */
1311 case CUPS_SC_CMD_GET_DEVICE_ID
: /* Return IEEE-1284 device ID */
1313 cupsSideChannelWrite(command
, CUPS_SC_STATUS_NOT_IMPLEMENTED
,
1323 * @function statusUpdate
1324 * @abstract Format and print a PAP status response to stderr.
1326 * @param status The status response string
1327 * @param statusLen The length of the status response string
1329 void statusUpdate(char* status
, u_char statusLen
)
1331 static char status_str
[255];
1332 static u_char last_statusLen
= 0xFF;
1334 /* Only send this if the status has changed */
1335 if (statusLen
!= last_statusLen
|| memcmp(status
, status_str
, statusLen
) != 0)
1337 if (statusLen
> sizeof(status_str
)-1)
1338 statusLen
= sizeof(status_str
)-1;
1339 last_statusLen
= statusLen
;
1340 memcpy(status_str
, status
, statusLen
);
1341 status_str
[(int)statusLen
] = '\0';
1344 * Make sure the status string is in the form of a PostScript comment.
1347 if (statusLen
> 3 && memcmp(status
, "%%[", 3) == 0)
1348 fprintf(stderr
, "INFO: %s\n", status_str
);
1350 fprintf(stderr
, "INFO: %%%%[ %s ]%%%%\n", status_str
);
1357 * @function parseUri
1358 * @abstract Parse a PAP URI into it's NBP components.
1360 * @param argv0 The PAP URI to parse
1361 * @param name NBP name
1362 * @param zone NBP zone
1363 * @param type NBP type
1365 * @result A non-zero return value for errors
1367 static int parseUri(const char* argv0
, char* name
, char* type
, char* zone
)
1369 char method
[255], /* Method in URI */
1370 hostname
[1024], /* Hostname */
1371 username
[255], /* Username info (not used) */
1372 resource
[1024], /* Resource info (device and options) */
1375 *options
, /* Pointer to options */
1376 *optionName
, /* Name of option */
1377 *value
, /* Value of option */
1378 sep
; /* Separator character */
1379 int port
; /* Port number (not used) */
1380 int statusInterval
; /* */
1383 * Extract the device name and options from the URI...
1385 method
[0] = username
[0] = hostname
[0] = resource
[0] = '\0';
1388 httpSeparateURI(HTTP_URI_CODING_NONE
, argv0
, method
, sizeof(method
),
1389 username
, sizeof(username
),
1390 hostname
, sizeof(hostname
), &port
,
1391 resource
, sizeof(resource
));
1394 * See if there are any options...
1396 if ((options
= strchr(resource
, '?')) != NULL
)
1399 * Yup, terminate the device name string and move to the first
1400 * character of the options...
1404 while (*options
!= '\0')
1410 optionName
= options
;
1412 while (*options
&& *options
!= '=' && *options
!= '+' && *options
!= '&')
1415 if ((sep
= *options
) != '\0')
1426 while (*options
&& *options
!= '+' && *options
!= '&')
1436 * Process the option...
1439 if (!strcasecmp(optionName
, "waiteof"))
1442 * Wait for the end of the print file?
1445 if (!strcasecmp(value
, "on") ||
1446 !strcasecmp(value
, "yes") ||
1447 !strcasecmp(value
, "true"))
1451 else if (!strcasecmp(value
, "off") ||
1452 !strcasecmp(value
, "no") ||
1453 !strcasecmp(value
, "false"))
1459 _cupsLangPrintf(stderr
,
1460 _("WARNING: Boolean expected for waiteof option \"%s\"\n"),
1464 else if (!strcasecmp(optionName
, "status"))
1467 * Set status reporting interval...
1470 statusInterval
= atoi(value
);
1471 if (value
[0] < '0' || value
[0] > '9' || statusInterval
< 0)
1473 _cupsLangPrintf(stderr
,
1474 _("WARNING: number expected for status option \"%s\"\n"),
1479 gStatusInterval
= statusInterval
;
1485 resourcePtr
= resource
;
1487 if (*resourcePtr
== '/')
1490 /* If the resource has a slash we assume the slash seperates the AppleTalk object
1491 * name from the AppleTalk type. If the slash is not present we assume the AppleTalk
1492 * type is LaserWriter.
1495 typePtr
= strchr(resourcePtr
, '/');
1496 if (typePtr
!= NULL
)
1502 typePtr
= "LaserWriter";
1505 removePercentEscapes(hostname
, zone
, NBP_NVE_STR_SIZE
+ 1);
1506 removePercentEscapes(resourcePtr
, name
, NBP_NVE_STR_SIZE
+ 1);
1507 removePercentEscapes(typePtr
, type
, NBP_NVE_STR_SIZE
+ 1);
1514 * @function addPercentEscapes
1515 * @abstract Encode a string with percent escapes
1517 * @param src The source C string
1518 * @param dst Desination buffer
1519 * @param dstMax Size of desination buffer
1521 * @result A non-zero return value for errors
1523 static int addPercentEscapes(const char* src
, char* dst
, int dstMax
)
1526 char *dstEnd
= dst
+ dstMax
- 1; /* -1 to leave room for the NUL */
1532 if ((c
>= 'a' && c
<= 'z') || (c
>= 'A' && c
<= 'Z') ||
1533 (c
>= '0' && c
<= '9') || (c
== '.' || c
== '-' || c
== '*' || c
== '_'))
1542 if (dst
>= dstEnd
- 2)
1545 snprintf(dst
, dstEnd
- dst
, "%%%02x", c
);
1556 * @function removePercentEscapes
1557 * @abstract Returns a string with any percent escape sequences replaced with their equivalent character
1559 * @param src Source buffer
1560 * @param srclen Number of bytes in source buffer
1561 * @param dst Desination buffer
1562 * @param dstMax Size of desination buffer
1564 * @result A non-zero return value for errors
1566 static int removePercentEscapes(const char* src
, char* dst
, int dstMax
)
1569 const char *dstEnd
= dst
+ dstMax
;
1571 while (*src
&& dst
< dstEnd
)
1577 sscanf(src
, "%02x", &c
);
1592 * @function nbptuple_compare
1593 * @abstract An NBP comparator for qsort.
1595 * @result p1<p2: -1, p1=p2: 0, p1>p2: 1
1597 int nbptuple_compare(const void *p1
, const void *p2
)
1600 int len
= MIN(((at_nbptuple_t
*)p1
)->enu_entity
.object
.len
,
1601 ((at_nbptuple_t
*)p2
)->enu_entity
.object
.len
);
1603 if ((result
= memcmp(((at_nbptuple_t
*)p1
)->enu_entity
.object
.str
, ((at_nbptuple_t
*)p2
)->enu_entity
.object
.str
, len
)) == 0)
1605 if (((at_nbptuple_t
*)p1
)->enu_entity
.object
.len
< ((at_nbptuple_t
*)p2
)->enu_entity
.object
.len
)
1607 else if (((at_nbptuple_t
*)p1
)->enu_entity
.object
.len
> ((at_nbptuple_t
*)p2
)->enu_entity
.object
.len
)
1617 * @function okayToUseAppleTalk
1618 * @abstract Returns true if AppleTalk is available and enabled.
1620 * @result non-zero if AppleTalk is enabled
1622 static int okayToUseAppleTalk()
1624 int atStatus
= checkATStack();
1626 /* I think the test should be:
1627 * return atStatus == RUNNING || atStatus == LOADED;
1628 * but when I disable AppleTalk from the network control panel and
1629 * reboot, AppleTalk shows up as loaded. The test empirically becomes
1632 return atStatus
== RUNNING
;
1637 * @function packet_name
1638 * @abstract Returns packet name string.
1642 static const char *packet_name(u_char x
)
1646 case AT_PAP_TYPE_OPEN_CONN
: return "PAP_OPEN_CONN";
1647 case AT_PAP_TYPE_OPEN_CONN_REPLY
: return "PAP_OPEN_CONN_REPLY";
1648 case AT_PAP_TYPE_SEND_DATA
: return "PAP_SEND_DATA";
1649 case AT_PAP_TYPE_DATA
: return "PAP_DATA";
1650 case AT_PAP_TYPE_TICKLE
: return "PAP_TICKLE";
1651 case AT_PAP_TYPE_CLOSE_CONN
: return "PAP_CLOSE_CONN";
1652 case AT_PAP_TYPE_CLOSE_CONN_REPLY
: return "PAP_CLOSE_CONN_REPLY";
1653 case AT_PAP_TYPE_SEND_STATUS
: return "PAP_SEND_STATUS";
1654 case AT_PAP_TYPE_SEND_STS_REPLY
: return "PAP_SEND_STS_REPLY";
1655 case AT_PAP_TYPE_READ_LW
: return "PAP_READ_LW";
1662 * @function connectTimeout
1663 * @abstract Returns the connect timeout preference value.
1665 static int connectTimeout()
1667 CFPropertyListRef value
;
1668 SInt32 connect_timeout
= (7 * 24 * 60 * 60); /* Default timeout is one week... */
1670 value
= CFPreferencesCopyValue(CFSTR("timeout"), CFSTR("com.apple.print.backends"),
1671 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
1674 if (CFGetTypeID(value
) == CFNumberGetTypeID())
1675 CFNumberGetValue(value
, kCFNumberSInt32Type
, &connect_timeout
);
1680 return connect_timeout
;
1685 * @function signalHandler
1686 * @abstract A signal handler so we can clean up the pap session before exiting.
1688 * @param sigraised The signal raised
1690 * @result Never returns
1692 static void signalHandler(int sigraised
)
1694 _cupsLangPuts(stderr
, _("ERROR: There was a timeout error while sending data to the printer\n"));