/* ********************************************************************* */
/* *                                                                   * */
/* * Copyright (c) 2010 - Dipl.-Ing. Dirk Krause                       * */
/* *                                                                   * */
/* * All rights reserved.                                              * */
/* *                                                                   * */
/* * Redistribution and use in source and binary forms,                * */
/* * with or without modification, are permitted provided              * */
/* * that the following conditions are met:                            * */
/* *                                                                   * */
/* * * Redistributions of source code must retain the above            * */
/* *   copyright notice, this list of conditions and the               * */
/* *   following disclaimer.                                           * */
/* * * Redistributions in binary form must reproduce the above         * */
/* *   opyright notice, this list of conditions and the following      * */
/* *   disclaimer in the documentation and/or other materials          * */
/* *   provided with the distribution.                                 * */
/* * * Neither the name of Dirk Krause nor the names of                * */
/* *   contributors may be used to endorse or promote                  * */
/* *   products derived from this software without specific            * */
/* *   prior written permission.                                       * */
/* *                                                                   * */
/* * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND            * */
/* * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,       * */
/* * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF          * */
/* * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE          * */
/* * DISCLAIMED.                                                       * */
/* * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE          * */
/* * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,             * */
/* * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT           * */
/* * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          * */
/* * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)          * */
/* * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN         * */
/* * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE         * */
/* * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           * */
/* * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH              * */
/* * DAMAGE.                                                           * */
/* *                                                                   * */
/* ********************************************************************* */

/**	@file	kwt2.ctr	Support functions for kwintool, module 2.
*/


#include "kwintool.h"




#line 49 "kwt2.ctr"



/**	Support structure for removing local print jobs.
*/
typedef struct {
  WCHAR const *cn;	/**< Computer name */
  WCHAR const *un;	/**< User name */
  WCHAR const *pp;	/**< Pointer to printer */
} CMD_LOCAL;


/**	Abbreviation for struct sockaddr_in.
*/
typedef struct sockaddr_in SOIN;

/**	Abbreviation for size of struct sockaddr_in.
*/
#define SZ_SOIN (sizeof(SOIN))



