AmendHub

Download

thecloud

/

SimpleSpooler

/

PAP.c

 

(View History)

thecloud   change FilterData to handle split sequences, detect reverse linefeed Latest amendment: 4 on 2026-07-29

1 /*
2 * PAP.c
3 *
4 * Implementation of PAP (Printer Access Protocol)
5 * as described in Inside AppleTalk, Second Edition, 1990.
6 *
7 * Written by: Ken McLeod, 2026-01-07
8 * Last update: 2026-02-23
9 *
10 * This software is provided as-is, with no warranties expressed or implied,
11 * under the terms of the BSD 2-Clause License. See separate "LICENSE" file.
12 */
13
14 #include <Types.h>
15 #include <Limits.h>
16 #include <SysEqu.h>
17 #include <AppleTalk.h>
18 #include <Events.h>
19 #include <ToolUtils.h>
20 #include <Errors.h>
21 #include <Devices.h>
22 #include <Memory.h>
23 #include <String.h>
24 #include "Logging.h"
25 #include "QueryParser.h"
26 #include "SpoolFiles.h"
27 #include "PAP.h"
28
29
30 /* we can't use PSendRequest since we can't get the response's user bytes */
31 #define USING_PSENDREQUEST_API 0
32
33 /* don't wait for a status request to be sent before asking for more data */
34 #define WAIT_FOR_POST_DATA_STATUS 0
35
36 /* function prototypes */
37
38 void HandleRequest(void);
39 void HandleResponse(void);
40 void CancelRequest(void);
41 int SendRequest(char connID, char funcID);
42 int SendResponse(char connID, char funcID);
43 int SetUpRequestData(char funcID, Ptr reqBuf);
44 int SetUpResponseData(char funcID, Ptr respBuf);
45 void SendTickle(void);
46 void StartTickling(void);
47 void StopTickling(void);
48
49 /* globals */
50
51 /* %%% these should all go into a PAPServer struct,
52 * so we could potentially have multiple instances */
53
54 int requestsPending = 0; /* how many calls to PGetRequest are pending */
55 int responsesPending = 0; /* how many calls to PSendRequest are pending */
56 long jobsSpooled = 0L; /* how many print jobs we have received and spooled */
57 Boolean listening = false; /* are we actively listening for incoming requests? */
58 Boolean respondedToFirstPostConnectionStatusReq = false; /* can we call SendData yet? */
59 short serverRefNum = 0; /* reference number of server instance */
60 char gConnID = 0; /* currently open connection ID, or 0 if no connection */
61 char gConnSocket = 0; /* destination socket to use for this connection */
62 char gFlowQuantum = 0; /* flow quantum to use for this connection */
63 short gNet = 0;
64 char gNode = 0;
65 char gSocket = 0;
66 unsigned short gWaitTime = 0;
67 unsigned short gDataSeq = 0; /* PAP data sequence number, increments from 1 to 65,535 */
68 long int gLastTickleRecd = 0L; /* last time we received any packet for connection */
69 long int gLastTickleSent = 0L; /* last time we sent a tickle for connection */
70 short gMPPRef = 0;
71 short gMyNet = 0, gMyNode = 0;
72 short gMyRecvSocket = 0, gMySendSocket = 0;
73 short gATPRequestResult = 0;
74 NamesTableEntry *NTPtr = 0L;
75 EntityName name;
76 MPPParamBlock p;
77
78 ATPPBPtr gReqPBPtr;
79 ATPPBPtr gTickleReqPBPtr;
80 ATPPBPtr gSendReqPBPtr;
81 ATPPBPtr gSendRespPBPtr;
82 ATATPRecHandle gSendReqHdl; /* outgoing request for ATPRequest */
83 ATATPRecPtr gSendReqPtr; /* so we only need to dereference locked gSendReqHdl once */
84 ATATPRecHandle gSendRespHdl; /* outgoing response for ATPResponse */
85 ATATPRecPtr gSendRespPtr; /* so we only need to dereference locked gSendReqHdl once */
86 Ptr gRReqBuf; /* buffer for received request data */
87 Ptr gSReqBuf; /* buffer for our sent request data */
88 Ptr gRRespBuf; /* buffer for response we will get back from ATPRequest */
89 Ptr gSRespBuf; /* buffer for outgoing response data from ATPResponse */
90 BDSPtr gSReqBdsPtr; /* BDS for request data that we send */
91 BDSPtr gRRespBdsPtr; /* BDS for the received response to our request */
92 BDSPtr gSRespBdsPtr; /* BDS for response data that we send */
93 long int gDataLen; /* total data received on current connection */
94 int gPrinterType; /* {0..3}, see kPrinterType definitions */
95 Boolean gColorRibbon;
96 Boolean gSheetFeeder;
97 Boolean gIW1Mode;
98 Boolean gNoReverse;
99 Boolean gFinishedQuery = false;
100 Handle gQueryHdl = nil; /* query sent to us by the LW driver */
101 int gQueryLength; /* length of query */
102 short gQueryReqSeq = 0; /* sequence ID of query (last SendData we received) */
103 short gQueryReqTID = 0; /* transaction ID of query */
104
105
106 /* constants */
107
108 #define kMaxAddrBlocks 100
109 #define kMaxPacketSize 578 /* maximum ATP packet size we can receive */
110 #define kMaxResponses 8 /* maximum number of responses to expect */
111 #define kRespBufSize (kMaxPacketSize * kMaxResponses)
112 #define kTickleTicks (60 * 30) /* 30 seconds in ticks */
113 #define kIdleTimeoutTicks (60 * 60 * 2) /* 2 minutes in ticks */
114
115 #define kPrinterTypeUnknown 0
116 #define kPrinterTypeLaserWriter 1
117 #define kPrinterTypeLQ 2
118 #define kPrinterTypeImageWriter 3
119
120 #define COLOR_RIBBON_INSTALLED 0x80 /* 0b10000000 */
121 #define SHEET_FEEDER_INSTALLED 0x40 /* 0b01000000 */
122 #define PAPER_OUT_ERROR 0x20 /* 0b00100000 */
123 #define COVER_OPEN_ERROR 0x10 /* 0b00010000 */
124 #define PRINTER_OFF_LINE 0x08 /* 0b00001000 */
125 #define PAPER_JAM_ERROR 0x04 /* 0b00000100 */
126 #define PRINTER_FAULT 0x02 /* 0b00000010 */
127 #define PRINTER_ACTIVE 0x01 /* 0b00000001 */
128
129 char *PAPFunctionNames[] = {
130 "<unknown>", /* 0 */
131 "OpenConn", /* 1 */
132 "OpenConnReply", /* 2 */
133 "SendData", /* 3 */
134 "Data", /* 4 */
135 "Tickle", /* 5 */
136 "CloseConn", /* 6 */
137 "CloseConnReply", /* 7 */
138 "SendStatus", /* 8 */
139 "Status", /* 9 */
140 };
141
142 void SetPrinterOptions(Boolean colorRibbon, Boolean sheetFeeder,
143 Boolean iw1Mode, Boolean noReverse)
144 {
145 gColorRibbon = colorRibbon;
146 gSheetFeeder = sheetFeeder;
147 gIW1Mode = iw1Mode;
148 gNoReverse = noReverse;
149 }
150
151 void GetPrinterOptions(Boolean *colorRibbon, Boolean *sheetFeeder,
152 Boolean *iw1Mode, Boolean *noReverse)
153 {
154 if (colorRibbon) { *colorRibbon = gColorRibbon; }
155 if (sheetFeeder) { *sheetFeeder = gSheetFeeder; }
156 if (iw1Mode) { *iw1Mode = gIW1Mode; }
157 if (noReverse) { *noReverse = gNoReverse; }
158 }
159
160 int GetActiveClientConnections(void)
161 {
162 return (gConnID) ? 1 : 0;
163 }
164
165 long JobsSpooled(void)
166 {
167 return jobsSpooled;
168 }
169
170 /* ---------------------------------------------------------------------------*/
171 /* server routines */
172
173 pascal int SLInit (EntityPtr printerName, /* AppleTalk entity name to serve */
174 int flowQuantum, /* # of 512-byte buffers for read */
175 PAPStatusPtr statusBuf, /* initial status is returned here */
176 int *refNum) /* server refNum for this instance */
177 {
178 OSErr err;
179 int len;
180 if (serverRefNum != 0) {
181 /* error: we already have a server up */
182 return aspNoMoreSess;
183 }
184 /* allocate buffers */
185 gReqPBPtr = (ATPPBPtr)(NewPtrClear(sizeof(ATPParamBlock)));
186 gSendReqPBPtr = (ATPPBPtr)(NewPtrClear(sizeof(ATPParamBlock)));
187 gSendRespPBPtr = (ATPPBPtr)(NewPtrClear(sizeof(ATPParamBlock)));
188 gTickleReqPBPtr = (ATPPBPtr)(NewPtrClear(sizeof(ATPParamBlock)));
189 gRReqBuf = NewPtrClear(kMaxPacketSize);
190 gSReqBuf = NewPtrClear(kMaxPacketSize);
191 gRRespBuf = NewPtrClear(kRespBufSize); /* 4624 bytes */
192 gSRespBuf = NewPtrClear(kMaxPacketSize); /* 578 bytes */
193 gSReqBdsPtr = (BDSPtr)(NewPtrClear(sizeof(BDSElement)*8));
194 gRRespBdsPtr = (BDSPtr)(NewPtrClear(sizeof(BDSElement)*8));
195 gSRespBdsPtr = (BDSPtr)(NewPtrClear(sizeof(BDSElement)*8));
196
197 gSendReqHdl = (ATATPRecHandle)NewHandleClear(atpSize);
198 MoveHHi((Handle)gSendReqHdl);
199 HLock((Handle)gSendReqHdl);
200 gSendReqPtr = *gSendReqHdl;
201
202 gSendRespHdl = (ATATPRecHandle)NewHandleClear(atpSize);
203 MoveHHi((Handle)gSendRespHdl);
204 HLock((Handle)gSendRespHdl);
205 gSendRespPtr = *gSendRespHdl;
206
207 gPrinterType = kPrinterTypeUnknown;
208 len = printerName->typeStr[0];
209 if (len == 11 && !memcmp(&printerName->typeStr[1],"LaserWriter",len)) {
210 gPrinterType = kPrinterTypeLaserWriter;
211 } else if (len == 2 && !memcmp(&printerName->typeStr[1],"LQ",len)) {
212 gPrinterType = kPrinterTypeLQ;
213 } else if (len == 11 && !memcmp(&printerName->typeStr[1],"ImageWriter",len)) {
214 gPrinterType = kPrinterTypeImageWriter;
215 }
216 if (statusBuf) {
217 /* set initial status (same as a Status response) */
218 (void)SetUpResponseData(kPAPStatus,(Ptr)&statusBuf);
219 }
220
221 Message(LOG_ERR,"\pStarting network setup");
222 err = OpenDriver("\p.MPP",&gMPPRef);
223 if (err != noErr) {
224 MessageWithIntValue(LOG_ERR_X,"\pAppleTalk could not be opened:", err);
225 return err;
226 }
227 err = GetNodeAddress(&gMyNode, &gMyNet);
228 if (err != noErr) {
229 MessageWithIntValue(LOG_ERR_X,"\pCould not obtain our address:", err);
230 return err;
231 }
232 gReqPBPtr->ATP.atpSocket = 0; /* dynamically allocate a socket */
233 gReqPBPtr->ATP.addrBlock.aNet = 0; /* accept requests from anyone */
234 gReqPBPtr->ATP.addrBlock.aNode = 0;
235 gReqPBPtr->ATP.addrBlock.aSocket = 0;
236 err = POpenATPSkt(gReqPBPtr, false); /* socket is returned in atpSocket */
237 gMyRecvSocket = (short)gReqPBPtr->ATP.atpSocket;
238 if (gMyRecvSocket == 0 || err != noErr || gReqPBPtr->ATP.ioResult != noErr) {
239 MessageWithIntValue(LOG_ERR_X,"\pReceive socket could not be opened:", err);
240 return err;
241 } else {
242 MessageWithNetAddr(LOG_WARN,"\pListening on",gMyNet,gMyNode,gMyRecvSocket);
243 }
244
245 gSendReqPBPtr->ATP.atpSocket = 0; /* dynamically allocate a socket */
246 gSendReqPBPtr->ATP.addrBlock.aNet = 0; /* accept responses from anyone */
247 gSendReqPBPtr->ATP.addrBlock.aNode = 0;
248 gSendReqPBPtr->ATP.addrBlock.aSocket = 0;
249 err = POpenATPSkt(gSendReqPBPtr, false); /* socket is returned in atpSocket */
250 gMySendSocket = (short)gSendReqPBPtr->ATP.atpSocket;
251 if (gMySendSocket == 0 || err != noErr || gSendReqPBPtr->ATP.ioResult != noErr) {
252 MessageWithIntValue(LOG_ERR_X,"\pSend socket could not be opened:",err);
253 return err;
254 } else {
255 MessageWithNetAddr(LOG_WARN,"\pSending on",gMyNet,gMyNode,gMySendSocket);
256 }
257
258 serverRefNum++;
259 if (refNum) {
260 *refNum = serverRefNum;
261 }
262 gFlowQuantum = flowQuantum;
263
264 err = PAPRegName(serverRefNum, printerName);
265 if (err == noErr) {
266 listening = 1;
267 }
268 return err;
269 }
270
271 pascal int PAPRegName (int refNum, /* server refNum for this name */
272 EntityPtr printerName) /* entity name to register */
273 {
274 OSErr err;
275 short index;
276 if (!refNum) {
277 return paramErr;
278 }
279 NTPtr = (NamesTableEntry*) NewPtrClear(sizeof(NamesTableEntry));
280 if (!NTPtr) {
281 return(MemError());
282 }
283 NTPtr->nt.nteAddress.aSocket = (char)gMyRecvSocket & 0x00FF;
284 p.NBPinterval = 3;
285 p.NBPcount = 3;
286 p.NBPverifyFlag = true;
287 p.NBPntQElPtr = (Ptr) NTPtr;
288 BlockMove(printerName->objStr,&(NTPtr->nt.entityData[0]),33L);
289 index = printerName->objStr[0] + 1;
290 BlockMove(printerName->typeStr,&(NTPtr->nt.entityData[index]),33L);
291 index += printerName->typeStr[0] + 1;
292 BlockMove(printerName->zoneStr,&(NTPtr->nt.entityData[index]),33L);
293 err = PRegisterName(&p,true);
294 if (err != noErr) {
295 MessageWithIntValue(LOG_ERR_X,"\pPRegisterName returned error", err);
296 }
297 while (p.MPPioResult == 1) ; /* poll until PRegisterName completes */
298 err = p.MPPioResult;
299 while (err == nbpDuplicate) {
300 printerName->objStr[0]++;
301 printerName->objStr[printerName->objStr[0]] = '1';
302 BlockMove(printerName->objStr,&(NTPtr->nt.entityData[0]),33L);
303 index = printerName->objStr[0] + 1;
304 BlockMove(printerName->typeStr,&(NTPtr->nt.entityData[index]),33L);
305 index += printerName->typeStr[0] + 1;
306 BlockMove(printerName->zoneStr,&(NTPtr->nt.entityData[index]),33L);
307 err = PRegisterName(&p,true);
308 while (p.MPPioResult == 1) ; /* try again once more */
309 err = p.MPPioResult;
310 }
311 /* save a copy of the name */
312 BlockMove(printerName, &name, sizeof(EntityName));
313 BlockMove(printerName->objStr,&name.objStr[0],33L);
314 /* we want to be able to print from our own node to the spooler */
315 if (err == noErr) {
316 p.SETSELF.newSelfFlag = 1;
317 err = PSetSelfSend(&p, false);
318 if (err == controlErr) { /* running on system 6? */
319 MessageWithIntValue(LOG_WARN_X,"\pCould not enable self-send mode:",err);
320 err = noErr; /* don't really need this to continue running */
321 }
322 }
323 return err;
324 }
325
326 pascal int PAPRemName (int refNum, /* server refNum for this name */
327 EntityPtr printerName) /* entity name to remove */
328 {
329 #pragma unused (printerName)
330 OSErr err;
331 if (gMyRecvSocket && NTPtr && refNum) {
332 p.NBPntQElPtr = (Ptr) &(NTPtr->nt.entityData[0]);
333 err = PRemoveName(&p,false);
334 if (err != noErr) {
335 MessageWithIntValue(LOG_ERR_X,"\pCould not unregister spooler:",err);
336 } else {
337 MessageWithQuotedString(LOG_ERR,"\pUnregistered",(char*)&NTPtr->nt.entityData[0]);
338 }
339 DisposPtr((Ptr) NTPtr);
340
341 gReqPBPtr->ATP.atpSocket = (char)gMyRecvSocket & 0x00FF;
342 err = PCloseATPSkt(gReqPBPtr,false);
343 if (err != noErr) {
344 MessageWithIntValue(LOG_ERR_X,"\pCould not close receive socket:",err);
345 } else {
346 Message(LOG_WARN,"\pReceive socket closed");
347 }
348 gSendReqPBPtr->ATP.atpSocket = (char)gMySendSocket & 0x00FF;
349 err = PCloseATPSkt(gSendReqPBPtr,false);
350 if (err != noErr) {
351 MessageWithIntValue(LOG_ERR_X,"\pCould not close send socket:",err);
352 } else {
353 Message(LOG_WARN,"\pSend socket closed");
354 }
355 } else {
356 err = badATPSkt;
357 }
358 return err;
359 }
360
361 pascal int HeresStatus (int refNum, /* server refNum for updated status */
362 PAPStatusPtr statusBuf) /* new status string */
363 {
364 #pragma unused (refNum, statusBuf)
365 return unimpErr; /* TBI */
366 }
367
368 pascal int SLClose (int refNum) /* server refNum from SLInit */
369 {
370 OSErr err;
371
372 /* Cancel any request which may be in flight */
373 CancelRequest();
374
375 /* Unregister from NBP and close our sockets */
376 err = PAPRemName(refNum, &name);
377
378 requestsPending = 0;
379 responsesPending = 0;
380 listening = 0;
381 serverRefNum = 0;
382 gMyRecvSocket = 0;
383 gMySendSocket = 0;
384 gPrinterType = kPrinterTypeUnknown;
385 respondedToFirstPostConnectionStatusReq = false;
386 gConnID = 0;
387 gDataSeq = 0;
388 gDataLen = 0;
389
390 /* Clean up allocations. */
391 if (gReqPBPtr) { DisposePtr((Ptr)(gReqPBPtr)); }
392 if (gSendReqPBPtr) { DisposePtr((Ptr)(gSendReqPBPtr)); }
393 if (gSendRespPBPtr) { DisposePtr((Ptr)(gSendRespPBPtr)); }
394 if (gTickleReqPBPtr) { DisposePtr((Ptr)(gTickleReqPBPtr)); }
395 if (gRReqBuf) { DisposePtr(gRReqBuf); }
396 if (gSReqBuf) { DisposePtr(gSReqBuf); }
397 if (gRRespBuf) { DisposePtr(gRRespBuf); }
398 if (gSRespBuf) { DisposePtr(gSRespBuf); }
399 if (gSReqBdsPtr) { DisposePtr((Ptr)(gSReqBdsPtr)); }
400 if (gRRespBdsPtr) { DisposePtr((Ptr)(gRRespBdsPtr)); }
401 if (gSRespBdsPtr) { DisposePtr((Ptr)(gSRespBdsPtr)); }
402 if (gSendReqHdl) { HUnlock((Handle)gSendReqHdl); DisposeHandle((Handle)gSendReqHdl); }
403 if (gSendRespHdl) { HUnlock((Handle)gSendRespHdl); DisposeHandle((Handle)gSendRespHdl); }
404
405 return err;
406 }
407
408 /* ---------------------------------------------------------------------------*/
409 /* client routines */
410
411 /* %%% These are all unimplemented at the moment, since we're a server only.
412 * Our implementation wants to make asynchronous ATP calls to avoid blocking
413 * on the network. We poll request and response status by calling CheckATP
414 * from the main runloop, but could alternatively provide ioCompletion routines.
415 * We would need to make synchronous ATP calls instead if this synchronous
416 * interface were to be implemented, or else spin up an inner runloop inside
417 * each API while we wait. There doesn't seem to be a good reason to do that.
418 */
419
420 pascal int PAPOpen (int *refNum, /* returned: connection ID ref */
421 EntityPtr printerName, /* AppleTalk entity name */
422 int flowQuantum, /* # of 512-byte buffers for read */
423 PAPStatusPtr statusBuf, /* returned: status string */
424 int *compState) /* returned: status, 0=success */
425 {
426 #pragma unused (refNum, printerName, flowQuantum, statusBuf, compState)
427 return unimpErr;
428 }
429
430 pascal int PAPStatus (EntityPtr printerName, /* AppleTalk entity name */
431 PAPStatusPtr statusBuf, /* returned: status string */
432 long int *nodeAddr) /* returned: node address */
433 {
434 #pragma unused (printerName, statusBuf, nodeAddr)
435 return unimpErr;
436 }
437
438 pascal int PAPRead (int refNum, /* connection ID from PAPOpen */
439 char *rxBuf, /* buffer in which to read the data */
440 int *rxBufLen, /* size of the data read on output */
441 int *eofByte, /* set to positive value when EOF */
442 int *compState) /* returned: status, 0=success */
443 {
444 #pragma unused (refNum, rxBuf, rxBufLen, eofByte, compState)
445 return unimpErr;
446 }
447
448 pascal int PAPWrite (int refNum, /* connection ID from PAPOpen */
449 char *txBuf, /* buffer with data to be written */
450 int txBufLen, /* size of the data to be written */
451 int eofByte, /* set to positive on last buffer */
452 int *compState) /* returned: status, 0=success */
453 {
454 #pragma unused (refNum, txBuf, txBufLen, eofByte, compState)
455 return unimpErr;
456 }
457
458 pascal int PAPClose (int refNum) /* connection ID from PAPOpen */
459 {
460 #pragma unused (refNum)
461 return unimpErr;
462 }
463
464 pascal int PAPUnload (void) /* close connections, free storage */
465 {
466 return unimpErr;
467 }
468
469 /* ---------------------------------------------------------------------------*/
470
471 void HandleRequest(void)
472 {
473 ATPPBPtr atp = gReqPBPtr;
474 char connID = *((char*)&atp->ATP.userData + 0);
475 char funcID = *((char*)&atp->ATP.userData + 1);
476 short seqID = atp->ATP.userData & 0x0000FFFF;
477 short net = atp->ATP.addrBlock.aNet;
478 char node = atp->ATP.addrBlock.aNode;
479 char socket = atp->ATP.addrBlock.aSocket;
480
481 if (funcID < kPAPOpenConn || funcID > kPAPStatus) {
482 funcID = 0; /* map unhandled functions to <unknown> string */
483 }
484 MessageReceivedFromNetAddr(LOG_INFO, funcID, false, net, node, socket);
485
486 if (gConnID == connID && connID != 0) {
487 gLastTickleRecd = TickCount(); /* update last activity on connection */
488 }
489 if (gConnID && connID && funcID == kPAPOpenConn) {
490 /* We only handle one connection at a time, and we already have one open.
491 Generally, a client trying to open a connection will keep retrying until
492 it succeeds or they decide to not wait anymore. If this is a duplicate
493 retry attempt for the already-open connection, then we'll drop it.
494 Otherwise, we reply with OpenConnReply "server busy" status, per IA:10-7.
495 */
496 if (gConnID == connID) {
497 return;
498 }
499 }
500
501 switch (funcID) {
502 case kPAPOpenConn:
503 if (gConnID) {
504 MessageWithIntValue(LOG_WARN_X,"\pIgnoring new connection",(int)connID & 0xFF);
505 SendResponse(connID, kPAPOpenConnReply);
506 return;
507 }
508 MessageWithNetAddr(LOG_ERR,"\pReceiving print job from", net, node, socket);
509 MessageWithIntValue(LOG_ERR,"\pOpening connection",(int)connID & 0xFF);
510 (void) OpenSpoolFile(gPrinterType);
511 gNet = net;
512 gNode = node;
513 gSocket = socket;
514 gConnSocket = gRReqBuf[0];
515 gFlowQuantum = gRReqBuf[1]; /* set before sending OpenConnReply */
516 if (gFlowQuantum < kPAPMinQuantum) { gFlowQuantum = kPAPMinQuantum; }
517 if (gFlowQuantum > kPAPMaxQuantum) { gFlowQuantum = kPAPMaxQuantum; }
518 BlockMove(&gRReqBuf[2], &gWaitTime, 2);
519 /* %%% gWaitTime is supposed to tell us which OpenConn to handle first.
520 * But we ignore it and just accept the first connection immediately. */
521 SendResponse(connID, kPAPOpenConnReply);
522 gConnID = connID; /* connection is now established */
523 StartTickling(); /* send periodic tickles */
524 break;
525 case kPAPOpenConnReply:
526 /* this is never sent as a request, only as a response */
527 break;
528 case kPAPSendData:
529 /* client is asking us to send data */
530 #if 0
531 /* %%% while this is correct behavior, if we miss the first SendData,
532 we will drop subsequent retries as dupes. Need to keep track
533 of what we processed successfully.
534 */
535 if (gQueryReqSeq >= seqID) {
536 MessageWithIntValue(LOG_INFO_X,"\pIgnoring dup SendData seq:",seqID);
537 break; /* avoid processing a duplicate request, per IA:10-11 */
538 }
539 #endif
540 /* remember transaction ID and new sequence ID */
541 gQueryReqSeq = seqID;
542 if (!gQueryReqTID) {
543 gQueryReqTID = atp->OTH2.transID;
544 }
545 if ((gPrinterType != kPrinterTypeLaserWriter) ||
546 (!gFinishedQuery && gQueryHdl && gQueryLength)) {
547 /* we can respond right away */
548 SendResponse(connID, kPAPData);
549 gFinishedQuery = true;
550 gQueryReqTID = 0;
551 } else {
552 /* need to pend the response until we've read Data with the query */
553 MessageWithIntValue(LOG_INFO,"\pPending response to TID:",gQueryReqTID);
554 /* %%% DEBUG
555 * It would seem logical to set gFinishedQuery=false here, so the
556 * next Data would be put into gQueryHdl instead of the spool file,
557 * but unfortunately actual data comes in before query data, and
558 * we quickly run out of memory. For now, gFinishedQuery cannot be
559 * set back to false again once the initial query has been processed.
560 * Need to better understand how to handle multiple SendData requests
561 * from the client which have different TIDs.
562 */
563 }
564 break;
565 case kPAPData:
566 /* this is never sent as a request, only as a response */
567 break;
568 case kPAPTickle:
569 /* client sent us a tickle so we don't time out, handled above */
570 break;
571 case kPAPCloseConn:
572 /* this is the normal sequence on success: client closes the connection */
573 if (gDataLen > 0) {
574 MessageWithIntValue(LOG_ERR,"\pReceived data bytes:", gDataLen);
575 jobsSpooled++;
576 }
577 CloseSpoolFile(gDataLen);
578 gDataLen = 0;
579 if (gConnID) {
580 StopTickling(); /* stop periodic tickles before connection closes */
581 CancelRequest(); /* cancel any outstanding requests so we don't wait */
582 MessageWithIntValue(LOG_ERR,"\pClosing connection",(int)connID & 0xFF);
583 SendResponse(connID, kPAPCloseConnReply);
584 gConnID = 0; /* connection is now closed */
585 respondedToFirstPostConnectionStatusReq = false;
586 gLastTickleRecd = 0L;
587 }
588 break;
589 case kPAPCloseConnReply:
590 /* this is never sent as a request, only as a response */
591 break;
592 case kPAPSendStatus:
593 SendResponse(connID, kPAPStatus);
594 if (gConnID && !respondedToFirstPostConnectionStatusReq) {
595 respondedToFirstPostConnectionStatusReq = true;
596 gDataSeq = 1;
597 gDataLen = 0;
598 SendRequest(gConnID, kPAPSendData); /* ask for data segment #1 */
599 }
600 break;
601 case kPAPStatus:
602 /* this is never sent as a request, only as a response */
603 break;
604 default:
605 MessageWithNetAddr(LOG_WARN,"\pGot unknown request from", net, node, socket);
606 break;
607 }
608 }
609
610 void CancelRequest(void)
611 {
612 OSErr err = noErr;
613 #if USING_PSENDREQUEST_API
614 if (gSendReqPBPtr->ATP.ioResult > noErr) {
615 err = PKillSendReq(gSendReqPBPtr, true);
616 gSendReqPBPtr->ATP.ioResult = reqAborted;
617 }
618 #else
619 if (gSendReqPtr->abResult > noErr) {
620 err = ATPReqCancel(gSendReqHdl, true);
621 gSendReqPtr->abResult = reqAborted;
622 }
623 #endif
624 if (responsesPending > 0) {
625 responsesPending--;
626 }
627 if (err != noErr && err != cbNotFound) {
628 MessageWithIntValue(LOG_WARN_X,"\pUnable to cancel request:",err);
629 }
630 }
631
632 #if USING_PSENDREQUEST_API
633 void HandleResponse_from_PSendRequest(void)
634 {
635 /* %%% PROBLEM %%%
636 * The ATPPBPtr (to ATPParamBlock) does not give us access to the
637 * user bytes for the response, separate from the request.
638 * Request user bytes are in MPPATPHeader's userData field, but
639 * where is the info we would find in ATATPRec's atpRspUData field?
640 */
641 ATPPBPtr atp = gSendReqPBPtr;
642 char connID = *((char*)&atp->ATP.userData + 0);
643 char funcID = *((char*)&atp->ATP.userData + 1);
644 char eofByte = *((char*)&atp->ATP.userData + 2);
645 short net = atp->ATP.addrBlock.aNet;
646 char node = atp->ATP.addrBlock.aNode;
647 char socket = atp->ATP.addrBlock.aSocket;
648
649 if (funcID < kPAPOpenConn || funcID > kPAPStatus) {
650 funcID = 0; /* map unhandled functions to <unknown> string */
651 }
652 MessageReceivedFromNetAddr(LOG_INFO,funcID, true, net, node, socket);
653
654 if (gConnID) {
655 if (gConnID == connID) {
656 gLastTickleRecd = TickCount(); /* update last activity for connection */
657 }
658 }
659
660 switch (funcID) {
661 case kPAPData:
662 MessageWithIntValue(LOG_INFO,"\pReceived data of length",
663 ((BDSPtr)atp->ATP.bdsPointer)->dataSize);
664 gDataLen += ((BDSPtr)atp->ATP.bdsPointer)->dataSize;
665 if (!eofByte) {
666 /* as long as we haven't hit EOF, keep asking for more data */
667 if (++gDataSeq == 0) {
668 gDataSeq = 1; /* reset to 1 if we wrap around, per IA 10-11 */
669 }
670 SendRequest(gConnID, kPAPSendData); /* ask for next data block */
671 }
672 break;
673 case kPAPCloseConnReply:
674 break;
675 default:
676 MessageWithNetAddr(LOG_WARN,"\pGot unknown response from", net, node, socket);
677 break;
678 }
679 }
680 #endif
681
682 void HandleResponse_from_ATPRequest(void)
683 {
684 ATATPRecPtr abp = gSendReqPtr;
685 char connID, funcID;
686 char eofByte;
687 short net = abp->atpAddress.aNet;
688 char node = abp->atpAddress.aNode;
689 char socket = abp->atpAddress.aSocket;
690 char *data = (char*)abp->atpRspBuf;
691 BDSPtr bds = abp->atpRspBDSPtr;
692 int numRsp = abp->atpNumRsp;
693 int index, dataLen, length = 0;
694
695 for (index=0; index < abp->atpNumRsp; index++) {
696 dataLen = ((BDSElement*)bds)[index].dataSize;
697 /* align response blocks so their data is contiguous */
698 BlockMove(((BDSElement*)bds)[index].buffPtr,data+length,dataLen);
699 length += dataLen;
700 }
701 abp->atpRspUData = ((BDSElement*)bds)[numRsp-1].userBytes;
702 connID = *((char*)&abp->atpRspUData + 0);
703 funcID = *((char*)&abp->atpRspUData + 1);
704 eofByte = *((char*)&abp->atpRspUData + 2);
705
706 if (funcID < kPAPOpenConn || funcID > kPAPStatus) {
707 funcID = 0; /* map unhandled functions to <unknown> string */
708 }
709 MessageReceivedFromNetAddr(LOG_INFO,funcID, true, net, node, socket);
710
711 if (gConnID) {
712 if (gConnID == connID) {
713 gLastTickleRecd = TickCount(); /* update last activity for connection */
714 }
715 }
716
717 switch (funcID) {
718 case kPAPData:
719 MessageWithIntValue(LOG_INFO,"\pReceived data with length",length);
720 MessageWithIntValue(LOG_DEBUG,"\p-- eofByte:",(short)eofByte);
721 MessageWithIntValue(LOG_DEBUG,"\p-- dataSeq:",(short)gDataSeq);
722 MessageWithDataPointer(LOG_DEBUG,"\p-- data:",data,length);
723
724 /* The LQ driver will send us a 2-byte Data response as the first
725 * and last packets of the data sequence (at minimum). If we haven't
726 * responded to its SendData request, this will be a self-id command
727 * (esc-?, or 0x1B3F). If we have responded to its SendData with the
728 * 'LQ1\r' identity string, then the data we get here will be simply 'LQ'.
729 * Either way, we need to filter this out as it isn't meant to be printed.
730 */
731 if ((length == 2) && (gPrinterType == kPrinterTypeLQ) &&
732 ((*(data+0) == 0x1B && *(data+1) == 0x3F) ||
733 (*(data+0) == 0x4C && *(data+1) == 0x51))) {
734 gDataSeq++;
735 Message(LOG_INFO,"\pFiltering 2 data bytes (LQ self-id)");
736
737 } else if (!gFinishedQuery && gPrinterType == kPrinterTypeLaserWriter) {
738 /* data sent to us after their SendData is a query, until '%%EOF' */
739 OSErr err;
740 gDataSeq++;
741 /* save this query; we will need it to compose a response */
742 if (!gQueryHdl) {
743 /* first part of query */
744 gQueryHdl = NewHandle((Size)length);
745 /* handle error if this call fails, no pun intended */
746 if ((err=MemError()) == noErr) {
747 HLock(gQueryHdl);
748 BlockMove(data,*gQueryHdl,length);
749 HUnlock(gQueryHdl);
750 gQueryLength = length;
751 }
752 } else {
753 /* continuation of query */
754 SetHandleSize(gQueryHdl,(Size)gQueryLength+length);
755 /* handle error if this call fails, no pun intended */
756 if ((err=MemError()) == noErr) {
757 HLock(gQueryHdl);
758 BlockMove(data,*gQueryHdl+(Size)gQueryLength,length);
759 HUnlock(gQueryHdl);
760 gQueryLength += length;
761 }
762 }
763 if (err != noErr) {
764 MessageWithIntValue(LOG_ERR_X,"\pCould not save query:",err);
765 return;
766 }
767 MessageWithIntValue(LOG_INFO,"\pFiltering query bytes:",length);
768 MessageWithDataPointer(LOG_DEBUG,"\p-- end: ",data+(length-6),5);
769 if ((length>5 && !memcmp("%%EOF",data+(length-6),5)) ||
770 (length>9 && !memcmp("unknown\r\r",data+(length-10),9))) {
771 /* got EOF or unknown at end of PostScript query;
772 * now we can reply to it
773 */
774 MessageWithIntValue(LOG_INFO,"\pGot EOF; total query:",gQueryLength);
775 if (gQueryReqTID) {
776 SendResponse(gConnID, kPAPData);
777 gFinishedQuery = true;
778 gQueryReqTID = 0;
779 } /* else we have to wait for the SendData request to be retried */
780 }
781 eofByte = 0; /* clear so we ask for more data */
782
783 } else {
784 WriteDataToSpoolFile(length,data);
785 gDataLen += length;
786 if (++gDataSeq == 0) {
787 gDataSeq = 1; /* reset to 1 if we wrap around, per IA 10-11 */
788 }
789 }
790 if (!eofByte) {
791 /* as long as we haven't seen EOF, keep asking for more data */
792 SendRequest(gConnID, kPAPSendData); /* ask for next data block */
793 } else {
794 /* this is EOF */
795 /* NOTE: we usually don't get here with ImageWriter or LQ drivers.
796 * The driver is supposed to set eof in the last data packet, but
797 * more frequently, it just sends CloseConn to indicate that it's
798 * done with us.
799 */
800 MessageWithIntValue(LOG_ERR,"\pReceived data bytes:", gDataLen);
801 CloseSpoolFile(gDataLen);
802 if (gDataLen != 0) {
803 gDataLen = 0;
804 jobsSpooled++;
805 }
806 /* we don't send CloseConn on EOF; workstation does, per IA:10-11 */
807 }
808 break;
809 case kPAPCloseConnReply:
810 break;
811 default:
812 MessageWithNetAddr(LOG_WARN,"\pGot unknown response from", net, node, socket);
813 break;
814 }
815 }
816
817 void HandleResponse(void)
818 {
819 #if USING_PSENDREQUEST_API
820 HandleResponse_from_PSendRequest();
821 #else
822 HandleResponse_from_ATPRequest();
823 #endif
824 }
825
826 int SendRequest_with_ATPRequest(char connID, char funcID)
827 {
828 ATATPRecPtr abp = gSendReqPtr;
829 short destSocket = (gConnSocket) ? gConnSocket : gSocket;
830 short retryInterval, retryCount;
831 int nBufs, dataLen;
832 OSErr err;
833
834 if (abp->abResult > noErr) {
835 /* %%% should we allocate a new ATATPRec and free in ioCompletion? */
836 /* %%% or does this just mean the connection has closed abnormally? */
837 Message(LOG_WARN_X,"\pRequest still in flight, cannot send yet");
838 return tooManyReqs;
839 }
840 retryInterval = 5; /* default timeout for our requests */
841 retryCount = 1;
842 if (funcID == kPAPSendData) {
843 /*retryInterval = 5; /* should be 15 seconds, per IA 10-10, but use 5 for now */
844 retryCount = SHRT_MAX; /* keep retrying until success or connection timeout */
845 }
846 dataLen = SetUpRequestData(funcID, gSReqBuf);
847
848 /* set up BDS to buffer a response that comes in across multiple packets */
849 nBufs = BuildBDS(gRRespBuf,(Ptr)(gRRespBdsPtr),kRespBufSize);
850
851 abp->atpUserData = 0L;
852 BlockMove(&connID,(char*)&abp->atpUserData + 0, 1);
853 BlockMove(&funcID,(char*)&abp->atpUserData + 1, 1);
854 if (funcID == kPAPSendData) {
855 BlockMove(&gDataSeq,(char*)&abp->atpUserData + 2, 2);
856 }
857 abp->abOpcode = tATPRequest;
858 abp->abResult = 0;
859 abp->abUserReference = 0;
860 abp->atpAddress.aNet = gNet;
861 abp->atpAddress.aNode = gNode;
862 abp->atpAddress.aSocket = destSocket;
863 abp->atpSocket = (char)gMySendSocket & 0x00FF;
864 abp->atpReqCount = dataLen;
865 abp->atpDataPtr = gSReqBuf;
866 abp->atpRspBDSPtr = gRRespBdsPtr;
867 abp->atpRspBuf = gRRespBuf;
868 abp->atpRspSize = kRespBufSize;
869 abp->atpNumBufs = nBufs;
870 abp->atpXO = true;
871 abp->atpTimeOut = retryInterval;
872 abp->atpRetries = retryCount;
873
874 err = ATPSndRequest(gSendReqHdl,true); /* send this async so we aren't blocked */
875 if (err != noErr) {
876 MessageWithIntValue(LOG_ERR_X,"\pATPSndRequest returned", err);
877 } else {
878 responsesPending++;
879 MessageSentToNetAddr(LOG_INFO,funcID, false, gNet, gNode, (short)destSocket & 0xFF);
880 }
881 return err;
882 }
883
884 #if USING_PSENDREQUEST_API
885 int SendRequest_with_PSendRequest(char connID, char funcID)
886 {
887 ATPPBPtr atp = gSendReqPBPtr;
888 short destSocket = (gConnSocket) ? gConnSocket : gSocket;
889 short retryInterval, retryCount;
890 int dataLen = 0;
891 int nBufs = 0;
892 OSErr err;
893
894 if (atp->ATP.ioResult > noErr) {
895 /* we can't use this ATPPBPtr since it's still waiting for a response */
896 /* %%% should we allocate a new ATPParamBlock and free in ioCompletion? */
897 Message(LOG_WARN_X,"\pRequest still in flight, cannot send yet");
898 return tooManyReqs;
899 }
900 retryInterval = 5; /* default timeout for our requests */
901 retryCount = 1;
902 if (funcID == kPAPSendData) {
903 /*retryInterval = 5; /* should be 15 seconds, per IA 10-10, but use 5 for now */
904 retryCount = SHRT_MAX; /* keep retrying until success or connection timeout */
905 }
906 dataLen = SetUpRequestData(funcID, gSReqBuf);
907 /* %%% FIXME this is wrong; using request data buffer for response buffer */
908 nBufs = BuildBDS(gSReqBuf,(Ptr)(gSReqBdsPtr),kMaxPacketSize);
909
910 atp->ATP.userData = 0L;
911 BlockMove(&connID,(char*)&atp->ATP.userData + 0, 1);
912 BlockMove(&funcID,(char*)&atp->ATP.userData + 1, 1);
913 if (funcID == kPAPSendData) {
914 BlockMove(&gDataSeq,(char*)&atp->ATP.userData + 2, 2);
915 }
916 atp->ATP.ioCompletion = nil; /* we will poll for completion */
917 atp->ATP.atpSocket = (char)gMySendSocket & 0x00FF;
918 atp->ATP.atpFlags = atpXOvalue;
919 atp->ATP.addrBlock.aNet = gNet;
920 atp->ATP.addrBlock.aNode = gNode;
921 atp->ATP.addrBlock.aSocket = (char)destSocket & 0x00FF;
922 atp->ATP.reqLength = dataLen; /* length of request buffer */
923 atp->ATP.reqPointer = gSReqBuf; /* pointer to buffer holding request data */
924 atp->ATP.bdsPointer = (Ptr)gSReqBdsPtr; /* buffer to hold response BDS */
925 atp->OTH1.u0.numOfBuffs = kMaxResponses; /* number of response buffers expected */
926 atp->SREQ.timeOutVal = retryInterval;
927 atp->SREQ.retryCount = retryCount;
928 atp->SREQ.TRelTime = 0; /* minimum 30 second timeout for other end of connection */
929
930 err = PNSendRequest(atp,true); /* send this async so we aren't blocked */
931 if (err != noErr) {
932 MessageWithIntValue(LOG_ERR_X,"\pPNSendRequest returned", err);
933 } else {
934 responsesPending++;
935 MessageSentToNetAddr(LOG_INFO,funcID, false, gNet, gNode, (short)destSocket & 0xFF);
936 }
937 return err;
938 }
939 #endif
940
941 int SendRequest(char connID, char funcID)
942 {
943 #if USING_PSENDREQUEST_API
944 return SendRequest_with_PSendRequest(connID, funcID);
945 #else
946 return SendRequest_with_ATPRequest(connID, funcID);
947 #endif
948 }
949
950 int SendResponse(char connID, char funcID)
951 {
952 OSErr err;
953 int dataLen = 0;
954 int nBufs = 0;
955 int NumOfBufs = 0;
956 #if 0
957 long int thisBit;
958
959 /* Walk through the request bitmap and see how many response buffers you need. */
960 for (thisBit = 0; thisBit < 8; thisBit++) {
961 /* Each bit that is set corresponds to a buffer. */
962 if (BitTst(&gReqPBPtr->OTH1.u0.bitMap, thisBit) == true) {
963 /* Your routine to fill in the appropriate response data. */
964 dataLen += SetUpResponseData(funcID, gSRespBuf);
965 NumOfBufs = NumOfBufs + 1;
966 }
967 }
968 #else
969 /* our responses always fit into a single packet, for now */
970 dataLen += SetUpResponseData(funcID, gSRespBuf);
971 NumOfBufs = 1;
972 #endif
973 /* Put your response data into the BDS structure. */
974 nBufs = BuildBDS(gSRespBuf,(Ptr)(gSRespBdsPtr),dataLen);
975
976 gSendRespPBPtr->ATP.atpSocket = gReqPBPtr->ATP.atpSocket;
977 gSendRespPBPtr->ATP.atpFlags = atpEOMvalue; /* indicate end of message */
978
979 /* Send response to the machine that sent you the request. */
980 gSendRespPBPtr->ATP.addrBlock.aNet = gReqPBPtr->ATP.addrBlock.aNet;
981 gSendRespPBPtr->ATP.addrBlock.aNode = gReqPBPtr->ATP.addrBlock.aNode;
982 gSendRespPBPtr->ATP.addrBlock.aSocket = gReqPBPtr->ATP.addrBlock.aSocket;
983 gSendRespPBPtr->ATP.bdsPointer = (Ptr)(gSRespBdsPtr);
984 gSendRespPBPtr->OTH1.u0.numOfBuffs = NumOfBufs; /* send all of the responses back now */
985 gSendRespPBPtr->OTH2.bdsSize = nBufs; /* indicate how many responses you are sending */
986 gSendRespPBPtr->OTH2.transID = gReqPBPtr->OTH2.transID; /* use transID returned from the PGetRequest */
987
988 /* Set up ATP user data in response */
989 gSendRespPBPtr->ATP.userData = 0L;
990 BlockMove(&connID,(char*)&gSendRespPBPtr->ATP.userData + 0, 1);
991 BlockMove(&funcID,(char*)&gSendRespPBPtr->ATP.userData + 1, 1);
992 if (funcID == kPAPData) {
993 char eofMarker = (nBufs > 1) ? 0 : 1; /* %%% how to handle last buf? */
994 BlockMove(&eofMarker,(char*)&gSendRespPBPtr->ATP.userData + 2, 1);
995 }
996
997 /* err = PSendResponse(gSendRespPBPtr,false);*/
998 /* %%% PROBLEM %%%
999 /* PSendResponse is overwriting the 4-byte userData portion of the ATP header. */
1000 /* The ATPPBPtr does not give us a way to set the response user bytes! */
1001 /* Is this a misunderstanding and they need to go into a different field? */
1002 /* Instead, set up the old ATATPRec response struct and use ATPResponse. */
1003 {
1004 ATATPRecPtr abp = (ATATPRecPtr)gSendRespPtr;
1005 if (abp->abResult > noErr) {
1006 /* we can't use this ATATPRec, since a response is still being sent */
1007 /* %%% should we allocate a new ATATPRec and free in ioCompletion? */
1008 /* %%% or does this just mean the connection has closed abnormally? */
1009 Message(LOG_WARN_X,"\pResponse still being sent, cannot send yet");
1010 return tooManyReqs;
1011 }
1012 abp->abOpcode = tATPResponse;
1013 abp->abResult = 0;
1014 abp->atpSocket = gReqPBPtr->ATP.atpSocket;
1015 abp->atpEOM = true;
1016 abp->atpAddress.aNet = gReqPBPtr->ATP.addrBlock.aNet;
1017 abp->atpAddress.aNode = gReqPBPtr->ATP.addrBlock.aNode;
1018 abp->atpAddress.aSocket = gReqPBPtr->ATP.addrBlock.aSocket;
1019 abp->atpRspUData = gSendRespPBPtr->ATP.userData;
1020 abp->atpRspBuf = gSRespBuf;
1021 abp->atpRspSize = dataLen;
1022 abp->atpTransID = gReqPBPtr->OTH2.transID;
1023 /* if this is a Data response from us, it needs to go to the right TID */
1024 if (funcID == kPAPData && gQueryReqTID) {
1025 abp->atpTransID = gQueryReqTID;
1026 }
1027 err = ATPResponse(gSendRespHdl,true); /* send async so we aren't blocked */
1028 }
1029 if (err != noErr) {
1030 MessageWithIntValue(LOG_ERR_X,"\pATPResponse error", err);
1031 } else {
1032 short net = gSendRespPBPtr->ATP.addrBlock.aNet;
1033 char node = gSendRespPBPtr->ATP.addrBlock.aNode;
1034 char socket = gSendRespPBPtr->ATP.addrBlock.aSocket;
1035 MessageSentToNetAddr(LOG_INFO,funcID, true, net, node, socket);
1036 }
1037 return err;
1038 }
1039
1040 int SetUpRequestData(char funcID, Ptr reqBuf)
1041 {
1042 #pragma unused (reqBuf)
1043 int dataLen = 0;
1044 if (funcID == kPAPSendData ||
1045 funcID == kPAPTickle ||
1046 funcID == kPAPCloseConn ||
1047 funcID == kPAPCloseConnReply ||
1048 funcID == kPAPSendStatus) {
1049 return dataLen; /* these requests have no additional data to send */
1050 }
1051 /* %%% the only other request type that needs data is kPAPOpenConn,
1052 * but we don't make those requests yet.
1053 */
1054 return dataLen;
1055 }
1056
1057 int SetUpResponseData(char funcID, Ptr respBuf)
1058 {
1059 int index, dataLen = 0;
1060 unsigned char status = 0;
1061 respBuf[0] = 0;
1062 respBuf[1] = 0;
1063 respBuf[2] = 0;
1064 respBuf[3] = 0;
1065
1066 if (gColorRibbon) { status |= COLOR_RIBBON_INSTALLED; }
1067 if (gSheetFeeder) { status |= SHEET_FEEDER_INSTALLED; }
1068
1069 if (funcID == kPAPOpenConnReply) {
1070 respBuf[0] = (char)gMyRecvSocket & 0x00FF; /* our responding socket */
1071 respBuf[1] = gFlowQuantum; /* flow quantum (passed to us in OpenConn) */
1072 /* respBuf[2] = 0; /* result */
1073 /* respBuf[3] = 0; /* result */
1074 if (gConnID) {
1075 /* error result: not opening this connection request since we're busy */
1076 *((short*)&respBuf[2]) = aspServerBusy;
1077 }
1078 }
1079 if (funcID == kPAPOpenConnReply || funcID == kPAPStatus) {
1080 /* status string contents vary depending on printer type */
1081 if (gPrinterType == kPrinterTypeLaserWriter) {
1082
1083 /* The format of LaserWriter status strings is documented in
1084 * the first edition of the PostScript Language Reference Manual
1085 * (the red book), Appendix D, p.283. It can include up to 3
1086 * key:value pairs, is bracketed by %%[ and ]%%, with fields
1087 * separated by semicolons: job (omitted if none), status, and
1088 * source (omitted if idle). Inside AppleTalk 10-12 confirms
1089 * that this is a Pascal format string with a length byte.
1090 *
1091 * Possible values for the status key:
1092 * 'idle' (no job in progress)
1093 * 'busy' (executing PostScript)
1094 * 'waiting' (for I/O mid-job)
1095 * 'printing' (paper in motion)
1096 * 'PrinterError: <reason>' (no paper, jam, etc.)
1097 * 'initializing' (printer is starting up)
1098 * 'printing test page' (during startup)
1099 *
1100 * Possible values for the source key:
1101 * 'serial 9' (the 9-pin RS-422 serial port)
1102 * 'serial 25' (the 25-pin RS-232 serial port)
1103 * 'AppleTalk' (the network)
1104 *
1105 * Example status strings:
1106 * %%[ status: idle ]%%
1107 * %%[ job: Fred's Memo; status: busy; source: AppleTalk ]%%
1108 */
1109 index = 4;
1110 BlockMove("\p%%[ status: ",&respBuf[index],13);
1111 index += 13;
1112 if (!gFinishedQuery || !gDataLen) {
1113 BlockMove("waiting",&respBuf[index],7);
1114 index += 7;
1115 } else {
1116 BlockMove((gConnID) ? "busy":"idle",&respBuf[index],4);
1117 index += 4;
1118 }
1119 BlockMove(" ]%%",&respBuf[index],4);
1120 index += 4;
1121 respBuf[4] = index-5;
1122
1123 } else { /* ImageWriter or LQ */
1124 /* documented in AppleTalk Technical Note #9 "The PAP Status Buffer" */
1125 respBuf[4] = 2; /* length of status "string" (just 2 bytes of flags) */
1126 respBuf[5] = (char)status;
1127 respBuf[6] = (gConnID) ? 0x80 : 0x00; /* high bit set if printer busy */
1128 }
1129 dataLen = 5 + respBuf[4];
1130 }
1131 if (funcID == kPAPData) {
1132 if (gPrinterType == kPrinterTypeLaserWriter) {
1133 /* build response to current PostScript query */
1134 HLock(gQueryHdl);
1135 dataLen = BuildResponseToPSQuery((char*)*gQueryHdl, gQueryLength,
1136 (char*)respBuf, kMaxPacketSize);
1137 HUnlock(gQueryHdl);
1138 /* we have consumed the current query and can release it now */
1139 DisposeHandle(gQueryHdl);
1140 gQueryHdl = nil;
1141 gQueryLength = 0;
1142 MessageWithIntValue(LOG_INFO,"\pSending data with length",dataLen);
1143 MessageWithDataPointer(LOG_DEBUG,"\p-- data:",respBuf,dataLen);
1144
1145 } else if (gPrinterType == kPrinterTypeLQ) {
1146 /* possible options:
1147 * '1' = 15-inch carriage (always the case for the LQ)
1148 * 'C' = color ribbon installed
1149 * 'F' = sheet feeder installed
1150 * 'E' = envelope feeder installed
1151 * F and E are mutually exclusive, and are followed
1152 * by a numeral indicating the number of feeder bins.
1153 * 'P' = tractor drive in pull tractor position
1154 * examples:
1155 * 'LQ1\r' 'LQ1P\r' 'LQ1CF1\r' 'LQ1CE3\r'
1156 */
1157 index = 0;
1158 dataLen = 4; /* min length of self ID string bytes */
1159 respBuf[index++] = 'L'; /* first two bytes are 'LQ' */
1160 respBuf[index++] = 'Q';
1161 respBuf[index++] = '1'; /* 15-inch carriage (only option) */
1162 if (gColorRibbon) {
1163 respBuf[index++] = 'C'; /* color ribbon installed */
1164 dataLen += 1;
1165 }
1166 if (gSheetFeeder) {
1167 respBuf[index++] = 'F'; /* sheet feeder installed */
1168 respBuf[index++] = '1'; /* number of feeder bins */
1169 dataLen += 2;
1170 }
1171 respBuf[index++] = '\r'; /* carriage return (^M, aka 0x0D) */
1172
1173 } else if (gPrinterType == kPrinterTypeImageWriter) {
1174 index = 0;
1175 dataLen = 4; /* min length of self ID string bytes */
1176 respBuf[index++] = 'I'; /* first two bytes are 'IW' */
1177 respBuf[index++] = 'W';
1178 respBuf[index++] = '1'; /* 10-inch carriage, first digit */
1179 respBuf[index++] = '0'; /* 10-inch carriage, second digit */
1180 if (gColorRibbon) {
1181 respBuf[index++] = 'C'; /* color ribbon installed */
1182 dataLen += 1;
1183 }
1184 if (gSheetFeeder) {
1185 respBuf[index++] = 'F'; /* sheet feeder installed */
1186 dataLen += 1;
1187 }
1188 }
1189 }
1190 return dataLen;
1191 }
1192
1193 void SendTickle(void)
1194 {
1195 OSErr err;
1196 ATPPBPtr atp = gTickleReqPBPtr;
1197 char connID = gConnID;
1198 char funcID = kPAPTickle;
1199 short destSocket = (gConnSocket) ? gConnSocket : gSocket;
1200
1201 BlockMove(&connID,(char*)&atp->ATP.userData + 0, 1);
1202 BlockMove(&funcID,(char*)&atp->ATP.userData + 1, 1);
1203 atp->ATP.ioCompletion = nil;
1204 atp->ATP.atpSocket = (char)gMySendSocket & 0x00FF;
1205 atp->ATP.atpFlags = 0; /* must be sent in ALO mode, not XO mode (IA:10-9) */
1206 atp->ATP.addrBlock.aNet = gNet;
1207 atp->ATP.addrBlock.aNode = gNode;
1208 atp->ATP.addrBlock.aSocket = (char)destSocket & 0x00FF;
1209 atp->ATP.reqLength = 0; /* length of request buffer */
1210 atp->ATP.reqPointer = nil; /* pointer to buffer holding request data */
1211 atp->ATP.bdsPointer = (Ptr)nil; /* buffer to hold response BDS */
1212 atp->OTH1.u0.numOfBuffs = 0; /* number of response buffers expected */
1213 atp->SREQ.timeOutVal = 1; /* timeout in seconds before retrying */
1214 atp->SREQ.retryCount = 0; /* how many times to retry the request */
1215 atp->SREQ.TRelTime = 0; /* minimum 30 second timeout for other end of connection */
1216
1217 err = PNSendRequest(atp,true); /* send this async so we aren't blocked */
1218 if (err != noErr) {
1219 MessageWithIntValue(LOG_WARN_X,"\pCould not start tickle:", err);
1220 } else {
1221 MessageSentToNetAddr(LOG_INFO,funcID, false, gNet, gNode, (short)destSocket & 0xFF);
1222 }
1223 }
1224
1225 void StartTickling(void)
1226 {
1227 gLastTickleSent = 1L;
1228 gLastTickleRecd = TickCount();
1229 }
1230
1231 void StopTickling(void)
1232 {
1233 OSErr err = PKillSendReq(gTickleReqPBPtr,false);
1234 if (err != noErr && err != cbNotFound) {
1235 MessageWithIntValue(LOG_WARN_X,"\pCould not stop tickle:", err);
1236 } else {
1237 MessageWithIntValue(LOG_WARN,"\pStopped tickle for connection",(int)gConnID & 0xFF);
1238 }
1239 gLastTickleSent = 0L;
1240 }
1241
1242 void CloseConnection(void)
1243 {
1244 OSErr err;
1245 StopTickling();
1246 CancelRequest(); /* cancel outstanding request (if any) so we don't wait */
1247 err = SendRequest(gConnID, kPAPCloseConn);
1248 if (err != noErr) {
1249 MessageWithIntValue(LOG_ERR_X,"\pCloseConn request error", err);
1250 } else {
1251 MessageWithIntValue(LOG_ERR,"\pClosing connection",(int)gConnID & 0xFF);
1252 }
1253 gConnID = 0; /* connection is now closed */
1254 gDataLen = 0;
1255 gLastTickleRecd = 0L;
1256 respondedToFirstPostConnectionStatusReq = false;
1257 }
1258
1259 void CheckATP(void)
1260 {
1261 OSErr err;
1262 if (!listening || !gReqPBPtr || !gSendReqPBPtr || !gSendReqPtr) {
1263 return; /* no server started, or no buffers allocated */
1264 }
1265 if (gConnID != 0) {
1266 long curTicks = TickCount();
1267 if (gLastTickleRecd && (gLastTickleRecd + kIdleTimeoutTicks < curTicks)) {
1268 /* time to close this connection due to inactivity */
1269 MessageWithIntValue(LOG_ERR,"\pReceived data bytes:", gDataLen);
1270 CloseSpoolFile(gDataLen);
1271 CloseConnection();
1272 } else if (gLastTickleSent && (gLastTickleSent + kTickleTicks < curTicks)) {
1273 /* time to send a tickle */
1274 SendTickle();
1275 gLastTickleSent = curTicks;
1276 }
1277 }
1278 #if USING_PSENDREQUEST_API
1279 if (gSendReqPBPtr->ATP.ioResult <= noErr)
1280 #else
1281 if (gSendReqPtr->abResult <= noErr)
1282 #endif
1283 {
1284 /* either a sent request completed, or we haven't made any request yet */
1285 if (responsesPending > 0) {
1286 /* got an incoming response */
1287 responsesPending--;
1288 #if USING_PSENDREQUEST_API
1289 err = gSendReqPBPtr->ATP.ioResult;
1290 #else
1291 err = gSendReqPtr->abResult;
1292 #endif
1293 if (err != noErr) {
1294 /* don't report failure if connection is already closed */
1295 if (!(gConnID == 0 && err == reqFailed)) {
1296 MessageWithIntValue(LOG_ERR_X,"\pSendRequest returned", err);
1297 }
1298 if (gConnID) {
1299 CloseConnection();
1300 }
1301 } else {
1302 /* handle this response to our request */
1303 HandleResponse();
1304 }
1305 }
1306 }
1307 if (gReqPBPtr->ATP.ioResult <= noErr) {
1308 /* either an incoming request completed, or we haven't asked for one yet */
1309 if (requestsPending > 0) {
1310 /* got an incoming request */
1311 requestsPending--;
1312 err = gReqPBPtr->ATP.ioResult;
1313 if (err != noErr) {
1314 MessageWithIntValue(LOG_ERR_X,"\pGetRequest returned", err);
1315 } else {
1316 /* handle the request and send response */
1317 HandleRequest();
1318 }
1319 }
1320 /* start a new PGetRequest */
1321 gReqPBPtr->ATP.ioCompletion = nil; /* we will poll for completion */
1322 gReqPBPtr->ATP.atpSocket = gMyRecvSocket & 0x00FF; /* use our socket */
1323 gReqPBPtr->ATP.reqLength = kMaxPacketSize; /* on input, max length of request buffer */
1324 gReqPBPtr->ATP.reqPointer = gRReqBuf; /* pointer to buffer for incoming request data */
1325 err = PGetRequest(gReqPBPtr,true); /* issue asynchronous PGetRequest */
1326 if (err == noErr) {
1327 requestsPending++;
1328 } else {
1329 MessageWithIntValue(LOG_ERR_X,"\pPGetRequest error", err);
1330 }
1331 }
1332 }