/* nwqconn.c 15-Sep-99 */
/* (C)opyright (C) 1997  Martin Stover, Marburg, Germany
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "net.h"
#include <dirent.h>

#include "nwvolume.h"
#include "nwfile.h"
#include "connect.h"
#include "nwqconn.h"

/* 
 * the connection based queue stuff is in this module.         
 * The running process is nwconn.
 * the bindery/global queue handling is in nwqueue.c.
*/

typedef struct S_INT_QUEUE_JOB {
  uint32           queue_id;
  int              job_id;
  int              fhandle;
  int              task;
  struct S_INT_QUEUE_JOB *next;
} INT_QUEUE_JOB;

INT_QUEUE_JOB *queue_jobs=NULL;

static INT_QUEUE_JOB *new_queue_job(uint32 queue_id, int job_id, int task)
{
  INT_QUEUE_JOB *p=(INT_QUEUE_JOB*)xcmalloc(sizeof(INT_QUEUE_JOB));
  if (!queue_jobs) {
    queue_jobs=p;
  } else {
    INT_QUEUE_JOB *qj=queue_jobs;
    while (qj->next != NULL) qj=qj->next;
    qj->next=p;
  }
  p->next=NULL;
  p->queue_id=queue_id;
  p->job_id=job_id;
  p->task=task;
  return(p);
}

static void free_queue_job(uint32 queue_id, int job_id)
{
  INT_QUEUE_JOB *qj=queue_jobs;
  if (!qj) return;
  if (qj->queue_id==queue_id && qj->job_id == job_id){
    queue_jobs=qj->next;
    xfree(qj);
    return;
  }
  while (qj->next) {
    if (qj->next->queue_id == queue_id && qj->next->job_id == job_id) {
      INT_QUEUE_JOB *tmp=qj->next;
      qj->next=tmp->next;
      xfree(tmp);
      return;
    } else qj=qj->next;
  }
  /* not found */
}

static INT_QUEUE_JOB *find_queue_job(uint32 queue_id, int job_id)
{
  INT_QUEUE_JOB *qj=queue_jobs;
  while (qj) {
    if (qj->queue_id==queue_id && qj->job_id == job_id)
      return(qj);
    qj=qj->next;
  }
  return(NULL);
}

static int open_creat_queue_file(int mode, uint8
                                *file_name, int file_name_len,
                                uint8 *dirname, int dirname_len)
 /* modes:
  * 0 : creat
  * 1 : open ro  (as root)
  * 2 : open rw  (as root)
  */
{
  int result;
  int eff_rights;
  result=nw_alloc_dir_handle(0, dirname, dirname_len, 99, 2, 1, &eff_rights);
  if (result > -1) {
    char unixname[300];
    int  dirhandle=result;
    result=conn_get_kpl_unxname(unixname, sizeof(unixname), 
                                 dirhandle, file_name, file_name_len);
    if (result > -1) {
      struct stat stbuff;
      if (mode == 0) {  /* creat */
        result=file_creat_open(result, (uint8*)unixname,
                            &stbuff, 0x6, 0x6, 1|4|8, 0);
        if (result > -1)
           chmod(unixname, 0600);
      } else if (mode == 1) { /* open ro */
         result=file_creat_open(result, (uint8*)unixname,
                            &stbuff, 0x6, 0x9, 8, 0);
                            /* ...............^^^...not &4 !... */
      } else if (mode == 2) { /* open rw  */
         result=file_creat_open(result, (uint8*)unixname,
                            &stbuff, 0x6, 0x6, 4|8, 0);
      } else result=-1;
    }
    nw_free_dir_handle(dirhandle, 1);
  }
  if (result < 0) {
    uint8 dn[300];
    uint8 fn[300];
    strmaxcpy(dn, dirname, dirname_len);
    strmaxcpy(fn, file_name, file_name_len);
    XDPRINTF((1, 0, "open_creat_queue_file, mode=%d,result=-0x%x, dn='%s', fn='%s'",
                        mode, -result, dn, fn));
    result=-0xff;
  }
  return(result);
}