/**	Process one print queue.
	@param	c	Data for processing.
*/
static
void
run_for_one_queue(CMD_LOCAL *c)
{
  int error = 0, cc = 0, i = 0, sbytes = 0, last_was_nl = 0, res = 0;
  char rq[KWINTOOL_BUFFER_SIZE];
  char hn[KWINTOOL_COMPUTER_NAME_SIZE], *p3;
  WCHAR mypp[KWINTOOL_BUFFER_SIZE], *p1;
  WCHAR const *p2;
  unsigned long a, *lp;
  struct hostent *he;
  SOCKET sock;
  struct sockaddr_in laddr, raddr;
  fd_set rfds, wfds, afds;
  struct timeval to;
  size_t lgtsnd;
  
  if(wcslen(c->pp) < SIZEOF(mypp,WCHAR)) {	
    wcscpy(mypp, c->pp);
    p1 = wcschr(mypp, L'@');
    if(p1) {					
      *(p1++) = L'\0'; 
      if(wcslen(p1) < sizeof(hn)) {		
        p2 = p1; p3 = hn;
	while(*p2) {
	  *p3 = (char)(*p2);
	  if((WCHAR)(*p3) != *p2) {
	    error = 1;
	  }
	  p2++; p3++;
	} *p3 = '\0';
	if(error == 0) {			
	  if((wcslen(mypp) + 2* wcslen(c->un) + 5) < sizeof(rq)) {
	    p3 = rq;				
	    *(p3++) = 0x05;
	    p2 = mypp;
	    while(*p2) {
	      *p3 = (char)(*p2);
	      if((WCHAR)(*p3) != *p2) {
	        error = 1;
	      }
	      p2++; p3++;
	    }
	    *(p3++) = ' ';
	    p2 = c->un;
	    while(*p2) {
	      *p3 = (char)(*p2);
	      if((WCHAR)(*p3) != *p2) {
	        error = 1;
	      }
	      p2++; p3++;
	    }
	    *(p3++) = ' ';
	    p2 = c->un;
	    while(*p2) {
	      *p3 = (char)(*p2);
	      if((WCHAR)(*p3) != *p2) {
	        error = 1;
	      }
	      p2++; p3++;
	    }
	    *(p3++) = '\n'; *p3 = '\0';
	    if(error == 0) {			
	      a = inet_addr(hn);
	      if(a == INADDR_NONE) {		
	        he = gethostbyname(hn);
		if(he) {			
		  if(he->h_addr_list) {		
		    if(he->h_length) {		
		      lp = (unsigned long *)(*(he->h_addr_list));
		      if(lp) {			
		        a = *lp;
		      } else {
		        error = 1;
		      }
		    } else {
		      error = 1;
		    }
		  } else {
		    error = 1;
		  }
		} else {
		  error = 1;
		}
	      } else {				
	      }
	      if(error == 0) {			
	        if(a != INADDR_NONE) {		
		  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		  if(sock != INVALID_SOCKET) {	
		    laddr.sin_family = AF_INET;
		    laddr.sin_addr.s_addr = htonl(INADDR_ANY);
		    laddr.sin_port = 0;
		    raddr.sin_family = AF_INET;
		    raddr.sin_port = htons(515);
		    raddr.sin_addr.s_addr = a;
		    if(bind(sock, (struct sockaddr *)(&laddr), SZ_SOIN) == 0) {
		      
		      if(connect(sock, (struct sockaddr *)(&raddr), SZ_SOIN) == 0) {
		        lgtsnd = strlen(rq);	
			sbytes = send(sock, rq, lgtsnd, 0);
			if(sbytes > 0) {	
			  if(shutdown(sock, SD_SEND) == 0) {
			    wprintf(L"%ls\n", c->pp); last_was_nl = 1;
			    cc = 1;		
			    while(cc) {
			      FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&afds);
			      FD_SET(sock, &rfds); FD_SET(sock, &afds);
			      to.tv_sec = 5;
			      to.tv_usec = 0;
			      res = select((sock + 1), &rfds, &wfds, &afds, &to);
			      if((res > 0) && (res != SOCKET_ERROR)) {	
			        if(FD_ISSET(sock, &rfds)) {
				  sbytes = recv(sock, rq, sizeof(rq), 0);
				  if(sbytes > 0) {			
				    fwrite((void *)rq, 1, sbytes, stdout);
				    if(rq[sbytes - 1] != '\n') last_was_nl = 0;
				    else last_was_nl = 1;
				  } else {	
				    cc = 0;
				  }
				} else {	
				  cc = 0; kwt_set_exval(1);
				}
			      } else {		
			        cc = 0; kwt_set_exval(1);
			      }
			    }
			    if(error == 0) {
			      if(last_was_nl == 0) {
			        fputc('\n', stdout);
			      }
			    }
			  } else {	
			    kwt_set_exval(1);
			  }
			} else {	
			  kwt_set_exval(1);
			}
		      } else {	
		        kwt_set_exval(1);
		      }
		    } else {	
		      kwt_set_exval(1);
		    }
		    closesocket(sock);
		  } else {	
		    kwt_set_exval(1);
		  }
		} else {	
		  kwt_set_exval(1);
		}
	      } else {		
	        kwt_set_exval(1);
	      }
	    } else {		
	      kwt_set_exval(1);
	    }
	  } else {		
	    kwt_set_exval(1);
	  }
	} else {		
	  kwt_set_exval(1);
	}
      } else {			
        kwt_set_exval(1);
      }
    } else {			
      kwt_set_exval(1);
    }
  } else {			
    kwt_set_exval(1);
  } 
}


