//lpdclient.h
#if !defined(__LPDCLIENT_H__)
#define __LPDCLIENT_H__
#include "lpdsystem.h"
#include "lpdsockets.h"
#include "lpdfilesystem.h"
#define MAX_BUF 1024
class Lpdclient {
public:
//public interface methods
TInt Initialise();
TInt Paused() const;
TInt Resume();
TInt ResultQueued();
TInt Result() const;
//public data - never read by this class
TBuf<64> resultString;
private:
//State machine methods
TInt ProcessResult(TInt resultCode);
TInt DoNext();
//LPD protocol methods
void SendQueueStateRequestA();
void GetQueueStateResponseA();
void SendSelectQueueA();
void SendDataFileHeaderA();
void SendDataA();
void SendFileTerminatorA();
void SendControlFileHeaderA();
void SendControlFileA();
void GetLpdResponseA();
//File methods
TInt FindPrintFile();
TInt OpenPrintFile();
void ReadPrintFileA();
void ClosePrintFile();
TInt DeletePrintFile();
//member objetcs
Lpdfilesystem filesystem;
Lpdsockets sockets;
Lpdtimer timer;
Lpdsemaphore semaphore;
//application data
TBuf<MAX_BUF> buf;
TBuf<1> resp;
TBuf<255> printFileName;
TBuf<255> hostName;
TBuf<255> queueName;
TInt job;
TInt fileSize;
TInt socketNumber;
//state machine data
TBuf<64> successString;
TBuf<64> pendingString;
TInt successResult;
TInt pendingResult;
TInt paused;
TInt result;
enum States {
EFindFile,
EFindFilePending,
EWait,
EFileOpen,
EFileOpenPending,
ENewJob,
EGetRemoteAddr,
EOpenSocketForJob,
EOpenSocketForJobPending,
EHostConnectForJob,
ESelectQueue,
EGetLPDResponse,
ECheckLPDResponse,
EDFHSend,
EDFSend,
EDFReadData,
EDFSendData,
EDFSendTerm,
ECFHSend,
ECFSend,
EDisconnectAndRetry,
EDisconnect,
EDeleteFile,
EOpenSocketForQueue,
EOpenSocketForQueuePending,
EHostConnectForQueue,
ESendQueueStateRequest,
EGetQueueStateResponse,
EGetQueueStateResponsePending,
EExit
};
States state;
States successState;
States pendingState;
States followonState;
States errorState;
};
#endif
//lpdclient.cpp v1.2
#include "lpdclient.h"
#define LPD_PORT 515
#define LPD_LHOST "psion"
#define LPD_RUSER "psion"
#define LPD_OK 0
TInt Lpdclient::Initialise() {
paused=FALSE;
job=0;
resultString.Format(_L("Failed to Initialise\n"));
if (timer.Initialise()!=0) return KErrGeneral;
if (semaphore.Initialise()!=0) return KErrGeneral;
if (sockets.Initialise()!=0) return KErrGeneral;
if (filesystem.Initialise()!=0) return KErrGeneral;
state=EExit;
resultString=_L("Initialised OK\n");
socketNumber=1023;
successString=_L("");
successState=EFindFile;
successResult=KErrNone;
pendingString=_L("");
pendingResult=1;
pendingState=EExit;
errorState=EExit;
semaphore.SignalA(KErrNone);
return 0;
}
TInt Lpdclient::Paused() const {
return paused;
}
TInt Lpdclient::Resume() {
if (paused) {
paused=FALSE;
resultString=_L("");
return DoNext();
}
return 0;
}
TInt Lpdclient::ResultQueued() {
//Handle events
if (sockets.ResultQueued()) {
result = ProcessResult(sockets.Result());
return TRUE;
}
if (timer.ResultQueued()) {
result = ProcessResult(timer.Result());
return TRUE;
}
if (semaphore.ResultQueued()) {
result = ProcessResult(semaphore.Result());
return TRUE;
}
return FALSE;
}
TInt Lpdclient::Result() const{
return result;
}
TInt Lpdclient::ProcessResult(TInt resultCode) {
if(resultCode==pendingResult) {
//go on to pending state
state=pendingState;
resultString=pendingString;
if (!paused) return DoNext();
return 0;
}
if(resultCode==successResult) {
//go on to next state
state=successState;
resultString=successString;
if (!paused) return DoNext();
return 0;
}
//Only gets here if theres an error
switch (resultCode) { //an error code
case KErrEof:
resultString=_L("EOF\n");
break;
case KErrCancel:
resultString=_L("cancelled\n");
break;
case KErrTimedOut:
resultString=_L("timed out\n");
break;
case KErrCouldNotConnect:
resultString=_L("could not connect\n");
break;
default:
resultString.Format(_L("error %d\n"),resultCode);
break;
}
resultString+=_L("PAUSED\n");
paused=TRUE;
state=errorState;
return 0;
}
TInt Lpdclient::DoNext() {
successResult=KErrNone; //Can be overriden in certain steps
successString=_L(""); //Only active in states where overridden
pendingResult=1; //Only active in states where overridden
switch (state) {
case EFindFile:
resultString+=_L("Looking for print file...");
pendingString=_L("");
case EFindFilePending:
successString=_L("found\n");
successState=ENewJob;
pendingResult=KErrNotFound;
pendingState=EWait;
followonState=EFindFilePending;
errorState=EExit;
semaphore.SignalA(FindPrintFile());
break;
case EWait: //Used multiple times in process
successState=followonState;
timer.WaitA(20);
break;
case ENewJob:
job=job+1;
resultString.AppendFormat(_L("Submitting job #%d\n"),job);
successState=EFileOpen;;
semaphore.SignalA(KErrNone);
break;
case EFileOpen:
resultString.AppendFormat(_L("Opening %s\n"),printFileName.PtrZ());
resultString+=_L("Waiting for spooling to stop...");
case EFileOpenPending:
successState=EGetRemoteAddr; successString=_L("spooling stopped\n");
pendingResult=KErrInUse; pendingState=EWait; followonState=EFileOpenPending;
errorState=EFindFile;
semaphore.SignalA(OpenPrintFile());
break;
case EGetRemoteAddr:
resultString.AppendFormat(_L("Looking up address for %s..."),hostName.PtrZ());
successString=_L("OK\n");
sockets.GetRemoteAddressA(hostName);
successState=EOpenSocketForJob;
errorState=EGetRemoteAddr;
break;
case EOpenSocketForJob:
resultString+=_L("Opening socket...");
case EOpenSocketForJobPending:
if (socketNumber--< 600) socketNumber=1023;
successString+=(_L("OK\n"));
successState=EHostConnectForJob;
pendingResult=KErrInUse;
pendingState=EOpenSocketForJobPending;
semaphore.SignalA(sockets.Open(socketNumber));
break;
case EHostConnectForJob:
resultString.AppendFormat(_L("Conecting to host %s..."),hostName.PtrZ());
successString=_L("OK\n");
sockets.ConnectToHostA(LPD_PORT);
successState=ESelectQueue;
break;
case ESelectQueue:
resultString.AppendFormat(_L("Selecting queue %s..."),queueName.PtrZ());
successString=_L("OK\n");
SendSelectQueueA();
successState=EGetLPDResponse;
followonState=EDFHSend;
errorState=EDisconnectAndRetry;
break;
case EGetLPDResponse: //used multiple times in one sequence
resultString+=_L("Waiting for LPD response...");
successString=_L("OK\n");
successState=ECheckLPDResponse;
GetLpdResponseA();
break;
case ECheckLPDResponse: //always follows GetLPDresponse
if (resp[0]==0) {
successState=followonState;
}
else {
resultString.Format(_L("LPD error %d\n"),resp[0]);
successState=EDisconnectAndRetry;
resp[0]=0;
}
semaphore.SignalA(KErrNone);
break;
case EDFHSend:
resultString+=_L("Sending DFH...");
successString=_L("OK\n");
successState=EGetLPDResponse;
followonState=EDFSend;
SendDataFileHeaderA();
break;
case EDFSend:
resultString+=_L("Sending DF...");
pendingString=_L(".");
followonState=ECFHSend;
case EDFReadData:
successResult=KErrEof;
successState=EDFSendTerm;
pendingResult=KErrNone;
pendingState=EDFSendData;
ReadPrintFileA();
break;
case EDFSendData: //only used coupled to EDFReadData
successState=EDFReadData;
SendDataA();
break;
case EDFSendTerm:
successString=_L("OK\n");
successState=EGetLPDResponse;
SendFileTerminatorA();
break;
case ECFHSend:
resultString+=_L("Sending CFH...");
successString=_L("OK\n");
successState=EGetLPDResponse;
followonState=ECFSend;
SendControlFileHeaderA();
break;
case ECFSend:
resultString+=_L("Sending CF...");
successString=_L("OK\n");
successState=EGetLPDResponse;
followonState=EDisconnect;
SendControlFileA();
break;
case EDisconnect:
resultString+=_L("Disconnecting...");
resultString+=_L("OK\n");
successState=EDeleteFile;
ClosePrintFile();
sockets.Close();
semaphore.SignalA(KErrNone);
break;
case EDisconnectAndRetry:
resultString+=_L("Retrying Job\n");
successState=ENewJob;
ClosePrintFile();
sockets.Close();
timer.WaitA(20);
break;
case EDeleteFile:
resultString.AppendFormat(_L("Deleting %s..."),printFileName.PtrZ());
successString=_L("OK\n");
successState=EOpenSocketForQueue;
errorState=EExit;
semaphore.SignalA(DeletePrintFile());
break;
case EOpenSocketForQueue:
resultString=_L("Opening socket...");
case EOpenSocketForQueuePending:
if (socketNumber--< 600) socketNumber=1023;
successState=EHostConnectForQueue;
successString=_L("OK\n");
pendingResult=KErrInUse;
pendingState=EOpenSocketForQueuePending;
semaphore.SignalA(sockets.Open(socketNumber));
break;
case EHostConnectForQueue:
resultString.AppendFormat(_L("Conecting to host %s..."),hostName.PtrZ());
successString=_L("OK\n");
successState=ESendQueueStateRequest;
errorState=EOpenSocketForQueue;
sockets.ConnectToHostA(LPD_PORT);
break;
case ESendQueueStateRequest:
resultString+=_L("Sending queue state request...");
successString=_L("OK\n");
successState=EGetQueueStateResponse;
SendQueueStateRequestA();
break;
case EGetQueueStateResponse:
resultString+=_L("Queue state response:\n");
case EGetQueueStateResponsePending:
successResult=KErrEof;
successState=EFindFile;
pendingResult=KErrNone;
pendingState=EGetQueueStateResponsePending;
GetQueueStateResponseA();
break;
case EExit:
resultString+=_L("Fatal Error - exiting\n");
return KErrGeneral;
break;
default:
resultString+=_L("Bad state - exiting\n");
return KErrGeneral;
break;
}
return KErrNone;
}
//LPD protocol methods
void Lpdclient::SendSelectQueueA() {
buf.Format(_L("\2%s\12"),queueName.PtrZ());
sockets.SendA(buf);
}
void Lpdclient::SendDataFileHeaderA() {
buf.Format(_L("\3%d dfA%03d%s\12"),fileSize,job,LPD_LHOST);
sockets.SendA(buf);
}
void Lpdclient::SendDataA() {
sockets.SendA(buf);
}
void Lpdclient::SendFileTerminatorA() {
buf.Fill(0,1); // file terminator
sockets.SendA(buf);
}
void Lpdclient::SendControlFileHeaderA() {
TInt controlFileBytes;// length of control file
buf.Format(_L("H%s\nP%s\nfdfA%03d%s\n"),LPD_LHOST,LPD_RUSER,job,LPD_LHOST);
controlFileBytes = buf.Length();
buf.Format(_L("\2%d cfA%03d%s\12"),controlFileBytes,job,LPD_LHOST);
sockets.SendA(buf);
}
void Lpdclient::SendControlFileA() {
buf.Format(_L("H%s\nP%s\nfdfA%03d%s\n"),LPD_LHOST,LPD_RUSER,job,LPD_LHOST);
buf.Append(0);
sockets.SendA(buf);
}
void Lpdclient::GetLpdResponseA() {
sockets.RecvA(resp);
}
void Lpdclient::SendQueueStateRequestA() {
buf.Format(_L("\3%s\12"),queueName.PtrZ());
sockets.SendA(buf);
}
void Lpdclient::GetQueueStateResponseA() {
sockets.RecvOneOrMoreA(pendingString);
}
//File methods
TInt Lpdclient::FindPrintFile() {
// scans directory once for a valid printile name,
// returns the name of the first valid PRINT file found
TBufC<16> fileSpec = _L("*.lpd");
TBufC<16> pathSpec = _L("C:\\");
printFileName=_L("");
TInt rc=filesystem.Find(fileSpec,pathSpec,printFileName);
if (rc==0) {
if (printFileName.Length() < 12) return KErrBadName;
if (printFileName.Locate('@') < 5) //012345678901
return KErrBadName; //c:\ab@cd.lpd
else
queueName=printFileName.Mid(3,printFileName.Locate('@')-3);
if (printFileName.Locate('.') < (printFileName.Locate('@')+2))
return KErrBadName;
else
hostName=printFileName.Mid(printFileName.Locate('@')+1,printFileName.Locate('.')-printFileName.Locate('@')-1);
}
return rc;
}
TInt Lpdclient::OpenPrintFile() {
fileSize=0;
TInt rc=filesystem.Open(printFileName);
if (rc==0){
fileSize=filesystem.Size();
}
return rc;
}
void Lpdclient::ReadPrintFileA() {
TInt rc = filesystem.Read(buf,MAX_BUF);
if (rc==KErrNone) {
if (buf.Length()==0) {
semaphore.SignalA(KErrEof);
return;
}
}
semaphore.SignalA(rc);
}
void Lpdclient::ClosePrintFile() {
filesystem.Close();
}
TInt Lpdclient::DeletePrintFile() {
return filesystem.Delete(printFileName);
}