| 1 | #include "stralloc.h" |
| 2 | #include "subfd.h" |
| 3 | #include "strerr.h" |
| 4 | #include "error.h" |
| 5 | #include "qmail.h" |
| 6 | #include "env.h" |
| 7 | #include "lock.h" |
| 8 | #include "sig.h" |
| 9 | #include "open.h" |
| 10 | #include "getln.h" |
| 11 | #include "case.h" |
| 12 | #include "scan.h" |
| 13 | #include "str.h" |
| 14 | #include "fmt.h" |
| 15 | #include "readwrite.h" |
| 16 | #include "exit.h" |
| 17 | #include "substdio.h" |
| 18 | #include "getconf.h" |
| 19 | #include "constmap.h" |
| 20 | |
| 21 | #define FATAL "ezmlm-send: fatal: " |
| 22 | |
| 23 | void die_usage() |
| 24 | { |
| 25 | strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir"); |
| 26 | } |
| 27 | void die_nomem() |
| 28 | { |
| 29 | strerr_die2x(111,FATAL,"out of memory"); |
| 30 | } |
| 31 | |
| 32 | char strnum[FMT_ULONG]; |
| 33 | |
| 34 | stralloc fnadir = {0}; |
| 35 | stralloc fnaf = {0}; |
| 36 | stralloc fnsub = {0}; |
| 37 | stralloc line = {0}; |
| 38 | |
| 39 | int flagarchived; |
| 40 | int fdarchive; |
| 41 | substdio ssarchive; |
| 42 | char archivebuf[1024]; |
| 43 | |
| 44 | int flagsublist; |
| 45 | stralloc sublist = {0}; |
| 46 | stralloc mailinglist = {0}; |
| 47 | stralloc outlocal = {0}; |
| 48 | stralloc outhost = {0}; |
| 49 | stralloc headerremove = {0}; |
| 50 | struct constmap headerremovemap; |
| 51 | stralloc headeradd = {0}; |
| 52 | |
| 53 | struct qmail qq; |
| 54 | substdio ssin; |
| 55 | char inbuf[1024]; |
| 56 | substdio ssout; |
| 57 | char outbuf[1]; |
| 58 | |
| 59 | int mywrite(fd,buf,len) |
| 60 | int fd; |
| 61 | char *buf; |
| 62 | unsigned int len; |
| 63 | { |
| 64 | qmail_put(&qq,buf,len); |
| 65 | return len; |
| 66 | } |
| 67 | |
| 68 | void die_archive() |
| 69 | { |
| 70 | strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": "); |
| 71 | } |
| 72 | void die_numnew() |
| 73 | { |
| 74 | strerr_die2sys(111,FATAL,"unable to create numnew: "); |
| 75 | } |
| 76 | |
| 77 | void put(buf,len) char *buf; int len; |
| 78 | { |
| 79 | qmail_put(&qq,buf,len); |
| 80 | if (flagarchived) |
| 81 | if (substdio_put(&ssarchive,buf,len) == -1) die_archive(); |
| 82 | } |
| 83 | |
| 84 | void puts(buf) char *buf; |
| 85 | { |
| 86 | qmail_puts(&qq,buf); |
| 87 | if (flagarchived) |
| 88 | if (substdio_puts(&ssarchive,buf) == -1) die_archive(); |
| 89 | } |
| 90 | |
| 91 | int sublistmatch(sender) |
| 92 | char *sender; |
| 93 | { |
| 94 | int i; |
| 95 | int j; |
| 96 | |
| 97 | j = str_len(sender); |
| 98 | if (j < sublist.len) return 0; |
| 99 | |
| 100 | i = byte_rchr(sublist.s,sublist.len,'@'); |
| 101 | if (i == sublist.len) return 1; |
| 102 | |
| 103 | if (byte_diff(sublist.s,i,sender)) return 0; |
| 104 | if (case_diffb(sublist.s + i,sublist.len - i,sender + j - (sublist.len - i))) |
| 105 | return 0; |
| 106 | |
| 107 | return 1; |
| 108 | } |
| 109 | |
| 110 | substdio ssnumnew; |
| 111 | char numnewbuf[16]; |
| 112 | unsigned long msgnum; |
| 113 | stralloc num = {0}; |
| 114 | |
| 115 | char buf0[256]; |
| 116 | substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0)); |
| 117 | |
| 118 | void numwrite() |
| 119 | { |
| 120 | int fd; |
| 121 | |
| 122 | fd = open_trunc("numnew"); |
| 123 | if (fd == -1) die_numnew(); |
| 124 | substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf)); |
| 125 | if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1) |
| 126 | die_numnew(); |
| 127 | if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew(); |
| 128 | if (substdio_flush(&ssnumnew) == -1) die_numnew(); |
| 129 | if (fsync(fd) == -1) die_numnew(); |
| 130 | if (close(fd) == -1) die_numnew(); /* NFS stupidity */ |
| 131 | if (rename("numnew","num") == -1) |
| 132 | strerr_die2sys(111,FATAL,"unable to move numnew to num: "); |
| 133 | } |
| 134 | |
| 135 | stralloc mydtline = {0}; |
| 136 | |
| 137 | void main(argc,argv) |
| 138 | int argc; |
| 139 | char **argv; |
| 140 | { |
| 141 | int fd; |
| 142 | char *dir; |
| 143 | int fdlock; |
| 144 | char *sender; |
| 145 | int flagmlwasthere; |
| 146 | int match; |
| 147 | int i; |
| 148 | char ch; |
| 149 | int flaginheader; |
| 150 | int flagbadfield; |
| 151 | |
| 152 | umask(022); |
| 153 | sig_pipeignore(); |
| 154 | |
| 155 | dir = argv[1]; |
| 156 | if (!dir) die_usage(); |
| 157 | |
| 158 | sender = env_get("SENDER"); |
| 159 | |
| 160 | if (chdir(dir) == -1) |
| 161 | strerr_die4sys(111,FATAL,"unable to switch to ",dir,": "); |
| 162 | |
| 163 | fdlock = open_append("lock"); |
| 164 | if (fdlock == -1) |
| 165 | strerr_die4sys(111,FATAL,"unable to open ",dir,"/lock: "); |
| 166 | if (lock_ex(fdlock) == -1) |
| 167 | strerr_die4sys(111,FATAL,"unable to obtain ",dir,"/lock: "); |
| 168 | |
| 169 | if (qmail_open(&qq) == -1) |
| 170 | strerr_die2sys(111,FATAL,"unable to run qmail-queue: "); |
| 171 | |
| 172 | flagarchived = getconf_line(&line,"archived",0,FATAL,dir); |
| 173 | |
| 174 | getconf_line(&num,"num",1,FATAL,dir); |
| 175 | if (!stralloc_0(&num)) die_nomem(); |
| 176 | scan_ulong(num.s,&msgnum); |
| 177 | ++msgnum; |
| 178 | |
| 179 | getconf_line(&outhost,"outhost",1,FATAL,dir); |
| 180 | getconf_line(&outlocal,"outlocal",1,FATAL,dir); |
| 181 | getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); |
| 182 | flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir); |
| 183 | |
| 184 | getconf(&headerremove,"headerremove",1,FATAL,dir); |
| 185 | constmap_init(&headerremovemap,headerremove.s,headerremove.len,0); |
| 186 | |
| 187 | getconf(&headeradd,"headeradd",1,FATAL,dir); |
| 188 | for (i = 0;i < headeradd.len;++i) |
| 189 | if (!headeradd.s[i]) |
| 190 | headeradd.s[i] = '\n'; |
| 191 | |
| 192 | if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem(); |
| 193 | if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); |
| 194 | if (!stralloc_cats(&mydtline,"@")) die_nomem(); |
| 195 | if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); |
| 196 | if (!stralloc_cats(&mydtline,"\n")) die_nomem(); |
| 197 | |
| 198 | if (sender) { |
| 199 | if (!*sender) |
| 200 | strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)"); |
| 201 | if (str_equal(sender,"#@[]")) |
| 202 | strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)"); |
| 203 | if (flagsublist) |
| 204 | if (!sublistmatch(sender)) |
| 205 | strerr_die2x(100,FATAL,"this message is not from my parent list (#5.7.2)"); |
| 206 | } |
| 207 | |
| 208 | if (flagarchived) { |
| 209 | if (!stralloc_copys(&fnadir,"archive/")) die_nomem(); |
| 210 | if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100))) die_nomem(); |
| 211 | if (!stralloc_copy(&fnaf,&fnadir)) die_nomem(); |
| 212 | if (!stralloc_cats(&fnaf,"/")) die_nomem(); |
| 213 | if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem(); |
| 214 | if (!stralloc_0(&fnadir)) die_nomem(); |
| 215 | if (!stralloc_0(&fnaf)) die_nomem(); |
| 216 | |
| 217 | if (mkdir(fnadir.s,0755) == -1) |
| 218 | if (errno != error_exist) |
| 219 | strerr_die4sys(111,FATAL,"unable to create ",fnadir.s,": "); |
| 220 | fdarchive = open_trunc(fnaf.s); |
| 221 | if (fdarchive == -1) |
| 222 | strerr_die4sys(111,FATAL,"unable to write ",fnaf.s,": "); |
| 223 | |
| 224 | substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf)); |
| 225 | } |
| 226 | |
| 227 | if (!flagsublist) { |
| 228 | puts("Mailing-List: "); |
| 229 | put(mailinglist.s,mailinglist.len); |
| 230 | puts("\n"); |
| 231 | } |
| 232 | put(headeradd.s,headeradd.len); |
| 233 | put(mydtline.s,mydtline.len); |
| 234 | |
| 235 | flagmlwasthere = 0; |
| 236 | flaginheader = 1; |
| 237 | flagbadfield = 0; |
| 238 | |
| 239 | for (;;) { |
| 240 | if (getln(&ss0,&line,&match,'\n') == -1) |
| 241 | strerr_die2sys(111,FATAL,"unable to read input: "); |
| 242 | |
| 243 | if (flaginheader && match) { |
| 244 | if (line.len == 1) |
| 245 | flaginheader = 0; |
| 246 | if ((line.s[0] != ' ') && (line.s[0] != '\t')) { |
| 247 | flagbadfield = 0; |
| 248 | if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':'))) |
| 249 | flagbadfield = 1; |
| 250 | if (case_startb(line.s,line.len,"mailing-list:")) |
| 251 | flagmlwasthere = 1; |
| 252 | if (line.len == mydtline.len) |
| 253 | if (!byte_diff(line.s,line.len,mydtline.s)) |
| 254 | strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)"); |
| 255 | } |
| 256 | } |
| 257 | |
| 258 | if (!(flaginheader && flagbadfield)) |
| 259 | put(line.s,line.len); |
| 260 | |
| 261 | if (!match) |
| 262 | break; |
| 263 | } |
| 264 | |
| 265 | if (flagsublist) |
| 266 | if (!flagmlwasthere) |
| 267 | strerr_die2x(100,FATAL,"sublist messages must have Mailing-List (#5.7.2)"); |
| 268 | if (!flagsublist) |
| 269 | if (flagmlwasthere) |
| 270 | strerr_die2x(100,FATAL,"message already has Mailing-List (#5.7.2)"); |
| 271 | |
| 272 | if (flagarchived) { |
| 273 | if (substdio_flush(&ssarchive) == -1) die_archive(); |
| 274 | if (fsync(fdarchive) == -1) die_archive(); |
| 275 | if (fchmod(fdarchive,0744) == -1) die_archive(); |
| 276 | if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */ |
| 277 | } |
| 278 | |
| 279 | numwrite(); |
| 280 | |
| 281 | if (!stralloc_copy(&line,&outlocal)) die_nomem(); |
| 282 | if (!stralloc_cats(&line,"-return-")) die_nomem(); |
| 283 | if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem(); |
| 284 | if (!stralloc_cats(&line,"-@")) die_nomem(); |
| 285 | if (!stralloc_cat(&line,&outhost)) die_nomem(); |
| 286 | if (!stralloc_cats(&line,"-@[]")) die_nomem(); |
| 287 | if (!stralloc_0(&line)) die_nomem(); |
| 288 | |
| 289 | qmail_from(&qq,line.s); |
| 290 | |
| 291 | for (i = 0;i < 53;++i) { |
| 292 | ch = 64 + i; |
| 293 | if (!stralloc_copys(&fnsub,"subscribers/")) die_nomem(); |
| 294 | if (!stralloc_catb(&fnsub,&ch,1)) strerr_die2x(111,FATAL,"out of memory"); |
| 295 | if (!stralloc_0(&fnsub)) strerr_die2x(111,FATAL,"out of memory"); |
| 296 | fd = open_read(fnsub.s); |
| 297 | if (fd == -1) { |
| 298 | if (errno != error_noent) |
| 299 | strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": "); |
| 300 | } |
| 301 | else { |
| 302 | substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); |
| 303 | substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf)); |
| 304 | if (substdio_copy(&ssout,&ssin) != 0) |
| 305 | strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": "); |
| 306 | close(fd); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | switch(qmail_close(&qq)) { |
| 311 | case 0: |
| 312 | strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; |
| 313 | strerr_die2x(0,"ezmlm-send: info: qp ",strnum); |
| 314 | default: |
| 315 | --msgnum; |
| 316 | numwrite(); |
| 317 | strerr_die2x(111,FATAL,"temporary qmail-queue error"); |
| 318 | } |
| 319 | } |