void
kwt_run_lprm_lpd(int argc, WCHAR const * const *argv)
{
  CMD_LOCAL c;
  WCHAR cn[KWINTOOL_COMPUTER_NAME_SIZE];
  WCHAR un[KWINTOOL_USER_NAME_SIZE];
  WORD wVersionRequested;
  WSADATA wsaData;
  int err, i;
  DWORD sz;
  DWORD szun;
  c.cn = NULL; c.un = NULL; c.pp = NULL;
  if(argc > 2) {
    szun = SIZEOF(un,WCHAR);
    if(GetUserName(un, &szun)) {
      if(szun > 0) {
        c.un = (WCHAR const *)un;
      } else {
        c.un = _wgetenv(L"USERNAME");
      }
    } else {
      c.un = _wgetenv(L"USERNAME");
    }
    if(c.un) {
      sz = SIZEOF(cn,WCHAR);
      if(GetComputerNameEx(ComputerNameNetBIOS, cn, &sz)) {
        wVersionRequested = MAKEWORD(2, 0);
        err = WSAStartup(wVersionRequested, &wsaData);
        if(err == 0) {
          for(i = 2; i < argc; i++) {
            c.pp = argv[i];
	    run_for_one_queue(&c);
          }
          WSACleanup();
        } else {
          fwprintf(stderr, L"ERROR: Failed to initialize TCP/IP subsystem!\n");
          fflush(stderr);
          kwt_set_exval(1);
        }
      }
    }
  } else {
    kwt_set_exval(1);
  }
}



/**	Remove the print jobs from a local print queue.
	@param	c	Data to process.
*/
static
void
handle_local_printer(CMD_LOCAL *c)
{
  char peb[KWINTOOL_PRINT_QUEUE_JOB_LIST_SIZE];
  HANDLE hPrinter;	/* handle to printer */
  DWORD	szpeb;		/* size of peb buffer */
  DWORD cbNeeded;	/* number of bytes needed */
  DWORD cbReturned;	/* number of jobs enumerated */
  DWORD i;		/* index to traverse array of jobs */
  OLEL	*jptr;		/* start of linked list */
  OLEL	*optr;		/* temporary, used to create items */
  PJOB_INFO_1	jobptr;
  WCHAR prn[KWINTOOL_BUFFER_SIZE];

  if(wcslen(c->pp) < SIZEOF(prn,WCHAR)) {
    wcscpy(prn, c->pp);
    jptr = NULL;
    if(OpenPrinter(prn, &hPrinter, NULL)) {
      wprintf(L"%ls\n", c->pp);
      szpeb = sizeof(peb);
      cbNeeded = 0; cbReturned = 0;
      if(EnumJobs(
        hPrinter,
        0,
        KWINTOOL_NUMBER_OF_JOBS,
        1,
        peb,
        szpeb,
        &cbNeeded,
        &cbReturned
      ))
      {
        if(cbReturned > 0) {
          jobptr = (JOB_INFO_1 *)peb;
	  for(i = 0; i < cbReturned; i++) {
	    if(jobptr->pUserName) {
	      if(wcscmp(jobptr->pUserName, c->un) == 0) {
	        /* user ok */
	        if(jobptr->pMachineName) {
	          if(c->cn) {
		    if(wcscmp(jobptr->pMachineName, c->cn) == 0) {
		      optr = kwt_olel_new(jobptr->pDocument);
		      if(optr) {
		        optr->next = jptr;
		        jptr = optr;
		        optr->jobid = jobptr->JobId;
		      } else {
	                fwprintf(stderr, L"ERROR: Not enough memory!\n");
	                fflush(stderr);
		        kwt_set_exval(1);
		      }
		    }
		  }
	        }
	      }
	    }
	    jobptr++;
	  }
        }
      }
      else
      {
        fwprintf(stderr, L"ERROR: Failed to open local printer \"%ls\"!\n", c->pp);
        fflush(stderr);
        kwt_set_exval(1);
      }
      while(jptr) {
        optr = jptr->next;
        wprintf(
      	  L"\t%08lx\t%ls\n",
	  jptr->jobid,
	  ((jptr->name) ? jptr->name : L"untitled print job" )
        );
        if(!SetJob(hPrinter, jptr->jobid, 0, NULL, JOB_CONTROL_DELETE)) {
	  fwprintf(
	    stderr,
	    L"ERROR: Failed to cancel print job %lu \"%ls\", reason=%ld!\n",
	    ((jptr->name) ? jptr->name : L"untitled print job"),
	    GetLastError()
	  ); fflush(stderr); kwt_set_exval(1);
        }
        if(jptr->name) {
          kwt_free(jptr->name);
        }
        kwt_free(jptr);
        jptr = optr;
      }
      ClosePrinter(hPrinter);
    } else {
      kwt_set_exval(1);
    }
  } else {
    kwt_set_exval(1);
  }
}



