/* lpdClient.c v. 1.01*/

/* Changes for v. 1.01  */
/*       added leading "H" to control file           */
/*   tidied up messages                          */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <sys/stat.h> /* filesize */
#include <unistd.h> /* close */
#include <dirent.h> 
#include <string.h>

#define LPD_PORT 515
#define LPD_LHOST "psion"
#define LPD_RUSER "psion"
#define LPD_OK 0x00

#define MAX_BUF 1024

int connect_to_lpd(char* host);
int getprintfile(char* fname, char* host, char* queue);

int main (int argc, char *argv[]) {

  int sd;   //Socket desctiptor for tcp link to lpd on remote host
  int rc;   //Result code
  char resp;//LPD response

  char pfname[255]; //Local printfile name
  char host[255];   //Remote host name
  char queue[255];  //Remote lpd queue name 
  
  struct stat file_attrib; //structure to store file attributes
  FILE * fd;      //file descriptor for local print file
  int cnt_file_bytes; //length of control file

  char buf[MAX_BUF];    //string buffer
  int sz_buf;      //occupied size of buffer 
  int job=0;      //lpd print job number
  
  while (1) {  //loop for ever
  
    job++;
  
    printf("Waiting for printfile.....\n");
    rc=0;
    while(rc!=1) { //while a valid file is not found
      sleep (2); //dont keep thrashing!
      rc=getprintfile(pfname,host,queue);
      if (rc==-1return 1;   //directory doesnt exist
    }
    printf("%s found!\n",pfname);

    printf("Waiting for spooling to stop....\n");
    fd=0;
    while(fd==0) {  //while a valid file descriptor is not obtained
      rc=stat(pfname,&file_attrib);
      if (rc != 0 ) {
        printf("file disappeared while spooling!\n");
        break;
      }
      
      fd=fopen(pfname,"r");
      if (fd != 0break//file is open - spooling has stopped
      sleep(2);
    }
    printf("Stopped!\n");
  
    //get final size of printfile
    rc=stat(pfname,&file_attrib);
    if (rc !=0) { sleep(2); continue;}
  
    //connect to lpd daemon on remote host
    printf("Connecting to %s ....\n",host);
    while (1) {
      sd=connect_to_lpd(host);
      if (sd >0break;
      sleep (5);
    }
    printf("Success!\n");
    
    //send print job

    //Select queue
    printf("Connecting to queue %s ....\n",queue);
    sz_buf=sprintf(buf,"\2%s\12",queue);
    rc = send(sd,buf,sz_buf, 0);
    if (!rc) {printf("Send error %d!\n",rc); fclose(fd); close(sd); continue;}
    rc = recv(sd,&resp,10); // waits for char
    if (!rc) {printf("Receive error %d!\n",rc); fclose(fd); close(sd); continue;}
    if (resp!=LPD_OK) {printf("LPD error %d!\n",resp); fclose(fd); close(sd); continue;}
    printf("Success!\n"); 

    //Send datafile header
    printf("Sending datafile header....\n");
    sz_buf=sprintf(buf,"\3%d dfA%03d%s\12",file_attrib.st_size,job,LPD_LHOST);
    rc = send(sd,buf,sz_buf, 0);
    if (!rc) {printf("Send error %d!\n",rc); fclose(fd); close(sd); continue;}
    rc = recv(sd,&resp,10); // waits for char
    if (!rc) {printf("Receive error %d!\n",rc); fclose(fd); close(sd); continue;}
    if (resp!=LPD_OK) {printf("LPD error %d!\n",resp); fclose(fd); close(sd); continue;}
    printf("Success!\n");

    //Send datafile
    printf("Sending datafile ....\n");
    sz_buf = fread(buf,1,MAX_BUF,fd);
    while (sz_buf != 0) {
      rc = send(sd,buf,sz_buf, 0);
      if (!rc) break;   
      sz_buf = fread(buf,1,MAX_BUF,fd);
    }
    fclose (fd);
    if (!rc) {printf("Send error %d!\n",rc); close(sd); continue;}
    rc = send(sd,"\0",10);
    if (!rc) {printf("Send error %d!\n",rc); close(sd); continue;}
    rc = recv(sd,&resp,10); // waits for char
    if (!rc) {printf("Receive error %d!\n",rc); close(sd); continue;}
    if (resp!=LPD_OK) {printf("LPD error %d!\n",resp); close(sd); continue;}
    printf("Success!\n"); 

    //Size control file
    cnt_file_bytes=sprintf(buf,"H%s\nP%s\nfdfA%03d%s\n",LPD_LHOST,LPD_RUSER,job,LPD_LHOST);

    //Send control file header
    printf("Sending control file header....\n");
    sz_buf=sprintf(buf,"\2%d cfA%03d%s\12",cnt_file_bytes,job,LPD_LHOST);
    rc = send(sd,buf,sz_buf, 0);
    if (!rc) {printf("Send error %d!\n",rc); close(sd); continue;}
    rc = recv(sd,&resp,10); // waits for char
    if (!rc) {printf("Receive error %d!\n",rc); close(sd); continue;}
    if (resp!=LPD_OK) {printf("LPD error %d!\n",resp); close(sd); continue;}
    printf("Success!\n"); 

    //Send control file
    printf("Sending control file....\n");
    sz_buf=sprintf(buf,"H%s\nP%s\nfdfA%03d%s\n",LPD_LHOST,LPD_RUSER,job,LPD_LHOST);
    rc = send(sd,buf,sz_buf, 0);
    if (!rc) {printf("Send error %d!\n",rc); close(sd); continue;}
    rc = send(sd,"\0",10);
    if (!rc) {printf("Send error %d!\n",rc); close(sd); continue;}
    rc = recv(sd,&resp,10); // waits for char
    if (!rc) {printf("Receive error %d!\n",rc); close(sd); continue;}
    if (resp!=LPD_OK) {printf("LPD error %d!\n",resp); close(sd); continue;}
    printf("Success!\n");

    //disconnect from remote host
    printf("Print job successfully submitted!\n");
    printf("Disconnecting from %s.\n",host);
    close(sd);
    sleep(2);
    
    //delete local spool file
    while(1) {
      rc=stat(pfname,&file_attrib);
      if (rc != 0break//file doesnt exist
      printf("Deleting %s....\n",pfname);
      rc= remove(pfname);
      if (rc != 0) perror("Error!\n");
      sleep(1);
    }
    printf("Success!\n");
  }
return 0;
}

int getprintfile(char* pfname, char* host, char* queue){
  
  /*scans directory once for a valid printfile name,
  returns the name of the first print file found
   and parses queue and host from the queue@host.lpd format */


  char *p, *q;
  long len;
  struct dirent *de;
  DIR *ds;
  
  ds = opendir("c:\\");
  if (ds==NULL) return -1;
  do{
    de=readdir(ds);
    if (de==NULL) {closedir(ds);return 0;}
    if (strstr(de->d_name,".lpd")!=NULL) {
      //valid printfile found         
      p=strstr(de->d_name,".");
      if (p==NULL) continue;
      q=strstr(de->d_name,"@");
      if (q==NULL) continue;
      
      len=(long)q-(long)de->d_name;
      if (len<1continue;
      strcpy(queue,"");
      strncat(queue,de->d_name,len);

      len=(long)p-(long)q-1;
      if (len<1continue;
      strcpy(host,"");
      strncat(host,(char *)((long)q+1),len);
      break;
    }
  }
  while (1);
  strcpy(pfname,"C:\\");
  strcat(pfname,de->d_name);
  return 1;
}


int connect_to_lpd(char* host) {

  int rc, sd;
  struct sockaddr_in localAddr, remoteAddr;
  struct hostent *h;

  /* resolve host ip */
  h = gethostbyname(host);
  if(h==NULL) {
    printf("unknown host '%s'\n",host);
    return-1;
  }

  remoteAddr.sin_family = h->h_addrtype;
  memcpy((char *) &remoteAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
  remoteAddr.sin_port = htons(LPD_PORT);

  /* open socket */
  sd = socket(AF_INET, SOCK_STREAM, 0);
  if(sd<0) {
    perror("cannot open socket ");
    return -1;
  }
  
  /* bind port 900 - needs to be priveledged for some lpds*/
  localAddr.sin_family = AF_INET;
  localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  localAddr.sin_port = htons(900);
  rc = bind(sd, (struct sockaddr *) &localAddr, sizeof(localAddr));
  if(rc<0) {
    printf("cannot bind port TCP %u\n",LPD_PORT);
    perror("error ");
    close (sd);
    return -1;
  }
  
  /* connect to server */
  rc = connect(sd, (struct sockaddr *) &remoteAddr, sizeof(remoteAddr));
  if(rc<0) {
    perror("cannot connect ");
    close (sd);
    return -1;
  } 
  /* Success! return socket descriptor */
  return sd;
}