1 /************************************************************************
2  *	Custom standard-io library					*
3  *									*
4  *	Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands	*
5  *	#include "../README"						*
6  ************************************************************************/
7 #ifdef RCS
8 static /*const*/char rcsid[]=
9  "$Id: cstdio.c,v 1.52 2000/11/22 01:29:56 guenther Exp $";
10 #endif
11 #include "procmail.h"
12 #include "robust.h"
13 #include "misc.h"
14 #include "lmtp.h"
15 #include "variables.h"
16 #include "shell.h"
17 #include "cstdio.h"
18 
19 static uchar rcbuf[STDBUF],*rcbufp,*rcbufend;	  /* buffer for custom stdio */
20 static off_t blasttell;
21 static struct dyna_array inced;				  /* includerc stack */
22 struct dynstring*incnamed;
23 
refill(offset)24 static void refill(offset)const int offset;		/* refill the buffer */
25 { int ret=rread(rc,rcbuf,STDBUF);
26   if(ret>0)
27    { rcbufend=rcbuf+ret;
28      rcbufp=rcbuf+offset;				 /* restore position */
29    }
30   else
31    { rcbufend=rcbuf;
32      rcbufp=rcbuf+1;					   /* looks like EOF */
33    }
34 }
35 
pushrc(name)36 void pushrc(name)const char*const name;		      /* open include rcfile */
37 { if(*name&&strcmp(name,devnull))
38    { struct stat stbuf;
39      if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode))
40 	goto rerr;
41      if(stbuf.st_size)					   /* only if size>0 */
42       { app_vali(inced,rcbufp?rcbufp-rcbuf:0);			 /* save old */
43 	app_valo(inced,blasttell);app_vali(inced,ifdepth);/* position, brace */
44 	app_vali(inced,rc);				       /* depth & fd */
45 	ifdepth=ifstack.filled;				  /* new stack depth */
46 	if(bopen(name)<0)			  /* try to open the new one */
47 	 { poprc();			       /* we couldn't, so restore rc */
48 rerr:	   readerr(name);
49 	 }
50       }
51    }
52 }
53 
changerc(name)54 void changerc(name)const char*const name;		    /* change rcfile */
55 { if(!*name||!strcmp(name,devnull))
56 pr:{ ifstack.filled=ifdepth;	   /* lose all the braces to avoid a warning */
57      rclose(rc);rcbufp=rcbufend+1;		    /* make it look like EOF */
58      return;
59    }
60   if(!strcmp(name,incnamed->ename))		    /* just restart this one */
61      lseek(rc,0,SEEK_SET),refill(0);
62   else
63    { struct stat stbuf;int orc;uchar*orbp,*orbe;struct dynstring*dp;
64      if(stat(name,&stbuf)||!S_ISREG(stbuf.st_mode))
65 rerr: { readerr(name);				      /* skip irregularities */
66 	return;
67       }
68      if(!stbuf.st_size)			    /* avoid opening trivial rcfiles */
69 	goto pr;
70      if(orbp=rcbufp,orbe=rcbufend,orc=rc,bopen(name)<0)
71       { rcbufp=orbp;rcbufend=orbe;rc=orc;		    /* restore state */
72 	goto rerr;
73       }
74      rclose(orc);				/* success! drop the old and */
75      if(dp=incnamed->enext)			      /* fixup the name list */
76 	incnamed->enext=dp->enext,free(dp);
77    }
78   ifstack.filled=ifdepth;			     /* close all the braces */
79 }
80 
duprcs(void)81 void duprcs P((void))		/* `duplicate' all the fds of opened rcfiles */
82 { size_t i;struct dynstring*dp;
83   dp=incnamed;rclose(rc);
84   if(0>(rc=ropen(dp->ename,O_RDONLY,0)))     /* first reopen the current one */
85      goto dupfailed;
86   lseek(rc,blasttell+STDBUF,SEEK_SET);	 /* you'll never know the difference */
87   for(i=inced.filled;dp=dp->enext,i;i-=3)
88    { int fd;
89      rclose(acc_vali(inced,--i));
90      if(0>(fd=ropen(dp->ename,O_RDONLY,0)))    /* reopen all (nested) others */
91 dupfailed:					   /* oops, file disappeared */
92 	nlog("Lost"),logqnl(dp->ename),exit(EX_NOINPUT);	    /* panic */
93      acc_vali(inced,i)=fd;		/* new & improved fd, decoupled from */
94    }							 /* fd in the parent */
95 }
96 
closeonerc(void)97 static void closeonerc P((void))
98 { struct dynstring*last;
99   if(rc>=0)
100      rclose(rc),rc= -1,last=incnamed,incnamed=last->enext,free(last);
101 }
102 
poprc(void)103 int poprc P((void))
104 { closeonerc();					     /* close it in any case */
105   if(ifstack.filled>ifdepth)		     /* force the matching of braces */
106      ifstack.filled=ifdepth,nlog("Missing closing brace\n");
107   if(!inced.filled)				  /* include stack is empty? */
108      return 0;	      /* restore rc, seekpos, prime rcbuf and restore rcbufp */
109   rc=acc_vali(inced,--inced.filled);
110   ifdepth=acc_vali(inced,--inced.filled);
111   blasttell=lseek(rc,acc_valo(inced,--inced.filled),SEEK_SET);
112   refill(acc_vali(inced,--inced.filled));
113   return 1;
114 }
115 
closerc(void)116 void closerc P((void))					/* {while(poprc());} */
117 { while(closeonerc(),inced.filled)
118      rc=acc_vali(inced,inced.filled-1),inced.filled-=4;
119   ifstack.filled=ifdepth=0;
120 }
121 							    /* destroys buf2 */
bopen(name)122 int bopen(name)const char*const name;				 /* my fopen */
123 { rcbufp=rcbufend=0;rc=ropen(name,O_RDONLY,0);
124   if(rc>=0)
125    { char*md;size_t len; /* if it's a relative name and an absolute $MAILDIR */
126      if(!strchr(dirsep,*name)&&
127 	*(md=(char*)tgetenv(maildir))&&
128 	strchr(dirsep,*md)&&
129 	(len=strlen(md))+strlen(name)+2<linebuf)
130       { strcpy(buf2,md);*(md=buf2+len)= *dirsep;strcpy(++md,name);
131 	md=buf2;				    /* then prepend $MAILDIR */
132       }
133      else
134 	md=(char*)name;			      /* pick the original otherwise */
135      newdynstring(&incnamed,md);
136    }
137   return rc;
138 }
139 
getbl(p,end)140 int getbl(p,end)char*p,*end;					  /* my gets */
141 { int i,overflow=0;char*q;
142   for(q=p,end--;;)
143    { switch(i=getb())
144       { case '\n':case EOF:*q='\0';
145 	   return overflow?-1:p!=q;	     /* did we read anything at all? */
146       }
147      if(q==end)	    /* check here so that a trailing backslash won't be lost */
148 	q=p,overflow=1;
149      *q++=i;
150    }
151 }
152 
getb(void)153 int getb P((void))						 /* my fgetc */
154 { if(rcbufp==rcbufend)						   /* refill */
155      blasttell=tell(rc),refill(0);
156   return rcbufp<rcbufend?(int)*rcbufp++:EOF;
157 }
158 
ungetb(x)159 void ungetb(x)const int x;	/* only for pushing back original characters */
160 { if(x!=EOF)
161      rcbufp--;							   /* backup */
162 }
163 
testB(x)164 int testB(x)const int x;	   /* fgetc that only succeeds if it matches */
165 { int i;
166   if((i=getb())==x)
167      return 1;
168   ungetb(i);
169   return 0;
170 }
171 
sgetc(void)172 int sgetc P((void))				/* a fake fgetc for a string */
173 { return *sgetcp?(int)*(uchar*)sgetcp++:EOF;
174 }
175 
skipspace(void)176 int skipspace P((void))
177 { int any=0;
178   while(testB(' ')||testB('\t'))
179      any=1;
180   return any;
181 }
182 
skipline(void)183 void skipline P((void))
184 { for(;;)					/* skip the rest of the line */
185      switch(getb())
186       { default:
187 	   continue;
188 	case '\n':case EOF:
189 	   return;
190       }
191 }
192 
getlline(target,end)193 int getlline(target,end)char*target,*end;
194 { char*chp2;int overflow;
195   for(overflow=0;;*target++='\n')
196      switch(getbl(chp2=target,end))			   /* read line-wise */
197       { case -1:overflow=1;
198 	case 1:
199 	   if(*(target=strchr(target,'\0')-1)=='\\')
200 	    { if(chp2!=target)				  /* non-empty line? */
201 		 target++;		      /* then preserve the backslash */
202 	      if(target>end-2)			  /* space enough for getbl? */
203 		 target=end-linebuf,overflow=1;		/* toss what we have */
204 	      continue;
205 	    }
206 	case 0:
207 	   if(overflow)
208 	    { nlog(exceededlb);setoverflow();
209 	    }
210 	   return overflow;
211       }
212 }
213 
214 #ifdef LMTP
215 static int origfd= -1;
216 
217 /* flush the input buffer and switch to a new input fd */
pushfd(fd)218 void pushfd(fd)int fd;
219 { origfd=rc;rc=fd;
220   rcbufend=rcbufp;
221 }
222 
223 /* restore the original input fd */
popfd(void)224 static int popfd P((void))
225 { rclose(rc);rc=origfd;
226   if(0>origfd)
227      return 0;
228   origfd= -1;
229   return 1;
230 }
231 
232 /*
233  * Are we at the end of an input read?	If so, we'll need to flush our
234  * output buffer to prevent a possible deadlock from the pipelining
235  */
endoread(void)236 int endoread P((void))
237 { return rcbufp>=rcbufend;
238 }
239 
240 /*
241  * refill the LMTP input buffer, switching back to the original input
242  * stream if needed
243  */
refillL(void)244 void refillL P((void))
245 { int retcode;
246   refill(0);
247   if(rcbufp>=rcbufend)				     /* we must have run out */
248    { if(popfd())				      /* try the original fd */
249       { refill(0);					  /* fill the buffer */
250 	if(rcbufp<rcbufend)		   /* looks good, clean up the child */
251 	 { if((retcode=waitfor(childserverpid))==EXIT_SUCCESS)
252 	      return;	     /* successfully switched and the child was fine */
253 	   syslog(LOG_WARNING,"LMTP child failed: exit code %d",retcode);
254 	   exit(EX_SOFTWARE);	       /* give up, give up, wherever you are */
255 	 }
256       }
257      exit(EX_NOINPUT);				     /* we ran out of input! */
258    }
259 }
260 
261 /* Like getb(), except for the LMTP input stream */
getL(void)262 int getL P((void))
263 { if(rcbufp==rcbufend)
264      refillL();
265   return (int)*rcbufp++;
266 }
267 
268 /* read a bunch of characters from the LMTP input stream */
readL(p,len)269 int readL(p,len)char*p;const int len;
270 { size_t min;
271   if(rcbufp==rcbufend)
272      refillL();
273   min=rcbufend-rcbufp;
274   if(min>len)
275      min=len;
276   tmemmove(p,rcbufp,min);
277   rcbufp+=min;
278   return min;
279 }
280 
281 /*
282  * read exactly len bytes from the LMTP input stream
283  * return 1 on success, 0 on EOF, and -1 on read error
284  */
readLe(p,len)285 int readLe(p,len)char*p;int len;
286 { long got=rcbufend-rcbufp;
287   if(got>0)				      /* first, copy from the buffer */
288    { if(got>len)			       /* is that more than we need? */
289 	got=len;
290      tmemmove(p,rcbufp,got);
291      rcbufp+=got;
292      p+=got;len-=got;
293    }
294   while(len)					   /* read the rest directly */
295    { if(0>(got=rread(rc,p,len)))
296 	return -1;
297      if(!got&&!popfd())
298 	return 0;
299      p+=got;len-=got;
300    }
301   return 1;
302 }
303 
304 #endif
305