1 /*$Id: ezmlm-clean.c,v 1.30 1999/05/12 22:15:26 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
18 #include "readwrite.h"
23 #include "date822fmt.h"
33 int flagmime = MOD_MIME; /* default is message as attachment */
34 int flagreturn = 1; /* default return timed-out messages */
35 char flagcd = '\0'; /* default: no transferencoding */
38 /* When ezmlm-clean is run, messages and message stubs in pending/ */
39 /* rejected/accepted are erased if they are older than delay hours. */
40 /* Timeouts in h for messages. If modtime has a number, it is made to be*/
41 /* in the range DELAY_MIN..DELAY_MAX. If the number is 0 or there is no */
42 /* number, DELAY_DEFAULT is used. Messages that are read-only are */
43 /* ignored. Messages in 'pending' that have the execute bit set result */
44 /* in an informative reply to the poster. Any defects in the message */
45 /* format, inability to open the file, etc, result in a maillog entry */
46 /* whereafter the message is erased. */
48 /* The defines are in "idx.h" */
50 #define FATAL "ezmlm-clean: fatal: "
54 strerr_die4x(111,FATAL,ERR_READ,fnmsg.s,": ");
59 strerr_die1x(100,"ezmlm-clean: usage: ezmlm-clean [-mMrRvV] dir");
62 void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
72 int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
74 qmail_put(&qq,buf,len);
78 substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
81 char strnum[FMT_ULONG];
82 char date[DATE822FMT];
83 char boundary[COOKIE];
84 datetime_sec hashdate;
86 stralloc outhost = {0};
87 stralloc outlocal = {0};
88 stralloc mailinglist = {0};
89 stralloc listid = {0};
90 stralloc quoted = {0};
92 stralloc modtime = {0};
94 stralloc charset = {0};
99 unsigned long msgnum = 0;
100 /* counter to make message-id unique, since we may */
101 /* send out several msgs. This is not bullet-proof.*/
102 /* Duplication occurs if we do x>1 msg && another */
103 /* ezmlm started within x seconds, and with the */
104 /* same pid. Very unlikely. */
109 qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
111 qmail_puts(&qq,"Quoted-Printable\n\n");
113 qmail_puts(&qq,"base64\n\n");
115 qmail_puts(&qq,"\n\n");
118 /* gets outlocal, outhost, etc. This is done only if there are any timed-out*/
119 /* messages found, that merit a reply to the author. */
122 getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
123 getconf_line(&listid,"listid",0,FATAL,dir);
124 getconf_line(&outhost,"outhost",1,FATAL,dir);
125 getconf_line(&outlocal,"outlocal",1,FATAL,dir);
126 set_cpouthost(&outlocal);
127 set_cpoutlocal(&outlocal);
132 /* sends file pointed to by d to the address in the return-path of the */
141 if (qmail_open(&qq, (stralloc *) 0) == -1)
142 strerr_die2x(111,FATAL,ERR_QMAIL_QUEUE);
146 strerr_die4sys(111,FATAL,ERR_OPEN,d,": ");
147 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
148 if (getln(&sstext,&line,&match,'\n') == -1) die_read();
149 if (!match) die_read();
150 if (!case_startb(line.s,line.len,"return-path:")) die_read();
151 x = 12 + byte_chr(line.s + 12,line.len-12,'<');
152 y = byte_rchr(line.s + x,line.len-x,'>');
153 if (x != line.len && x+y != line.len) {
154 if (!stralloc_copyb(&to,line.s+x+1, y-1)) die_nomem();
155 if (!stralloc_0(&to)) die_nomem();
158 qmail_puts(&qq,"Mailing-List: ");
159 qmail_put(&qq,mailinglist.s,mailinglist.len);
160 qmail_puts(&qq,"\nList-ID: ");
161 qmail_put(&qq,listid.s,listid.len);
162 qmail_puts(&qq,"\nDate: ");
163 datetime_tai(&dt,when);
164 qmail_put(&qq,date,date822fmt(date,&dt));
165 qmail_puts(&qq,"Message-ID: <");
166 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,
167 (unsigned long) when + msgnum++))) die_nomem();
168 if (!stralloc_append(&line,".")) die_nomem();
169 if (!stralloc_catb(&line,strnum,
170 fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
171 if (!stralloc_cats(&line,".ezmlm@")) die_nomem();
172 if (!stralloc_cat(&line,&outhost)) die_nomem();
173 if (!stralloc_0(&line)) die_nomem();
174 qmail_puts(&qq,line.s);
175 /* "unique" MIME boundary as hash of messageid */
176 cookie(boundary,"",0,"",line.s,"");
177 qmail_puts(&qq,">\nFrom: ");
178 if (!quote("ed,&outlocal)) die_nomem();
179 qmail_put(&qq,quoted.s,quoted.len);
180 qmail_puts(&qq,"-help@");
181 qmail_put(&qq,outhost.s,outhost.len);
182 qmail_puts(&qq,"\nSubject: ");
183 qmail_puts(&qq,TXT_RETURNED_POST);
184 qmail_put(&qq,quoted.s,quoted.len);
186 qmail_put(&qq,outhost.s,outhost.len);
187 qmail_puts(&qq, "\nTo: ");
188 qmail_puts(&qq,to.s);
190 if (getconf_line(&charset,"charset",0,FATAL,dir)) {
191 if (charset.len >= 2 && charset.s[charset.len - 2] == ':') {
192 if (charset.s[charset.len - 1] == 'B' ||
193 charset.s[charset.len - 1] == 'Q') {
194 flagcd = charset.s[charset.len - 1];
195 charset.s[charset.len - 2] = '\0';
199 if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem();
200 if (!stralloc_0(&charset)) die_nomem();
201 qmail_puts(&qq,"\nMIME-Version: 1.0\n");
202 qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary=");
203 qmail_put(&qq,boundary,COOKIE);
204 qmail_puts(&qq,"\n\n--");
205 qmail_put(&qq,boundary,COOKIE);
206 qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
207 qmail_puts(&qq,charset.s);
210 qmail_puts(&qq,"\n\n");
212 copy(&qq,"text/top",flagcd,FATAL);
213 copy(&qq,"text/mod-timeout",flagcd,FATAL);
215 encodeB("",0,&line,2,FATAL);
216 qmail_put(&qq,line.s,line.len);
220 qmail_puts(&qq,"\n--");
221 qmail_put(&qq,boundary,COOKIE);
222 qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n");
225 if (seek_begin(fd) == -1)
226 strerr_die4sys(111,FATAL,ERR_SEEK,d,": ");
228 substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
229 if (substdio_copy(&ssqq,&sstext) != 0) die_read();
233 qmail_puts(&qq,"\n--");
234 qmail_put(&qq,boundary,COOKIE);
235 qmail_puts(&qq,"--\n");
238 if (!stralloc_copy(&line,&outlocal)) die_nomem();
239 if (!stralloc_cats(&line,"-return-@")) die_nomem();
240 if (!stralloc_cat(&line,&outhost)) die_nomem();
241 if (!stralloc_0(&line)) die_nomem();
242 qmail_from(&qq,line.s); /* sender */
245 if (*(err = qmail_close(&qq)) != '\0')
246 strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE, err + 1);
248 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
249 strerr_warn2("ezmlm-clean: info: qp ",strnum,0);
252 void dodir(dirname,reply)
253 char *dirname; int reply;
254 /* parses file names in directory 'dirname'. Files that are not owner */
255 /* writable (w) are ignored. If the files are older (by name!) than */
256 /* now-delay, action is taken: */
257 /* If the owner x bit is not set, the file is erased. */
258 /* If it is set and reply is not set, the file is erased. If both are */
259 /* set, a notice about the timeout is sent to the poster. If this */
260 /* fails due to a message-related error (format, etc) the file is */
261 /* erased even though no notice is sent. For temporary errors (like */
262 /* out-of-memory) the message is left intact for the next run. If the */
263 /* notice is sent successfully, the file is erased. All this is to */
264 /* do the best possible without risking a rerun of the .qmail file, */
265 /* which could result in a redelivery of the action request and a */
266 /* second (incorrect) reply to the moderator's request. */
268 /* NOTE: ALL non-hidden files in this dir are processed and merci- */
269 /* lessly deleted. No checks for proper file name. E.g. 'HELLO' */
270 /* => time 0 => will be deleted on the next ezmlm-clean run. */
274 unsigned long modtime;
277 moddir = opendir(dirname);
279 strerr_die6sys(0,FATAL,ERR_OPEN,dir,"/",dirname,": ");
280 while ((d = readdir(moddir))) {
281 if (d->d_name[0] == '.') continue;
282 scan_ulong(d->d_name,&modtime);
283 if (modtime < older) {
284 if (!stralloc_copys(&fnmsg,dirname)) die_nomem();
285 if (!stralloc_cats(&fnmsg,d->d_name)) die_nomem();
286 if (!stralloc_0(&fnmsg)) die_nomem();
287 if((stat(fnmsg.s,&st) != -1) && (st.st_mode & 0200)) {
288 if(reply && (st.st_mode & 0100)) {
289 /* unlink unless there was a TEMPORARY */
290 /* not message-related error notifying */
291 /* poster and msg x bit set. Leave r/o*/
292 /* messages alone. Non-x bit msg are */
293 /* trash. Just unlink, don't notify */
316 while ((opt = getopt(argc,argv,"mMrRvV")) != opteof)
318 case 'm': flagmime = 1; break;
319 case 'M': flagmime = 0; break;
320 case 'r': flagreturn = 1; break;
321 case 'R': flagreturn = 0; break;
323 case 'V': strerr_die2x(0,"ezmlm-clean version: ", EZIDX_VERSION);
330 if (!dir) die_usage();
332 if (chdir(dir) == -1)
333 strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
335 getconf_line(&modtime,"modtime",0,FATAL,dir);
336 if (!stralloc_0(&modtime)) die_nomem();
337 scan_ulong(modtime.s,&delay);
338 if (!delay) delay = DELAY_DEFAULT;
339 else if (delay < DELAY_MIN) delay = DELAY_MIN;
340 else if (delay > DELAY_MAX) delay = DELAY_MAX;
341 older = (unsigned long) when - 3600L * delay; /* delay is in hours */
343 fdlock = open_append("mod/lock");
345 strerr_die4sys(0,FATAL,ERR_OPEN,dir,"/mod/lock: ");
346 if (lock_ex(fdlock) == -1)
347 strerr_die4sys(0,FATAL,ERR_OBTAIN,dir,"/mod/lock: ");
350 dodir("mod/pending/",flagreturn);
351 dodir("mod/accepted/",0);
352 dodir("mod/rejected/",0);