| 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 |
} |