int creat_queue_job(int task,
                    uint32 q_id,
                    uint8 *queue_job,
                    uint8 *responsedata,
                    uint8 old_call)
{
  uint8 *dirname  = (old_call) ? queue_job+sizeof(QUEUE_JOB_OLD)
  		    	       : queue_job+sizeof(QUEUE_JOB);
  int   job_id=-1;
  INT_QUEUE_JOB *jo;
  int result;
  memcpy(responsedata, queue_job, (old_call) ? sizeof(QUEUE_JOB_OLD)
   		       		  	     : sizeof(QUEUE_JOB));
  if (old_call) {
    QUEUE_JOB_OLD *job=(QUEUE_JOB_OLD*)responsedata;  /* before 3.11 */
    job_id = GET_BE16(job->job_id);
    jo     = new_queue_job(q_id, job_id, task);
    result = open_creat_queue_file(0, job->job_file_name+1, *(job->job_file_name),
                               dirname+1, *dirname);
    if (result > -1) {
      jo->fhandle  = (uint32) result;
      U16_TO_BE16(0,           job->job_file_handle);
      U32_TO_32(jo->fhandle,   job->job_file_handle+2);
      result = sizeof(QUEUE_JOB_OLD) - 202;
    }
  } else {
    QUEUE_JOB *job=(QUEUE_JOB*)responsedata;
    job_id=GET_BE16(job->job_id);
    jo     = new_queue_job(q_id, job_id, task);
    result = open_creat_queue_file(0, job->job_file_name+1, *(job->job_file_name),
                               dirname+1, *dirname);
    if (result > -1) {
      jo->fhandle  = (uint32) result;
      U32_TO_32(jo->fhandle,   job->job_file_handle);
      result = sizeof(QUEUE_JOB) - 202;
    }
  }
  if (result < 0)
    free_queue_job(q_id, job_id);

  XDPRINTF((6, 0, "creat_q_job, id=%d, result=%d", jo ? jo->job_id : -1,
    result));
  return(result);
}

int close_queue_job(uint32 q_id, int job_id)
{
  int result = -0xff;
  INT_QUEUE_JOB *jo=find_queue_job(q_id, job_id);
  if (jo) {
    nw_close_file(jo->fhandle, 0, jo->task);
    result=0;
  }
  XDPRINTF(( (result<0) ? 1 :5 ,0,"close_queue_job Q=0x%x, job=%d, result=%d",
        q_id, job_id, result));
  return(result);
}

int close_queue_job2(uint32 q_id, int job_id,
                     uint8 *client_area,
                     uint8 *prc, int prc_len)
{
  int result = -0xff;
  INT_QUEUE_JOB *jo=find_queue_job(q_id, job_id);
  if (jo) {
    if (prc_len) {
      char unixname[300];
      QUEUE_PRINT_AREA qpa;
      memcpy(&qpa, client_area, sizeof(QUEUE_PRINT_AREA));
      strmaxcpy((uint8*)unixname,
          (uint8*)file_get_unix_name(jo->fhandle), sizeof(unixname)-1);
      XDPRINTF((5,0,"nw_close_file_queue fhandle=%d", jo->fhandle));
      if (*unixname) {
        char buff[1024];
        char printcommand[1400];
        FILE *f=NULL;
        if (prc_len && *(prc+prc_len-1)=='!'){
          strmaxcpy((uint8*)buff, prc, prc_len-1);
          snprintf(printcommand, sizeof(printcommand), "%s %s %s", buff,
             qpa.banner_user_name, qpa.banner_file_name);
        } else
          strmaxcpy((uint8*)printcommand, prc, prc_len);
        nw_close_file(jo->fhandle, 1, jo->task);
        jo->fhandle = 0L;
        if (NULL == (f = fopen(unixname, "r"))) {
          /* OK now we try the open as root */
          if (seteuid(0)) {}
          f = fopen(unixname, "r");
          reset_guid();
        }
        if (NULL != f) {
          int  is_ok = 0;
          FILE_PIPE *fp = ext_popen(printcommand, geteuid(), getegid(), 1);
          if (fp) {
            int  k;
            is_ok++;
            while (is_ok && (k = fread(buff, 1, sizeof(buff), f)) > 0) {
              if (k != write(fp->fds[0], buff, k)) {
                XDPRINTF((1,0x10,"Cannot write to pipe `%s`", printcommand));
                if ((k=read(fp->fds[2], buff, sizeof(buff)-1)) > 0 ) {
                  buff[k]='\0';
                  XDPRINTF((1,0x0,"err='%s'", buff));
                }
                is_ok=0;
              }
            }
            if (0 != (k=ext_pclose(fp))) {
              XDPRINTF((1,0,"Errorresult = %d by closing print pipe", k));
            }
          }
          fclose(f);
          if (is_ok) {
            if (seteuid(0)) {}
            unlink(unixname);
            reset_guid();
            result=0;
          }
        } else XDPRINTF((1,0,"Cannot open queue-file `%s`", unixname));
      }
    } else {
      result=0;
      nw_close_file(jo->fhandle, 1, jo->task);
    }
    free_queue_job(q_id, job_id);
  }
  XDPRINTF(((result<0)?1:5,0,"close_queue_job2, Q=0x%x, job=%d", q_id, job_id));
  return(result);
}