/**	Remove print jobs from a local queue.
	@param	c	Data to process.
*/
static
void
remove_local_jobs(CMD_LOCAL *c)
{
  char peb[KWINTOOL_PRINT_QUEUE_JOB_LIST_SIZE];
  OLEL *pr;		/* start of linked list */
  DWORD szpeb;		/* size of peb buffer */
  DWORD cbNeeded;	/* number of bytes needed */
  DWORD cbReturned;	/* number of printers returned */
  DWORD i;		/* running index */
  PRINTER_INFO_4	*pi4;
  OLEL *optr;
  
  pr = NULL;
  szpeb = sizeof(peb);
  cbNeeded = 0;
  cbReturned = 0;
  if(EnumPrinters(
      PRINTER_ENUM_LOCAL,
      NULL,
      4,
      (LPBYTE)peb,
      szpeb,
      &cbNeeded,
      &cbReturned
    ))
  {
    if(cbReturned > 0) {
      pi4 = (PRINTER_INFO_4 *)peb;
      for(i = 0; i < cbReturned; i++) {
        if(pi4[i].pPrinterName) {
	  optr = kwt_olel_new(pi4[i].pPrinterName);
	  if(optr) {
	    optr->next = pr;
	    pr = optr;
	  } else {
	    fwprintf(stderr, L"ERROR: Not enough memory!\n");
	    fflush(stderr);
	    kwt_set_exval(1);
	  }
	}
      }
    }
  }
  else
  {
    fwprintf(stderr, L"ERROR: Failed to enumerate local printers!\n");
    fflush(stderr);
    kwt_set_exval(1);
  }
  if(pr) {
    optr = pr;
    while(optr) {
      pr = optr->next;
      c->pp = optr->name;
      handle_local_printer(c);
      kwt_olel_delete(optr);
      optr = pr;
    }
  } 
}



void
kwt_run_lprm_local(int argc, WCHAR const * const *argv)
{
  CMD_LOCAL c;
  WCHAR cn[KWINTOOL_COMPUTER_NAME_SIZE];
  WCHAR un[KWINTOOL_USER_NAME_SIZE];
  DWORD sz;
  DWORD szun;

  c.cn = NULL; c.pp = NULL;
  szun = SIZEOF(un,WCHAR);
  if(GetUserName(un, &szun)) {
    if(szun > 0) {
      c.un = un;
    } else {
      c.un = _wgetenv(L"USERNAME");
    }
  } else {
    c.un =  _wgetenv(L"USERNAME");
  }
  if(c.un) {
    sz = SIZEOF(cn,WCHAR);
    if(GetComputerNameEx(ComputerNameNetBIOS, cn, &sz)) {
      if(sz > 0) {
        cn[(sz < SIZEOF(cn,WCHAR)) ? sz : (SIZEOF(cn,WCHAR)-1)] = L'\0';
	c.cn = cn;
	remove_local_jobs(&c);
      } else {
        kwt_set_exval(1);
      }
    } else {
      kwt_set_exval(1);
    }
  } else {
    kwt_set_exval(1);
  }
}

