Home | History | Annotate | Line # | Download | only in rpc.pcnfsd
pcnfsd_print.c revision 1.1
      1 /* RE_SID: @(%)/usr/dosnfs/shades_SCCS/unix/pcnfsd/v2/src/SCCS/s.pcnfsd_print.c 1.7 92/01/24 19:58:58 SMI */
      2 /*
      3 **=====================================================================
      4 ** Copyright (c) 1986,1987,1988,1989,1990,1991 by Sun Microsystems, Inc.
      5 **	@(#)pcnfsd_print.c	1.7	1/24/92
      6 **=====================================================================
      7 */
      8 #include "common.h"
      9 /*
     10 **=====================================================================
     11 **             I N C L U D E   F I L E   S E C T I O N                *
     12 **                                                                    *
     13 ** If your port requires different include files, add a suitable      *
     14 ** #define in the customization section, and make the inclusion or    *
     15 ** exclusion of the files conditional on this.                        *
     16 **=====================================================================
     17 */
     18 #include "pcnfsd.h"
     19 #include <malloc.h>
     20 #include <stdio.h>
     21 #include <pwd.h>
     22 #include <sys/file.h>
     23 #include <signal.h>
     24 #include <sys/stat.h>
     25 #include <sys/ioctl.h>
     26 #include <netdb.h>
     27 #include <errno.h>
     28 #include <string.h>
     29 
     30 #ifndef SYSV
     31 #include <sys/wait.h>
     32 #endif
     33 
     34 #ifdef ISC_2_0
     35 #include <sys/fcntl.h>
     36 #endif
     37 
     38 #ifdef SHADOW_SUPPORT
     39 #include <shadow.h>
     40 #endif
     41 
     42 /*
     43 **---------------------------------------------------------------------
     44 ** Other #define's
     45 **---------------------------------------------------------------------
     46 */
     47 #ifndef MAXPATHLEN
     48 #define MAXPATHLEN 1024
     49 #endif
     50 
     51 #ifndef SPOOLDIR
     52 #define SPOOLDIR        "/export/pcnfs"
     53 #endif
     54 
     55 #ifndef LPRDIR
     56 #define LPRDIR		"/usr/bin"
     57 #endif
     58 
     59 #ifndef LPCDIR
     60 #define LPCDIR		"/usr/sbin"
     61 #endif
     62 
     63 /*
     64 ** The following defintions give the maximum time allowed for
     65 ** an external command to run (in seconds)
     66 */
     67 #define MAXTIME_FOR_PRINT	10
     68 #define MAXTIME_FOR_QUEUE	10
     69 #define MAXTIME_FOR_CANCEL	10
     70 #define MAXTIME_FOR_STATUS	10
     71 
     72 #define QMAX 50
     73 
     74 /*
     75 ** The following is derived from ucb/lpd/displayq.c
     76 */
     77 #define SIZECOL 62
     78 #define FILECOL 24
     79 
     80 extern void     scramble();
     81 extern void     run_ps630();
     82 extern char    *crypt();
     83 extern FILE    *su_popen();
     84 extern int      su_pclose();
     85 int             build_pr_list();
     86 char 	       *map_printer_name();
     87 char	       *expand_alias();
     88 void           *grab();
     89 void            free_pr_list_item();
     90 void            free_pr_queue_item();
     91 pr_list		list_virtual_printers();
     92 
     93 /*
     94 **---------------------------------------------------------------------
     95 **                       Misc. variable definitions
     96 **---------------------------------------------------------------------
     97 */
     98 
     99 extern int      errno;
    100 extern int	interrupted;	/* in pcnfsd_misc.c */
    101 struct stat     statbuf;
    102 char            pathname[MAXPATHLEN];
    103 char            new_pathname[MAXPATHLEN];
    104 char            sp_name[MAXPATHLEN] = SPOOLDIR;
    105 char            tempstr[256];
    106 char            delims[] = " \t\r\n:()";
    107 
    108 pr_list         printers = NULL;
    109 pr_queue        queue = NULL;
    110 
    111 /*
    112 **=====================================================================
    113 **                      C O D E   S E C T I O N                       *
    114 **=====================================================================
    115 */
    116 
    117 /*
    118  * This is the latest word on the security check. The following
    119  * routine "suspicious()" returns non-zero if the character string
    120  * passed to it contains any shell metacharacters.
    121  * Callers will typically code
    122  *
    123  *	if(suspicious(some_parameter)) reject();
    124  */
    125 
    126 int suspicious (s)
    127 char *s;
    128 {
    129 	if(strpbrk(pathname, ";|&<>`'#!?*()[]^") != NULL)
    130 		return 1;
    131 	return 0;
    132 }
    133 
    134 
    135 int
    136 valid_pr(pr)
    137 char *pr;
    138 {
    139 char *p;
    140 pr_list curr;
    141 	if(printers == NULL)
    142 		build_pr_list();
    143 
    144 	if(printers == NULL)
    145 		return(1); /* can't tell - assume it's good */
    146 
    147 	p = map_printer_name(pr);
    148 	if (p == NULL)
    149 		return(1);	/* must be ok is maps to NULL! */
    150 	curr = printers;
    151 	while(curr) {
    152 		if(!strcmp(p, curr->pn))
    153 			return(1);
    154 		curr = curr->pr_next;
    155 	}
    156 
    157 	return(0);
    158 }
    159 
    160 pirstat pr_init(sys, pr, sp)
    161 char *sys;
    162 char *pr;
    163 char**sp;
    164 {
    165 int    dir_mode = 0777;
    166 int rc;
    167 
    168 	*sp = &pathname[0];
    169 	pathname[0] = '\0';
    170 
    171 	if(suspicious(sys) || suspicious(pr))
    172 		return(PI_RES_FAIL);
    173 
    174 	/* get pathname of current directory and return to client */
    175 
    176 	(void)sprintf(pathname,"%s/%s",sp_name, sys);
    177 	(void)mkdir(sp_name, dir_mode);	/* ignore the return code */
    178 	(void)chmod(sp_name, dir_mode);
    179 	rc = mkdir(pathname, dir_mode);	/* DON'T ignore this return code */
    180 	if((rc < 0 && errno != EEXIST) ||
    181 	   (chmod(pathname, dir_mode) != 0) ||
    182 	   (stat(pathname, &statbuf) != 0) ||
    183 	   !(statbuf.st_mode & S_IFDIR)) {
    184 	   (void)sprintf(tempstr,
    185 		         "rpc.pcnfsd: unable to set up spool directory %s\n",
    186 		 	  pathname);
    187             msg_out(tempstr);
    188 	    pathname[0] = '\0';	/* null to tell client bad vibes */
    189 	    return(PI_RES_FAIL);
    190 	    }
    191  	if (!valid_pr(pr))
    192            {
    193 	    pathname[0] = '\0';	/* null to tell client bad vibes */
    194 	    return(PI_RES_NO_SUCH_PRINTER);
    195 	    }
    196 	return(PI_RES_OK);
    197 
    198 }
    199 
    200 
    202 psrstat pr_start2(system, pr, user, fname, opts, id)
    203 char *system;
    204 char *pr;
    205 char *user;
    206 char *fname;
    207 char *opts;
    208 char **id;
    209 {
    210 char            snum[20];
    211 static char     req_id[256];
    212 char            cmdbuf[256];
    213 char            resbuf[256];
    214 FILE *fd;
    215 int i;
    216 char *xcmd;
    217 char *cp;
    218 int failed = 0;
    219 
    220 #ifdef HACK_FOR_ROTATED_TRANSCRIPT
    221 char            scratch[512];
    222 #endif
    223 
    224 
    225 	if(suspicious(system) ||
    226 		suspicious(pr) ||
    227 		suspicious(user) ||
    228 		suspicious(fname))
    229 		return(PS_RES_FAIL);
    230 
    231 	(void)sprintf(pathname,"%s/%s/%s",sp_name,
    232 	                         system,
    233 	                         fname);
    234 
    235 	*id = &req_id[0];
    236 	req_id[0] = '\0';
    237 
    238 	if (stat(pathname, &statbuf))
    239            {
    240 	   /*
    241            **-----------------------------------------------------------------
    242 	   ** We can't stat the file. Let's try appending '.spl' and
    243 	   ** see if it's already in progress.
    244            **-----------------------------------------------------------------
    245 	   */
    246 
    247 	   (void)strcat(pathname, ".spl");
    248 	   if (stat(pathname, &statbuf))
    249 	      {
    250 	      /*
    251               **----------------------------------------------------------------
    252 	      ** It really doesn't exist.
    253               **----------------------------------------------------------------
    254 	      */
    255 
    256 
    257 	      return(PS_RES_NO_FILE);
    258 	      }
    259 	      /*
    260               **-------------------------------------------------------------
    261 	      ** It is already on the way.
    262               **-------------------------------------------------------------
    263 	      */
    264 
    265 
    266 		return(PS_RES_ALREADY);
    267 	     }
    268 
    269 	if (statbuf.st_size == 0)
    270 	   {
    271 	   /*
    272            **-------------------------------------------------------------
    273 	   ** Null file - don't print it, just kill it.
    274            **-------------------------------------------------------------
    275 	   */
    276 	   (void)unlink(pathname);
    277 
    278 	    return(PS_RES_NULL);
    279 	    }
    280 	 /*
    281          **-------------------------------------------------------------
    282 	 ** The file is real, has some data, and is not already going out.
    283 	 ** We rename it by appending '.spl' and exec "lpr" to do the
    284 	 ** actual work.
    285          **-------------------------------------------------------------
    286 	 */
    287 	(void)strcpy(new_pathname, pathname);
    288 	(void)strcat(new_pathname, ".spl");
    289 
    290 	/*
    291         **-------------------------------------------------------------
    292 	** See if the new filename exists so as not to overwrite it.
    293         **-------------------------------------------------------------
    294 	*/
    295 
    296 
    297 	if (!stat(new_pathname, &statbuf))
    298 	   {
    299 	   (void)strcpy(new_pathname, pathname);  /* rebuild a new name */
    300 	   (void)sprintf(snum, "%d", rand());	  /* get some number */
    301 	   (void)strncat(new_pathname, snum, 3);
    302 	   (void)strcat(new_pathname, ".spl");	  /* new spool file */
    303 	    }
    304 	if (rename(pathname, new_pathname))
    305 	   {
    306 	   /*
    307            **---------------------------------------------------------------
    308 	   ** Should never happen.
    309            **---------------------------------------------------------------
    310            */
    311 	   (void)sprintf(tempstr, "rpc.pcnfsd: spool file rename (%s->%s) failed.\n",
    312 			pathname, new_pathname);
    313                 msg_out(tempstr);
    314 		return(PS_RES_FAIL);
    315 	    }
    316 
    317 		if (*opts == 'd')
    318 	           {
    319 		   /*
    320                    **------------------------------------------------------
    321 		   ** This is a Diablo print stream. Apply the ps630
    322 		   ** filter with the appropriate arguments.
    323                    **------------------------------------------------------
    324 		   */
    325 		   (void)run_ps630(new_pathname, opts);
    326 		   }
    327 		/*
    328 		** Try to match to an aliased printer
    329 		*/
    330 		xcmd = expand_alias(pr, new_pathname, user, system);
    331 	        /*
    332                 **----------------------------------------------------------
    333 	        **  Use the copy option so we can remove the orignal spooled
    334 	        **  nfs file from the spool directory.
    335                 **----------------------------------------------------------
    336 	        */
    337 		if(!xcmd) {
    338 			sprintf(cmdbuf, "%s/lpr -P%s %s",
    339 				LPRDIR, pr, new_pathname);
    340 			xcmd = cmdbuf;
    341 		}
    342 		if ((fd = su_popen(user, xcmd, MAXTIME_FOR_PRINT)) == NULL) {
    343 			msg_out("rpc.pcnfsd: su_popen failed");
    344 			return(PS_RES_FAIL);
    345 		}
    346 		req_id[0] = '\0';	/* asume failure */
    347 		while(fgets(resbuf, 255, fd) != NULL) {
    348 			i = strlen(resbuf);
    349 			if(i)
    350 				resbuf[i-1] = '\0'; /* trim NL */
    351 			if(!strncmp(resbuf, "request id is ", 14))
    352 				/* New - just the first word is needed */
    353 				strcpy(req_id, strtok(&resbuf[14], delims));
    354 			else if (strembedded("disabled", resbuf))
    355 				failed = 1;
    356 		}
    357 		if(su_pclose(fd) == 255)
    358 			msg_out("rpc.pcnfsd: su_pclose alert");
    359 		(void)unlink(new_pathname);
    360 		return((failed | interrupted)? PS_RES_FAIL : PS_RES_OK);
    361 }
    362 
    363 
    365 /*
    366  * determine which printers are valid...
    367  * on BSD use "lpc status"
    368  * on SVR4 use "lpstat -v"
    369  */
    370 int
    371 build_pr_list()
    372 {
    373 	pr_list last = NULL;
    374 	pr_list curr = NULL;
    375 	char buff[256];
    376 	FILE *p;
    377 	char *cp;
    378 	int saw_system;
    379 
    380 	sprintf(buff, "%s/lpc status", LPCDIR);
    381 	p = popen(buff, "r");
    382 	if(p == NULL) {
    383 		msg_out("rpc.pcnfsd: unable to popen lpc stat");
    384 		return(0);
    385 	}
    386 
    387 	while(fgets(buff, 255, p) != NULL) {
    388 		if (isspace(buff[0]))
    389 			continue;
    390 
    391 		if ((cp = strtok(buff, delims)) == NULL)
    392 			continue;
    393 
    394 		curr = (struct pr_list_item *)
    395 			grab(sizeof (struct pr_list_item));
    396 
    397 		/* XXX - Should distinguish remote printers. */
    398 		curr->pn = strdup(cp);
    399 		curr->device = strdup(cp);
    400 		curr->remhost = strdup("");
    401 		curr->cm = strdup("-");
    402 		curr->pr_next = NULL;
    403 
    404 		if(last == NULL)
    405 			printers = curr;
    406 		else
    407 			last->pr_next = curr;
    408 		last = curr;
    409 
    410 	}
    411 	(void) fclose(p);
    412 
    413 	/*
    414 	 ** Now add on the virtual printers, if any
    415 	 */
    416 	if(last == NULL)
    417 		printers = list_virtual_printers();
    418 	else
    419 		last->pr_next = list_virtual_printers();
    420 
    421 	return(1);
    422 }
    423 
    424 void *grab(n)
    425 int n;
    426 {
    427 void *p;
    428 
    429 	p = (void *)malloc(n);
    430 	if(p == NULL) {
    431 		msg_out("rpc.pcnfsd: malloc failure");
    432 		exit(1);
    433 	}
    434 	return(p);
    435 }
    436 
    437 void
    438 free_pr_list_item(curr)
    439 pr_list curr;
    440 {
    441 	if(curr->pn)
    442 		free(curr->pn);
    443 	if(curr->device)
    444 		free(curr->device);
    445 	if(curr->remhost)
    446 		free(curr->remhost);
    447 	if(curr->cm)
    448 		free(curr->cm);
    449 	if(curr->pr_next)
    450 		free_pr_list_item(curr->pr_next); /* recurse */
    451 	free(curr);
    452 }
    453 /*
    454 ** Print queue handling.
    455 **
    456 ** Note that the first thing we do is to discard any
    457 ** existing queue.
    458 */
    459 
    460 pirstat
    461 build_pr_queue(pn, user, just_mine, p_qlen, p_qshown)
    462 printername     pn;
    463 username        user;
    464 int            just_mine;
    465 int            *p_qlen;
    466 int            *p_qshown;
    467 {
    468 pr_queue last = NULL;
    469 pr_queue curr = NULL;
    470 char buff[256];
    471 FILE *p;
    472 char *cp;
    473 int i;
    474 char *rank;
    475 char *owner;
    476 char *job;
    477 char *files;
    478 char *totsize;
    479 
    480 	if(queue) {
    481 		free_pr_queue_item(queue);
    482 		queue = NULL;
    483 	}
    484 	*p_qlen = 0;
    485 	*p_qshown = 0;
    486 	pn = map_printer_name(pn);
    487 	if(pn == NULL || suspicious(pn))
    488 		return(PI_RES_NO_SUCH_PRINTER);
    489 
    490 	sprintf(buff, "%s/lpq -P%s", LPRDIR, pn);
    491 
    492 	p = su_popen(user, buff, MAXTIME_FOR_QUEUE);
    493 	if(p == NULL) {
    494 		msg_out("rpc.pcnfsd: unable to popen() lpq");
    495 		return(PI_RES_FAIL);
    496 	}
    497 
    498 	while(fgets(buff, 255, p) != NULL) {
    499 		i = strlen(buff) - 1;
    500 		buff[i] = '\0';		/* zap trailing NL */
    501 		if(i < SIZECOL)
    502 			continue;
    503 		if(!mystrncasecmp(buff, "rank", 4))
    504 			continue;
    505 
    506 		totsize = &buff[SIZECOL-1];
    507 		files = &buff[FILECOL-1];
    508 		cp = totsize;
    509 		cp--;
    510 		while(cp > files && isspace(*cp))
    511 			*cp-- = '\0';
    512 
    513 		buff[FILECOL-2] = '\0';
    514 
    515 		cp = strtok(buff, delims);
    516 		if(!cp)
    517 			continue;
    518 		rank = cp;
    519 
    520 		cp = strtok(NULL, delims);
    521 		if(!cp)
    522 			continue;
    523 		owner = cp;
    524 
    525 		cp = strtok(NULL, delims);
    526 		if(!cp)
    527 			continue;
    528 		job = cp;
    529 
    530 		*p_qlen += 1;
    531 
    532 		if(*p_qshown > QMAX)
    533 			continue;
    534 
    535 		if(just_mine && mystrcasecmp(owner, user))
    536 			continue;
    537 
    538 		*p_qshown += 1;
    539 
    540 		curr = (struct pr_queue_item *)
    541 			grab(sizeof (struct pr_queue_item));
    542 
    543 		curr->position = atoi(rank); /* active -> 0 */
    544 		curr->id = strdup(job);
    545 		curr->size = strdup(totsize);
    546 		curr->status = strdup(rank);
    547 		curr->system = strdup("");
    548 		curr->user = strdup(owner);
    549 		curr->file = strdup(files);
    550 		curr->cm = strdup("-");
    551 		curr->pr_next = NULL;
    552 
    553 		if(last == NULL)
    554 			queue = curr;
    555 		else
    556 			last->pr_next = curr;
    557 		last = curr;
    558 
    559 	}
    560 	(void) su_pclose(p);
    561 	return(PI_RES_OK);
    562 }
    563 
    564 void
    565 free_pr_queue_item(curr)
    566 pr_queue curr;
    567 {
    568 	if(curr->id)
    569 		free(curr->id);
    570 	if(curr->size)
    571 		free(curr->size);
    572 	if(curr->status)
    573 		free(curr->status);
    574 	if(curr->system)
    575 		free(curr->system);
    576 	if(curr->user)
    577 		free(curr->user);
    578 	if(curr->file)
    579 		free(curr->file);
    580 	if(curr->cm)
    581 		free(curr->cm);
    582 	if(curr->pr_next)
    583 		free_pr_queue_item(curr->pr_next); /* recurse */
    584 	free(curr);
    585 }
    586 
    587 
    588 
    589 pirstat
    590 get_pr_status(pn, avail, printing, qlen, needs_operator, status)
    591 printername   pn;
    592 bool_t       *avail;
    593 bool_t       *printing;
    594 int          *qlen;
    595 bool_t       *needs_operator;
    596 char         *status;
    597 {
    598 char cmd[128];
    599 char buff[256];
    600 char buff2[256];
    601 char pname[64];
    602 FILE *p;
    603 char *cp;
    604 char *cp1;
    605 char *cp2;
    606 int n;
    607 pirstat stat = PI_RES_NO_SUCH_PRINTER;
    608 
    609 	/* assume the worst */
    610 	*avail = FALSE;
    611 	*printing = FALSE;
    612 	*needs_operator = FALSE;
    613 	*qlen = 0;
    614 	*status = '\0';
    615 
    616 	pn = map_printer_name(pn);
    617 	if(pn == NULL || suspicious(pn))
    618 		return(PI_RES_NO_SUCH_PRINTER);
    619 
    620 	sprintf(pname, "%s:", pn);
    621 	n = strlen(pname);
    622 
    623 #if 0
    624 /*
    625  * We used to use lpstat -a, but it breaks because of
    626  * printer alias inconsistency
    627  */
    628 	p = popen("/usr/bin/lpstat -a", "r");
    629 #else
    630 /*
    631  * So now we use:  lpc status
    632  */
    633 	sprintf(cmd, "%s/lpc status %s", LPCDIR, pn);
    634 	p = popen(cmd, "r");
    635 #endif
    636 	if(p == NULL) {
    637 		msg_out("rpc.pcnfsd: unable to popen() lp status");
    638 		return(PI_RES_FAIL);
    639 	}
    640 
    641 	while(fgets(buff, 255, p) != NULL) {
    642 		if(strncmp(buff, pname, n))
    643 			continue;
    644 /*
    645 ** We have a match. The only failure now is PI_RES_FAIL if
    646 ** lpstat output cannot be decoded
    647 */
    648 		stat = PI_RES_FAIL;
    649 /*
    650 ** The next four lines are usually if the form
    651 **
    652 **     queuing is [enabled|disabled]
    653 **     printing is [enabled|disabled]
    654 **     [no entries | N entr[y|ies] in spool area]
    655 **     <status message, may include the word "attention">
    656 */
    657 		while(fgets(buff, 255, p) != NULL && isspace(buff[0])) {
    658 			cp = buff;
    659 			while(isspace(*cp))
    660 				cp++;
    661 			if(*cp == '\0')
    662 				break;
    663 			cp1 = cp;
    664 			cp2 = buff2;
    665 			while(*cp1 && *cp1 != '\n') {
    666 				*cp2++ = tolower(*cp1);
    667 				cp1++;
    668 			}
    669 			*cp1 = '\0';
    670 			*cp2 = '\0';
    671 /*
    672 ** Now buff2 has a lower-cased copy and cp points at the original;
    673 ** both are null terminated without any newline
    674 */
    675 			if(!strncmp(buff2, "queuing", 7)) {
    676 				*avail = (strstr(buff2, "enabled") != NULL);
    677 				continue;
    678 			}
    679 			if(!strncmp(buff2, "printing", 8)) {
    680 				*printing = (strstr(buff2, "enabled") != NULL);
    681 				continue;
    682 			}
    683 			if(isdigit(buff2[0]) && (strstr(buff2, "entr") !=NULL)) {
    684 
    685 				*qlen = atoi(buff2);
    686 				continue;
    687 			}
    688 			if(strstr(buff2, "attention") != NULL ||
    689 			   strstr(buff2, "error") != NULL)
    690 				*needs_operator = TRUE;
    691 			if(*needs_operator || strstr(buff2, "waiting") != NULL)
    692 				strcpy(status, cp);
    693 		}
    694 		stat = PI_RES_OK;
    695 		break;
    696 	}
    697 	(void) pclose(p);
    698 	return(stat);
    699 }
    700 
    701 
    702 pcrstat pr_cancel(pr, user, id)
    703 char *pr;
    704 char *user;
    705 char *id;
    706 {
    707 char            cmdbuf[256];
    708 char            resbuf[256];
    709 FILE *fd;
    710 int i;
    711 pcrstat stat = PC_RES_NO_SUCH_JOB;
    712 
    713 	pr = map_printer_name(pr);
    714 	if(pr == NULL || suspicious(pr))
    715 		return(PC_RES_NO_SUCH_PRINTER);
    716 	if(suspicious(id))
    717 		return(PC_RES_NO_SUCH_JOB);
    718 
    719 		sprintf(cmdbuf, "%s/lprm -P%s %s", LPRDIR, pr, id);
    720 		if ((fd = su_popen(user, cmdbuf, MAXTIME_FOR_CANCEL)) == NULL) {
    721 			msg_out("rpc.pcnfsd: su_popen failed");
    722 			return(PC_RES_FAIL);
    723 		}
    724 		while(fgets(resbuf, 255, fd) != NULL) {
    725 			i = strlen(resbuf);
    726 			if(i)
    727 				resbuf[i-1] = '\0'; /* trim NL */
    728 			if(strstr(resbuf, "dequeued") != NULL)
    729 				stat = PC_RES_OK;
    730 			if(strstr(resbuf, "unknown printer") != NULL)
    731 				stat = PC_RES_NO_SUCH_PRINTER;
    732 			if(strstr(resbuf, "Permission denied") != NULL)
    733 				stat = PC_RES_NOT_OWNER;
    734 		}
    735 		if(su_pclose(fd) == 255)
    736 			msg_out("rpc.pcnfsd: su_pclose alert");
    737 		return(stat);
    738 }
    739 
    740 
    741 /*
    743 ** New subsystem here. We allow the administrator to define
    744 ** up to NPRINTERDEFS aliases for printer names. This is done
    745 ** using the "/etc/pcnfsd.conf" file, which is read at startup.
    746 ** There are three entry points to this subsystem
    747 **
    748 ** void add_printer_alias(char *printer, char *alias_for, char *command)
    749 **
    750 ** This is invoked from "config_from_file()" for each
    751 ** "printer" line. "printer" is the name of a printer; note that
    752 ** it is possible to redefine an existing printer. "alias_for"
    753 ** is the name of the underlying printer, used for queue listing
    754 ** and other control functions. If it is "-", there is no
    755 ** underlying printer, or the administrative functions are
    756 ** not applicable to this printer. "command"
    757 ** is the command which should be run (via "su_popen()") if a
    758 ** job is printed on this printer. The following tokens may be
    759 ** embedded in the command, and are substituted as follows:
    760 **
    761 ** $FILE	-	path to the file containing the print data
    762 ** $USER	-	login of user
    763 ** $HOST	-	hostname from which job originated
    764 **
    765 ** Tokens may occur multiple times. If The command includes no
    766 ** $FILE token, the string " $FILE" is silently appended.
    767 **
    768 ** pr_list list_virtual_printers()
    769 **
    770 ** This is invoked from build_pr_list to generate a list of aliased
    771 ** printers, so that the client that asks for a list of valid printers
    772 ** will see these ones.
    773 **
    774 ** char *map_printer_name(char *printer)
    775 **
    776 ** If "printer" identifies an aliased printer, this function returns
    777 ** the "alias_for" name, or NULL if the "alias_for" was given as "-".
    778 ** Otherwise it returns its argument.
    779 **
    780 ** char *expand_alias(char *printer, char *file, char *user, char *host)
    781 **
    782 ** If "printer" is an aliased printer, this function returns a
    783 ** pointer to a static string in which the corresponding command
    784 ** has been expanded. Otherwise ot returns NULL.
    785 */
    786 #define NPRINTERDEFS	16
    787 int num_aliases = 0;
    788 struct {
    789 	char *a_printer;
    790 	char *a_alias_for;
    791 	char *a_command;
    792 } alias [NPRINTERDEFS];
    793 
    794 
    795 
    796 void
    797 add_printer_alias(printer, alias_for, command)
    798 char *printer;
    799 char *alias_for;
    800 char *command;
    801 {
    802 	if(num_aliases < NPRINTERDEFS) {
    803 		alias[num_aliases].a_printer = strdup(printer);
    804 		alias[num_aliases].a_alias_for =
    805 			(strcmp(alias_for,  "-") ? strdup(alias_for) : NULL);
    806 		if(strstr(command, "$FILE"))
    807 			alias[num_aliases].a_command = strdup(command);
    808 		else {
    809 			alias[num_aliases].a_command = (char *)grab(strlen(command) + 8);
    810 			strcpy(alias[num_aliases].a_command, command);
    811 			strcat(alias[num_aliases].a_command, " $FILE");
    812 		}
    813 		num_aliases++;
    814 	}
    815 }
    816 
    817 pr_list list_virtual_printers()
    818 {
    819 pr_list first = NULL;
    820 pr_list last = NULL;
    821 pr_list curr = NULL;
    822 int i;
    823 
    824 
    825 	if(num_aliases == 0)
    826 		return(NULL);
    827 
    828 	for (i = 0; i < num_aliases; i++) {
    829 		curr = (struct pr_list_item *)
    830 			grab(sizeof (struct pr_list_item));
    831 
    832 		curr->pn = strdup(alias[i].a_printer);
    833 		if(alias[i].a_alias_for == NULL)
    834 			curr->device = strdup("");
    835 		else
    836 			curr->device = strdup(alias[i].a_alias_for);
    837 		curr->remhost = strdup("");
    838 		curr->cm = strdup("(alias)");
    839 		curr->pr_next = NULL;
    840 		if(last == NULL)
    841 			first = curr;
    842 		else
    843 			last->pr_next = curr;
    844 		last = curr;
    845 
    846 	}
    847 	return(first);
    848 }
    849 
    850 
    851 char *
    852 map_printer_name(printer)
    853 char *printer;
    854 {
    855 int i;
    856 	for (i = 0; i < num_aliases; i++){
    857 		if(!strcmp(printer, alias[i].a_printer))
    858 			return(alias[i].a_alias_for);
    859 	}
    860 	return(printer);
    861 }
    862 
    863 static void
    864 substitute(string, token, data)
    865 char *string;
    866 char *token;
    867 char *data;
    868 {
    869 char temp[512];
    870 char *c;
    871 
    872 	while(c = strstr(string, token)) {
    873 		*c = '\0';
    874 		strcpy(temp, string);
    875 		strcat(temp, data);
    876 		c += strlen(token);
    877 		strcat(temp, c);
    878 		strcpy(string, temp);
    879 	}
    880 }
    881 
    882 char *
    883 expand_alias(printer, file, user, host)
    884 char *printer;
    885 char *file;
    886 char *user;
    887 char *host;
    888 {
    889 static char expansion[512];
    890 int i;
    891 	for (i = 0; i < num_aliases; i++){
    892 		if(!strcmp(printer, alias[i].a_printer)) {
    893 			strcpy(expansion, alias[i].a_command);
    894 			substitute(expansion, "$FILE", file);
    895 			substitute(expansion, "$USER", user);
    896 			substitute(expansion, "$HOST", host);
    897 			return(expansion);
    898 		}
    899 	}
    900 	return(NULL);
    901 }
    902 
    903