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);