1 /* 2 * ------+---------+---------+---------+---------+---------+---------+---------* 3 * Copyright (c) 2001 - Garance Alistair Drosehn <gad@FreeBSD.org>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * The views and conclusions contained in the software and documentation 28 * are those of the authors and should not be interpreted as representing 29 * official policies, either expressed or implied, of the FreeBSD Project. 30 * 31 * ------+---------+---------+---------+---------+---------+---------+---------* 32 */ 33 34 #ifndef lint 35 static const char rcsid[] = 36 "$FreeBSD$"; 37 #endif /* not lint */ 38 39 /* 40 * ctlinfo - This collection of routines will know everything there is to 41 * know about the information inside a control file ('cf*') which is used 42 * to describe a print job in lpr & friends. The eventual goal is that it 43 * will be the ONLY source file to know what's inside these control-files. 44 */ 45 46 /* 47 * Some define's useful for debuging. 48 * TRIGGERTEST_FNAME and DEBUGREADCF_FNAME, allow us to do testing on 49 * a per-spool-directory basis. 50 */ 51 /* #define TRIGGERTEST_FNAME "LpdTestRenameTF" */ 52 /* #define DEBUGREADCF_FNAME "LpdDebugReadCF" */ 53 /* #define LEAVE_TMPCF_FILES 1 */ 54 55 #include <sys/types.h> 56 #include <sys/stat.h> 57 #include <ctype.h> 58 #include <errno.h> 59 #include <fcntl.h> 60 #include <limits.h> 61 #include <netdb.h> 62 #include <stdio.h> 63 #include <stdlib.h> 64 #include <string.h> 65 #include <syslog.h> 66 #include <unistd.h> 67 #include "ctlinfo.h" 68 69 struct cjprivate { 70 struct cjobinfo pub; 71 char *cji_buff; /* buffer for getline */ 72 char *cji_eobuff; /* last byte IN the buffer */ 73 FILE *cji_fstream; 74 int cji_buffsize; /* # bytes in the buffer */ 75 int cji_dumpit; 76 }; 77 78 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 79 80 /* 81 * This has to be large enough to fit the maximum length of a single line 82 * in a control-file, including the leading 'command id', a trailing '\n' 83 * and ending '\0'. The max size of an 'U'nlink line, for instance, is 84 * 1 ('U') + PATH_MAX (filename) + 2 ('\n\0'). The maximum 'H'ost line is 85 * 1 ('H') + NI_MAXHOST (remote hostname) + 2 ('\n\0'). Other lines can be 86 * even longer than those. So, pick some nice, large, arbitrary value. 87 */ 88 #define CTI_LINEMAX PATH_MAX+NI_MAXHOST+5 89 90 extern const char *from_host; /* client's machine name */ 91 extern const char *from_ip; /* client machine's IP address */ 92 93 __BEGIN_DECLS 94 void ctl_dumpcji(FILE *_dbg_stream, const char *_heading, 95 struct cjobinfo *_cjinf); 96 void ctl_freeinf(struct cjobinfo *_cjinf); 97 static char *ctl_getline(struct cjobinfo *_cjinf); 98 struct cjobinfo *ctl_readcf(const char *_ptrname, const char *_cfname); 99 static void ctl_rewindcf(struct cjobinfo *_cjinf); 100 char *ctl_rmjob(const char *_ptrname, const char *_cfname); 101 __END_DECLS 102 103 /* 104 * Here are some things which might be needed when compiling this under 105 * platforms other than FreeBSD. 106 */ 107 #ifndef __FreeBSD__ 108 # ifndef NAME_MAX 109 # define NAME_MAX 255 110 # endif 111 # ifndef NI_MAXHOST 112 # define NI_MAXHOST 1025 113 # endif 114 # ifndef PATH_MAX 115 # define PATH_MAX 1024 116 # endif 117 __BEGIN_DECLS 118 char *strdup(const char *_src); 119 size_t strlcpy(char *_dst, const char *_src, size_t _siz); 120 __END_DECLS 121 #endif 122 123 /* 124 * Control-files (cf*) have the following format. 125 * 126 * Each control-file describes a single job. It will list one or more 127 * "datafiles" (df*) which should be copied to some printer. Usually 128 * there is only one datafile per job. For the curious, RFC 1179 is an 129 * informal and out-of-date description of lpr/lpd circa 1990. 130 * 131 * Each line in the file gives an attribute of the job as a whole, or one 132 * of the datafiles in the job, or a "command" indicating something to do 133 * with one of the datafiles. Each line starts with an 'id' that indicates 134 * what that line is there for. The 'id' is historically a single byte, 135 * but may be multiple bytes (obviously it would be best if multi-byte ids 136 * started with some letter not already used as a single-byte id!). 137 * After the 'id', the remainder of the line will be the value of the 138 * indicated attribute, or a name of the datafile to be operated on. 139 * 140 * In the following lists of ids, the ids with a '!' in front of them are 141 * NOT explicitly supported by this version of lpd, or at least "not yet 142 * supported". They are only listed for reference purposes, so people 143 * won't be tempted to reuse the same id for a different purpose. 144 * 145 * The following are attributes of the job which should not appear more 146 * than once in a control file. Only the 'H' and 'P' lines are required 147 * by the RFC, but some implementations of lpr won't even get that right. 148 * 149 * ! A - [used by lprNG] 150 * B - As far as I know, this is never used as a single-byte id. 151 * Therefore, I intend to use it for multi-byte id codes. 152 * C - "class name" to display on banner page (this is sometimes 153 * used to hold options for print filters) 154 * ! D - [in lprNG, "timestamp" of when the job was submitted] 155 * ! E - "environment variables" to set [some versions of linux] 156 * H - "host name" of machine where the original 'lpr' was done 157 * I - "indent", the amount to indent output 158 * J - "job name" to display on banner page 159 * L - "literal" user's name as it should be displayed on the 160 * banner page (it is the existence of an 'L' line which 161 * indicates that a job should have a banner page). 162 * M - "mail", userid to mail to when done printing (with email 163 * going to 'M'@'H', so to speak). 164 * P - "person", the user's login name (e.g. for accounting) 165 * ! Q - [used by lprNG for queue-name] 166 * R - "resolution" in dpi, for some laser printer queues 167 * T - "title" for files sent thru 'pr' 168 * W - "width" to use for printing plain-text files 169 * Z - In BSD, "locale" to use for datafiles sent thru 'pr'. 170 * (this BSD usage should move to a different id...) 171 * [in lprNG - this line holds the "Z options"] 172 * 1 - "R font file" for files sent thru troff 173 * 2 - "I font file" for files sent thru troff 174 * 3 - "B font file" for files sent thru troff 175 * 4 - "S font file" for files sent thru troff 176 * 177 * The following are attributes attached to a datafile, and thus may 178 * appear multiple times in a control file (once per datafile): 179 * 180 * N - "name" of file (for display purposes, used by 'lpq') 181 * S - "stat() info" used for symbolic link ('lpr -s') 182 * security checks. 183 * 184 * The following indicate actions to take on a given datafile. The same 185 * datafile may appear on more than one "print this file" command in the 186 * control file. Note that ALL ids with lowercase letters are expected 187 * to be actions to "print this file": 188 * 189 * c - "file name", cifplot file to print. This action appears 190 * when the user has requested 'lpr -c'. 191 * d - "file name", dvi file to print, user requested 'lpr -d' 192 * f - "file name", a plain-text file to print = "standard" 193 * g - "file name", plot(1G) file to print, ie 'lpr -g' 194 * l - "file name", text file with control chars which should 195 * be printed literally, ie 'lpr -l' (note: some printers 196 * take this id as a request to print a postscript file, 197 * and because of *that* some OS's use 'l' to indicate 198 * that a datafile is a postscript file) 199 * n - "file name", ditroff(1) file to print, ie 'lpr -n' 200 * o - "file name", a postscript file to print. This id is 201 * described in the original RFC, but not much has been 202 * done with it. This 'lpr' does not generate control 203 * lines with 'o'-actions, but lpd's printjob processing 204 * will treat it the same as 'l'. 205 * p - "file name", text file to print with pr(1), ie 'lpr -p' 206 * t - "file name", troff(1) file to print, ie 'lpr -t' 207 * v - "file name", plain raster file to print 208 * 209 * U - "file name" of datafile to unlink (ie, remove file 210 * from spool directory. To be done in a 'Pass 2', 211 * AFTER having processed all datafiles in the job). 212 * 213 */ 214 215 void 216 ctl_freeinf(struct cjobinfo *cjinf) 217 { 218 #define FREESTR(xStr) \ 219 if (xStr != NULL) { \ 220 free(xStr); \ 221 xStr = NULL;\ 222 } 223 224 struct cjprivate *cpriv; 225 226 if (cjinf == NULL) 227 return; 228 cpriv = cjinf->cji_priv; 229 if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { 230 syslog(LOG_ERR, "in ctl_freeinf(%p): invalid cjinf (cpriv %p)", 231 (void *)cjinf, (void *)cpriv); 232 return; 233 } 234 235 FREESTR(cpriv->pub.cji_accthost); 236 FREESTR(cpriv->pub.cji_acctuser); 237 FREESTR(cpriv->pub.cji_class); 238 FREESTR(cpriv->pub.cji_curqueue); 239 /* [cpriv->pub.cji_fname is part of cpriv-malloced area] */ 240 FREESTR(cpriv->pub.cji_jobname); 241 FREESTR(cpriv->pub.cji_mailto); 242 FREESTR(cpriv->pub.cji_username); 243 244 if (cpriv->cji_fstream != NULL) { 245 fclose(cpriv->cji_fstream); 246 cpriv->cji_fstream = NULL; 247 } 248 249 cjinf->cji_priv = NULL; 250 free(cpriv); 251 #undef FREESTR 252 } 253 254 #ifdef DEBUGREADCF_FNAME 255 static FILE *ctl_dbgfile = NULL; 256 static struct stat ctl_dbgstat; 257 #endif 258 static int ctl_dbgline = 0; 259 260 struct cjobinfo * 261 ctl_readcf(const char *ptrname, const char *cfname) 262 { 263 int id; 264 char *lbuff; 265 void *cstart; 266 FILE *cfile; 267 struct cjprivate *cpriv; 268 struct cjobinfo *cjinf; 269 size_t msize, sroom, sroom2; 270 271 cfile = fopen(cfname, "r"); 272 if (cfile == NULL) { 273 syslog(LOG_ERR, "%s: ctl_readcf error fopen(%s): %s", 274 ptrname, cfname, strerror(errno)); 275 return NULL; 276 } 277 278 sroom = roundup(sizeof(struct cjprivate), 8); 279 sroom2 = sroom + strlen(cfname) + 1; 280 sroom2 = roundup(sroom2, 8); 281 msize = sroom2 + CTI_LINEMAX; 282 msize = roundup(msize, 8); 283 cstart = malloc(msize); 284 if (cstart == NULL) 285 return NULL; 286 memset(cstart, 0, msize); 287 cpriv = (struct cjprivate *)cstart; 288 cpriv->pub.cji_priv = cpriv; 289 290 cpriv->pub.cji_fname = (char *)cstart + sroom; 291 strcpy(cpriv->pub.cji_fname, cfname); 292 cpriv->cji_buff = (char *)cstart + sroom2; 293 cpriv->cji_buffsize = (int)(msize - sroom2); 294 cpriv->cji_eobuff = (char *)cstart + msize - 1; 295 296 cpriv->cji_fstream = cfile; 297 cpriv->pub.cji_curqueue = strdup(ptrname); 298 299 ctl_dbgline = 0; 300 #ifdef DEBUGREADCF_FNAME 301 ctl_dbgfile = NULL; 302 id = stat(DEBUGREADCF_FNAME, &ctl_dbgstat); 303 if (id != -1) { 304 /* the file exists in this spool directory, write some simple 305 * debugging info to it */ 306 ctl_dbgfile = fopen(DEBUGREADCF_FNAME, "a"); 307 if (ctl_dbgfile != NULL) { 308 fprintf(ctl_dbgfile, "%s: s=%p r=%ld e=%p %p->%s\n", 309 ptrname, (void *)cpriv, (long)sroom, 310 cpriv->cji_eobuff, cpriv->pub.cji_fname, 311 cpriv->pub.cji_fname); 312 } 313 } 314 #endif 315 /* 316 * Copy job-attribute values from control file to the struct of 317 * "public" information. In some cases, it is invalid for the 318 * value to be a null-string, so that is ignored. 319 */ 320 cjinf = &(cpriv->pub); 321 lbuff = ctl_getline(cjinf); 322 while (lbuff != NULL) { 323 id = *lbuff++; 324 switch (id) { 325 case 'C': 326 cpriv->pub.cji_class = strdup(lbuff); 327 break; 328 case 'H': 329 if (*lbuff == '\0') 330 break; 331 cpriv->pub.cji_accthost = strdup(lbuff); 332 break; 333 case 'J': 334 cpriv->pub.cji_jobname = strdup(lbuff); 335 break; 336 case 'L': 337 cpriv->pub.cji_username = strdup(lbuff); 338 break; 339 case 'M': 340 /* 341 * No valid mail-to address would start with a minus. 342 * If this one does, it is probably some trickster who 343 * is trying to trigger options on sendmail. Ignore. 344 */ 345 if (*lbuff == '-') 346 break; 347 if (*lbuff == '\0') 348 break; 349 cpriv->pub.cji_mailto = strdup(lbuff); 350 break; 351 case 'P': 352 /* don't allow userid's with a leading minus, either */ 353 if (*lbuff == '-') 354 break; 355 if (*lbuff == '\0') 356 break; 357 cpriv->pub.cji_acctuser = strdup(lbuff); 358 break; 359 default: 360 if (islower(id)) { 361 cpriv->pub.cji_dfcount++; 362 } 363 break; 364 } 365 lbuff = ctl_getline(cjinf); 366 } 367 368 /* the 'H'ost and 'P'erson fields are *always* supposed to be there */ 369 if (cpriv->pub.cji_accthost == NULL) 370 cpriv->pub.cji_accthost = strdup(".na."); 371 if (cpriv->pub.cji_acctuser == NULL) 372 cpriv->pub.cji_acctuser = strdup(".na."); 373 374 #ifdef DEBUGREADCF_FNAME 375 if (ctl_dbgfile != NULL) { 376 if (cpriv->cji_dumpit) 377 ctl_dumpcji(ctl_dbgfile, "end readcf", &(cpriv->pub)); 378 fclose(ctl_dbgfile); 379 ctl_dbgfile = NULL; 380 } 381 #endif 382 return &(cpriv->pub); 383 } 384 385 /* 386 * This routine renames the temporary control file as received from some 387 * other (remote) host. That file will almost always with `tfA*', because 388 * recvjob.c creates the file by changing `c' to `t' in the original name 389 * for the control file. Now if you read the RFC, you would think that all 390 * control filenames start with `cfA*'. However, it seems there are some 391 * implementations which send control filenames which start with `cf' 392 * followed by *any* letter, so this routine can not assume what the third 393 * letter will (or will not) be. Sigh. 394 * 395 * So this will rewrite the temporary file to `rf*' (correcting any lines 396 * which need correcting), rename that `rf*' file to `cf*', and then remove 397 * the original `tf*' temporary file. 398 * 399 * The *main* purpose of this routine is to be paranoid about the contents 400 * of that control file. It is partially meant to protect against people 401 * TRYING to cause trouble (perhaps after breaking into root of some host 402 * that this host will accept print jobs from). The fact that we're willing 403 * to print jobs from some remote host does not mean that we should blindly 404 * do anything that host tells us to do. 405 * 406 * This is also meant to protect us from errors in other implementations of 407 * lpr, particularly since we may want to use some values from the control 408 * file as environment variables when it comes time to print, or as parameters 409 * to commands which will be exec'ed, or values in statistics records. 410 * 411 * This may also do some "conversions" between how different versions of 412 * lpr or lprNG define the contents of various lines in a control file. 413 * 414 * If there is an error, it returns a pointer to a descriptive error message. 415 * Error messages which are RETURNED (as opposed to syslog-ed) do not include 416 * the printer-queue name. Let the caller add that if it is wanted. 417 */ 418 char * 419 ctl_renametf(const char *ptrname, const char *tfname) 420 { 421 int chk3rd, newfd, nogood, res; 422 FILE *newcf; 423 struct cjobinfo *cjinf; 424 char *lbuff, *slash, *cp; 425 char tfname2[NAME_MAX+1], cfname2[NAME_MAX+1]; 426 char errm[CTI_LINEMAX]; 427 428 #ifdef TRIGGERTEST_FNAME 429 struct stat tstat; 430 res = stat(TRIGGERTEST_FNAME, &tstat); 431 if (res == -1) { 432 /* 433 * if the trigger file does NOT exist in this spool directory, 434 * then do the exact same steps that the pre-ctlinfo code had 435 * been doing. Ie, very little. 436 */ 437 strlcpy(cfname2, tfname, sizeof(cfname2)); 438 cfname2[0] = 'c'; 439 res = link(tfname, cfname2); 440 if (res < 0) { 441 snprintf(errm, sizeof(errm), 442 "ctl_renametf error link(%s,%s): %s", tfname, 443 cfname2, strerror(errno)); 444 return strdup(errm); 445 } 446 unlink(tfname); 447 return NULL; 448 } 449 #endif 450 cjinf = NULL; /* in case of early jump to error_ret */ 451 newcf = NULL; /* in case of early jump to error_ret */ 452 *errm = '\0'; /* in case of early jump to error_ret */ 453 454 chk3rd = tfname[2]; 455 if ((tfname[0] != 't') || (tfname[1] != 'f') || (!isalpha(chk3rd))) { 456 snprintf(errm, sizeof(errm), 457 "ctl_renametf invalid filename: %s", tfname); 458 goto error_ret; 459 } 460 461 cjinf = ctl_readcf(ptrname, tfname); 462 if (cjinf == NULL) { 463 snprintf(errm, sizeof(errm), 464 "ctl_renametf error cti_readcf(%s)", tfname); 465 goto error_ret; 466 } 467 468 /* 469 * This uses open+fdopen instead of fopen because that combination 470 * gives us greater control over file-creation issues. 471 */ 472 strlcpy(tfname2, tfname, sizeof(tfname2)); 473 tfname2[0] = 'r'; /* rf<letter><job><hostname> */ 474 newfd = open(tfname2, O_WRONLY|O_CREAT|O_TRUNC, 0660); 475 if (newfd == -1) { 476 snprintf(errm, sizeof(errm), 477 "ctl_renametf error open(%s): %s", tfname2, 478 strerror(errno)); 479 goto error_ret; 480 } 481 newcf = fdopen(newfd, "w"); 482 if (newcf == NULL) { 483 close(newfd); 484 snprintf(errm, sizeof(errm), 485 "ctl_renametf error fopen(%s): %s", tfname2, 486 strerror(errno)); 487 goto error_ret; 488 } 489 490 /* 491 * Do extra sanity checks on some key job-attribute fields, and 492 * write them out first (thus making sure they are written in the 493 * order we generally expect them to be in). 494 */ 495 /* 496 * Some lpr implementations on PC's set a null-string for their 497 * hostname. A MacOS 10 system which has not correctly setup 498 * /etc/hostconfig will claim a hostname of 'localhost'. Anything 499 * with blanks in it would be an invalid value for hostname. For 500 * any of these invalid hostname values, replace the given value 501 * with the name of the host that this job is coming from. 502 */ 503 nogood = 0; 504 if (cjinf->cji_accthost == NULL) 505 nogood = 1; 506 else if (strcmp(cjinf->cji_accthost, ".na.") == 0) 507 nogood = 1; 508 else if (strcmp(cjinf->cji_accthost, "localhost") == 0) 509 nogood = 1; 510 else { 511 for (cp = cjinf->cji_accthost; *cp != '\0'; cp++) { 512 if (*cp <= ' ') { 513 nogood = 1; 514 break; 515 } 516 } 517 } 518 if (nogood) 519 fprintf(newcf, "H%s\n", from_host); 520 else 521 fprintf(newcf, "H%s\n", cjinf->cji_accthost); 522 523 /* 524 * Now do some sanity checks on the 'P' (original userid) value. Note 525 * that the 'P'erson line is the second line which is ALWAYS supposed 526 * to be present in a control file. 527 * 528 * There is no particularly good value to use for replacements, but 529 * at least make sure the value is something reasonable to use in 530 * environment variables and statistics records. Again, some PC 531 * implementations send a null-string for a value. Various Mac 532 * implementations will set whatever string the user has set for 533 * their 'Owner Name', which usually includes blanks, etc. 534 */ 535 nogood = 0; 536 if (cjinf->cji_acctuser == NULL) 537 nogood = 1; 538 else { 539 for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) { 540 if (*cp <= ' ') 541 *cp = '_'; 542 } 543 } 544 if (nogood) 545 fprintf(newcf, "P%s\n", ".na."); 546 else 547 fprintf(newcf, "P%s\n", cjinf->cji_acctuser); 548 549 /* No need for sanity checks on class, jobname, "literal" user. */ 550 if (cjinf->cji_class != NULL) 551 fprintf(newcf, "C%s\n", cjinf->cji_class); 552 if (cjinf->cji_jobname != NULL) 553 fprintf(newcf, "J%s\n", cjinf->cji_jobname); 554 if (cjinf->cji_username != NULL) 555 fprintf(newcf, "L%s\n", cjinf->cji_username); 556 557 /* 558 * This should probably add more sanity checks on mailto value. 559 * Note that if the mailto value is "wrong", then there's no good 560 * way to know what the "correct" value would be, and we should not 561 * semd email to some random address. At least for now, just ignore 562 * any invalid values. 563 */ 564 nogood = 0; 565 if (cjinf->cji_mailto == NULL) 566 nogood = 1; 567 else { 568 for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) { 569 if (*cp <= ' ') { 570 nogood = 1; 571 break; 572 } 573 } 574 } 575 if (!nogood) 576 fprintf(newcf, "M%s\n", cjinf->cji_mailto); 577 578 /* 579 * Now go thru the old control file, copying all information which 580 * hasn't already been written into the new file. 581 */ 582 ctl_rewindcf(cjinf); 583 lbuff = ctl_getline(cjinf); 584 while (lbuff != NULL) { 585 switch (lbuff[0]) { 586 case 'H': 587 case 'P': 588 case 'C': 589 case 'J': 590 case 'L': 591 case 'M': 592 /* already wrote values for these to the newcf */ 593 break; 594 case 'N': 595 /* see comments under 'U'... */ 596 if (cjinf->cji_dfcount == 0) { 597 /* in this case, 'N's will be done in 'U' */ 598 break; 599 } 600 fprintf(newcf, "%s\n", lbuff); 601 break; 602 case 'U': 603 /* 604 * check for the very common case where the remote 605 * host had to process 'lpr -s -r', but it did not 606 * remove the Unlink line from the control file. 607 * Such Unlink lines will legitimately have a '/' in 608 * them, but it is the original lpr host which would 609 * have done the unlink of such files, and not any 610 * host receiving that job. 611 */ 612 slash = strchr(lbuff, '/'); 613 if (slash != NULL) { 614 break; /* skip this line */ 615 } 616 /* 617 * Okay, another kind of broken lpr implementation 618 * is one which send datafiles, and Unlink's those 619 * datafiles, but never includes any PRINT request 620 * for those files. Experimentation shows that one 621 * copy of those datafiles should be printed with a 622 * format of 'f'. If this is an example of such a 623 * screwed-up control file, fix it here. 624 */ 625 if (cjinf->cji_dfcount == 0) { 626 lbuff++; 627 if (strncmp(lbuff, "df", (size_t)2) == 0) { 628 fprintf(newcf, "f%s\n", lbuff); 629 fprintf(newcf, "U%s\n", lbuff); 630 fprintf(newcf, "N%s\n", lbuff); 631 } 632 break; 633 } 634 fprintf(newcf, "%s\n", lbuff); 635 break; 636 default: 637 fprintf(newcf, "%s\n", lbuff); 638 break; 639 } 640 lbuff = ctl_getline(cjinf); 641 } 642 643 ctl_freeinf(cjinf); 644 cjinf = NULL; 645 646 res = fclose(newcf); 647 newcf = NULL; 648 if (res != 0) { 649 snprintf(errm, sizeof(errm), 650 "ctl_renametf error fclose(%s): %s", tfname2, 651 strerror(errno)); 652 goto error_ret; 653 } 654 655 strlcpy(cfname2, tfname, sizeof(cfname2)); 656 cfname2[0] = 'c'; /* rename new file to 'cfA*' */ 657 res = link(tfname2, cfname2); 658 if (res != 0) { 659 snprintf(errm, sizeof(errm), 660 "ctl_renametf error link(%s,%s): %s", tfname2, cfname2, 661 strerror(errno)); 662 goto error_ret; 663 } 664 665 /* All the important work is done. Now just remove temp files */ 666 #ifdef LEAVE_TMPCF_FILES 667 { 668 struct stat tfstat; 669 size_t size1; 670 tfstat.st_size = 1; /* certainly invalid value */ 671 res = stat(tfname, &tfstat); 672 size1 = tfstat.st_size; 673 tfstat.st_size = 2; /* certainly invalid value */ 674 res = stat(tfname2, &tfstat); 675 /* if the sizes do not match, or either stat call failed, 676 * then do not remove the temp files, but return "all OK". 677 * This is just so I can see what this routine had changed. 678 */ 679 if (size1 != tfstat.st_size) 680 return NULL; 681 } 682 #endif 683 unlink(tfname); 684 unlink(tfname2); 685 686 return NULL; 687 688 error_ret: 689 if (cjinf != NULL) 690 ctl_freeinf(cjinf); 691 if (newcf != NULL) 692 fclose(newcf); 693 694 if (*errm != '\0') 695 return strdup(errm); 696 return strdup("ctl_renametf internal (missed) error"); 697 } 698 699 void 700 ctl_rewindcf(struct cjobinfo *cjinf) 701 { 702 struct cjprivate *cpriv; 703 704 if (cjinf == NULL) 705 return; 706 cpriv = cjinf->cji_priv; 707 if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { 708 syslog(LOG_ERR, "in ctl_rewindcf(%p): invalid cjinf (cpriv %p)", 709 (void *)cjinf, (void *)cpriv); 710 return; 711 } 712 713 rewind(cpriv->cji_fstream); /* assume no errors... :-) */ 714 } 715 716 char * 717 ctl_rmjob(const char *ptrname, const char *cfname) 718 { 719 struct cjobinfo *cjinf; 720 char *lbuff; 721 char errm[CTI_LINEMAX]; 722 723 cjinf = ctl_readcf(ptrname, cfname); 724 if (cjinf == NULL) { 725 snprintf(errm, sizeof(errm), 726 "ctl_renametf error cti_readcf(%s)", cfname); 727 return strdup(errm); 728 } 729 730 ctl_rewindcf(cjinf); 731 lbuff = ctl_getline(cjinf); 732 while (lbuff != NULL) { 733 /* obviously we need to fill in the following... */ 734 switch (lbuff[0]) { 735 case 'S': 736 break; 737 case 'U': 738 break; 739 default: 740 break; 741 } 742 lbuff = ctl_getline(cjinf); 743 } 744 745 ctl_freeinf(cjinf); 746 cjinf = NULL; 747 748 return NULL; 749 } 750 751 /* 752 * The following routine was originally written to pin down a bug. It is 753 * no longer needed for that problem, but may be useful to keep around for 754 * other debugging. 755 */ 756 void 757 ctl_dumpcji(FILE *dbg_stream, const char *heading, struct cjobinfo *cjinf) 758 { 759 #define PRINTSTR(xHdr,xStr) \ 760 astr = xStr; \ 761 ctl_dbgline++; \ 762 fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, xHdr); \ 763 if (astr == NULL) \ 764 fprintf(dbg_stream, "NULL\n"); \ 765 else \ 766 fprintf(dbg_stream, "%p -> %s\n", astr, astr) 767 768 struct cjprivate *cpriv; 769 char *astr; 770 771 if (cjinf == NULL) { 772 fprintf(dbg_stream, 773 "ctl_dumpcji: ptr to cjobinfo for '%s' is NULL\n", 774 heading); 775 return; 776 } 777 cpriv = cjinf->cji_priv; 778 779 fprintf(dbg_stream, "ctl_dumpcji: Dump '%s' of cjobinfo at %p->%p\n", 780 heading, (void *)cjinf, cpriv->cji_buff); 781 782 PRINTSTR("accthost.H", cpriv->pub.cji_accthost); 783 PRINTSTR("acctuser.P", cpriv->pub.cji_acctuser); 784 PRINTSTR("class.C", cpriv->pub.cji_class); 785 PRINTSTR("cf-qname", cpriv->pub.cji_curqueue); 786 PRINTSTR("cf-fname", cpriv->pub.cji_fname); 787 PRINTSTR("jobname.J", cpriv->pub.cji_jobname); 788 PRINTSTR("mailto.M", cpriv->pub.cji_mailto); 789 PRINTSTR("hdruser.L", cpriv->pub.cji_username); 790 791 ctl_dbgline++; 792 fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, "*cjprivate"); 793 if (cpriv->pub.cji_priv == NULL) 794 fprintf(dbg_stream, "NULL !!\n"); 795 else 796 fprintf(dbg_stream, "%p\n", (void *)cpriv->pub.cji_priv); 797 798 fprintf(dbg_stream, "|- - - - --> Dump '%s' complete\n", heading); 799 800 /* flush output for the benefit of anyone doing a 'tail -f' */ 801 fflush(dbg_stream); 802 803 #undef PRINTSTR 804 } 805 806 /* 807 * This routine reads in the next line from the control-file, and removes 808 * the trailing newline character. 809 * 810 * Historical note: Earlier versions of this routine did tab-expansion for 811 * ALL lines read in, which did not make any sense for most of the lines 812 * in a control file. For the lines where tab-expansion is useful, it will 813 * now have to be done by the calling routine. 814 */ 815 static char * 816 ctl_getline(struct cjobinfo *cjinf) 817 { 818 char *strp, *nl; 819 struct cjprivate *cpriv; 820 821 if (cjinf == NULL) 822 return NULL; 823 cpriv = cjinf->cji_priv; 824 if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) { 825 syslog(LOG_ERR, "in ctl_getline(%p): invalid cjinf (cpriv %p)", 826 (void *)cjinf, (void *)cpriv); 827 return NULL; 828 } 829 830 errno = 0; 831 strp = fgets(cpriv->cji_buff, cpriv->cji_buffsize, cpriv->cji_fstream); 832 if (strp == NULL) { 833 if (errno != 0) 834 syslog(LOG_ERR, "%s: ctl_getline error fgets(%s): %s", 835 cpriv->pub.cji_curqueue, cpriv->pub.cji_fname, 836 strerror(errno)); 837 return NULL; 838 } 839 nl = strchr(strp, '\n'); 840 if (nl != NULL) 841 *nl = '\0'; 842 843 #ifdef DEBUGREADCF_FNAME 844 /* I'd like to find out if the previous work to expand tabs was ever 845 * really used, and if so, on what lines and for what reason. 846 * Yes, all this work probably means I'm obsessed about this 'tab' 847 * issue, but isn't programming a matter of obsession? 848 */ 849 { 850 int tabcnt; 851 char *ch; 852 853 tabcnt = 0; 854 ch = strp; 855 for (ch = strp; *ch != '\0'; ch++) { 856 if (*ch == '\t') 857 tabcnt++; 858 } 859 860 if (tabcnt && (ctl_dbgfile != NULL)) { 861 cpriv->cji_dumpit++; 862 fprintf(ctl_dbgfile, "%s: tabs=%d '%s'\n", 863 cpriv->pub.cji_fname, tabcnt, cpriv->cji_buff); 864 } 865 } 866 #endif 867 return strp; 868 } 869