int service_queue_job(int task,
                    uint32 q_id,
                    uint8 *queue_job,
                    uint8 *responsedata,
                    uint8 old_call)
{
  uint8 *dirname  = (old_call) ? queue_job+sizeof(QUEUE_JOB_OLD)
  		    	       : queue_job+sizeof(QUEUE_JOB);
  int   job_id;
  INT_QUEUE_JOB *jo;
  int result;
  memcpy(responsedata, queue_job, (old_call) ? sizeof(QUEUE_JOB_OLD)
   		       		  	     : sizeof(QUEUE_JOB));
  if (old_call) {
    QUEUE_JOB_OLD *job=(QUEUE_JOB_OLD*)responsedata;  /* before 3.11 */
    job_id = GET_BE16(job->job_id);
    jo     = new_queue_job(q_id, job_id,task);
    result = open_creat_queue_file(1,
                               job->job_file_name+1, *(job->job_file_name),
                               dirname+1, *dirname);
    if (result > -1) {
      jo->fhandle  = (uint32) result;
      U16_TO_BE16(0,           job->job_file_handle);
      U32_TO_32(jo->fhandle,   job->job_file_handle+2);
      result = sizeof(QUEUE_JOB_OLD) - 202;
    }
  } else {
    QUEUE_JOB *job=(QUEUE_JOB*)responsedata;
    job_id = GET_BE16(job->job_id);
    jo     = new_queue_job(q_id, job_id,task);
    result = open_creat_queue_file(1,
                               job->job_file_name+1, *(job->job_file_name),
                               dirname+1, *dirname);
    if (result > -1) {
      jo->fhandle  = (uint32) result;
      U32_TO_32(jo->fhandle,   job->job_file_handle);
      result = sizeof(QUEUE_JOB) - 202;
    }
  }
  if (result < 0) {
    free_queue_job(q_id, job_id);
  }
  return(result);
}

int finish_abort_queue_job(uint32 q_id, int job_id)
{
  int result = -0xff;
  INT_QUEUE_JOB *jo=find_queue_job(q_id, job_id);
  if (jo) {
    nw_close_file(jo->fhandle, 0, jo->task);
    free_queue_job(q_id, job_id);
    result=0;
  }
  XDPRINTF((5,0,"finish_abort_queue_job Q=0x%x, job=%d, result=%d",
        q_id, job_id, result));
  return(result);
}

uint32 get_queue_job_fhandle(uint32 q_id, int job_id)
{
  INT_QUEUE_JOB *jo=find_queue_job(q_id, job_id);
  if (jo)
    return(jo->fhandle);
  return(0);
}

void free_queue_jobs(void)
{
  INT_QUEUE_JOB *qj=queue_jobs;
  while(qj){
    INT_QUEUE_JOB *tmp=qj;
    qj=qj->next;
    xfree(tmp);
  }
  queue_jobs=NULL;
}

static int loc_free_connection_task_jobs(int task)
{
  INT_QUEUE_JOB *qj=queue_jobs;
  if (!qj) return(0);
  if (qj->task==task){
    queue_jobs=qj->next;
    nw_close_file(qj->fhandle, 1, task);
    xfree(qj);
    return(1);
  }
  while (qj->next) {
    if (qj->next->task == task) {
      INT_QUEUE_JOB *tmp=qj->next;
      qj->next=tmp->next;
      nw_close_file(tmp->fhandle, 1, task);
      xfree(tmp);
      return(1);
    } else qj=qj->next;
  }
  return(0); /* not found */
}

void free_connection_task_jobs(int task)
{
  while(loc_free_connection_task_jobs(task)) ;;
}

