1implement Sendmail; 2 3include "sys.m"; 4 sys: Sys; 5include "draw.m"; 6include "bufio.m"; 7include "daytime.m"; 8include "smtp.m"; 9include "env.m"; 10 11sprint, fprint : import sys; 12 13DEBUG : con 0; 14STRMAX : con 512; 15 16Sendmail : module 17{ 18 PATH : con "/dis/sendmail.dis"; 19 20 # argv is list of persons to send mail to (or nil if To: lines present in message) 21 # mail is read from standard input 22 # scans mail for headers (From: , To: , Cc: , Subject: , Re: ) where case is not sensitive 23 init: fn(ctxt : ref Draw->Context, argv : list of string); 24}; 25 26init(nil : ref Draw->Context, args : list of string) { 27 from : string; 28 tos, cc : list of string = nil; 29 30 sys = load Sys Sys->PATH; 31 smtp := load Smtp Smtp->PATH; 32 if (smtp == nil) 33 error(sprint("cannot load %s", Smtp->PATH), 1); 34 daytime := load Daytime Daytime->PATH; 35 if (daytime == nil) 36 error(sprint("cannot load %s", Daytime->PATH), 1); 37 msgl := readin(); 38 for (ml := msgl; ml != nil; ml = tl ml) { 39 msg := hd ml; 40 lenm := len msg; 41 sol := 1; 42 for (i := 0; i < lenm; i++) { 43 if (sol) { 44 for (j := i; j < lenm; j++) 45 if (msg[j] == '\n') 46 break; 47 s := msg[i:j]; 48 if (from == nil) { 49 from = match(s, "from"); 50 if (from != nil) 51 from = extract(from); 52 } 53 if (tos == nil) 54 tos = lmatch(s, "to"); 55 if (cc == nil) 56 cc = lmatch(s, "cc"); 57 sol = 0; 58 } 59 if (msg[i] == '\n') 60 sol = 1; 61 } 62 } 63 if (tos != nil && tl args != nil) 64 error("recipients specified on To: line and as args - aborted", 1); 65 if (from == nil) 66 from = readfile("/dev/user"); 67 from = adddom(from); 68 if (tos == nil) 69 tos = tl args; 70 (ok, err) := smtp->open(nil); 71 if (ok < 0) { 72 smtp->close(); 73 error(sprint("smtp open failed: %s", err), 1); 74 } 75 dump(from, tos, cc, msgl); 76 msgl = "From " + from + "\t" + daytime->time() + "\n" :: msgl; 77 # msgl = "From: " + from + "\n" + "Date: " + daytime->time() + "\n" :: msgl; 78 (ok, err) = smtp->sendmail(from, tos, cc, msgl); 79 if (ok < 0) { 80 smtp->close(); 81 error(sprint("send failed : %s", err), 0); 82 } 83 smtp->close(); 84} 85 86readin() : list of string 87{ 88 m : string; 89 ls : list of string; 90 nc : int; 91 92 bufio := load Bufio Bufio->PATH; 93 Iobuf : import bufio; 94 b := bufio->fopen(sys->fildes(0), Bufio->OREAD); 95 ls = nil; 96 m = nil; 97 nc = 0; 98 while ((s := b.gets('\n')) != nil) { 99 if (nc > STRMAX) { 100 ls = m :: ls; 101 m = nil; 102 nc = 0; 103 } 104 m += s; 105 nc += len s; 106 } 107 b.close(); 108 if (m != nil) 109 ls = m :: ls; 110 return rev(ls); 111} 112 113match(s: string, pat : string) : string 114{ 115 ls := len s; 116 lp := len pat; 117 if (ls < lp) 118 return nil; 119 for (i := 0; i < lp; i++) { 120 c := s[i]; 121 if (c >= 'A' && c <= 'Z') 122 c += 'a'-'A'; 123 if (c != pat[i]) 124 return nil; 125 } 126 if (i < len s && s[i] == ':') 127 i++; 128 else if (i < len s - 1 && s[i] == ' ' && s[i+1] == ':') 129 i += 2; 130 else 131 return nil; 132 while (i < len s && (s[i] == ' ' || s[i] == '\t')) 133 i++; 134 j := ls-1; 135 while (j >= 0 && (s[j] == ' ' || s[j] == '\t' || s[j] == '\n')) 136 j--; 137 return s[i:j+1]; 138} 139 140lmatch(s : string, pat : string) : list of string 141{ 142 r := match(s, pat); 143 if (r != nil) { 144 (ok, lr) := sys->tokenize(r, " ,\t"); 145 return lr; 146 } 147 return nil; 148} 149 150extract(s : string) : string 151{ 152 ls := len s; 153 for(i := 0; i < ls; i++) { 154 if(s[i] == '<') { 155 for(j := i+1; j < ls; j++) 156 if(s[j] == '>') 157 break; 158 return s[i+1:j]; 159 } 160 } 161 return s; 162} 163 164adddom(s : string) : string 165{ 166 if (s == nil) 167 return nil; 168 for (i := 0; i < len s; i++) 169 if (s[i] == '@') 170 return s; 171 # better to get it from environment if possible 172 env := load Env Env->PATH; 173 if (env != nil && (dom := env->getenv("DOMAIN")) != nil) { 174 ldom := len dom; 175 if (dom[ldom - 1] == '\n') 176 dom = dom[0:ldom - 1]; 177 return s + "@" + dom; 178 } 179 d := readfile("/usr/" + s + "/mail/domain"); 180 if (d != nil) { 181 ld := len d; 182 if (d[ld - 1] == '\n') 183 d = d[0:ld - 1]; 184 return s + "@" + d; 185 } 186 return s; 187} 188 189readfile(f : string) : string 190{ 191 fd := sys->open(f, sys->OREAD); 192 if(fd == nil) 193 return nil; 194 buf := array[128] of byte; 195 n := sys->read(fd, buf, len buf); 196 if(n < 0) 197 return nil; 198 return string buf[0:n]; 199} 200 201rev(l1 : list of string) : list of string 202{ 203 l2 : list of string = nil; 204 205 for ( ; l1 != nil; l1 = tl l1) 206 l2 = hd l1 :: l2; 207 return l2; 208} 209 210lprint(fd : ref Sys->FD, ls : list of string) 211{ 212 for ( ; ls != nil; ls = tl ls) 213 fprint(fd, "%s ", hd ls); 214 fprint(fd, "\n"); 215} 216 217cfd : ref Sys->FD; 218 219opencons() 220{ 221 if (cfd == nil) 222 cfd = sys->open("/dev/cons", Sys->OWRITE); 223} 224 225dump(from : string, tos : list of string, cc : list of string, msgl : list of string) 226{ 227 if (DEBUG) { 228 opencons(); 229 fprint(cfd, "from\n"); 230 fprint(cfd, "%s\n", from); 231 fprint(cfd, "to\n"); 232 lprint(cfd, tos); 233 fprint(cfd, "cc\n"); 234 lprint(cfd, cc); 235 fprint(cfd, "message\n"); 236 for ( ; msgl != nil; msgl = tl msgl) { 237 fprint(cfd, "%s", hd msgl); 238 fprint(cfd, "xxxx\n"); 239 } 240 } 241} 242 243error(s : string, ex : int) 244{ 245 if (DEBUG) { 246 opencons(); 247 fprint(cfd, "sendmail: %s\n", s); 248 } 249 fprint(sys->fildes(2), "sendmail: %s\n", s); 250 if (ex) 251 exit; 252} 253