w3mail.c (7262B)
1 /* 2 3 w3mail -- program for sending web page by email 4 5 Copyright (C) 2010 Tomas Hlavaty 6 7 This program is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see 19 <http://www.gnu.org/licenses/>. 20 21 */ 22 23 #include <string.h> 24 #include <unistd.h> 25 #include <time.h> 26 27 #include "utils.h" 28 29 static void cb_md5sum(void *udata, FILE* in) { 30 char *sum = udata; 31 sum = fgets(sum, 33, in); 32 } 33 34 static void md5sum(char *fname, char *sum) { 35 in(sum, cb_md5sum, "md5sum '%s'", fname); 36 } 37 38 static void cb_echo(void *udata, FILE* in) { 39 FILE *out = udata; 40 char buf[BLEN]; 41 int n; 42 while(0 < (n = fread(buf, sizeof(char), BLEN, in))) 43 fwrite(buf, sizeof(char), n, out); 44 } 45 46 static void now(char *buf) { 47 time_t t = time(NULL); 48 struct tm *tm = localtime(&t); 49 strftime(buf, BLEN, "%a, %d %b %Y %T %z", tm); 50 } 51 52 static int head(char *sub, char *str) { 53 return 0 == strncmp(sub, str, strlen(sub)); 54 } 55 56 static int equal(char *str1, char *str2) { 57 return 0 == strcmp(str1, str2); 58 } 59 60 static int sh_cp(char *fin, char *fout) { 61 return systemf("cp %s %s", fin, fout); 62 } 63 64 static int sh_tidy(char *fin, char *fout) { 65 // TODO tidy fragile, no html5, tidy generator, not parser! replace 66 int x = systemf("tidy -q -n -c -asxml -o %s -f /dev/null %s", fout, fin); 67 return x == 0 || x == 1 ? 0 : x; 68 } 69 70 static int fix_xml(char *fin, char *fout) { 71 return systemf("~/picolisp/p @lib/xml.l -'xml (_xml)' <%s >%s", fin, fout); 72 } 73 74 static void fix_xpath(char *xpath) { 75 while(*xpath) { 76 if('"' == *xpath) 77 *xpath = '\''; 78 xpath++; 79 } 80 } 81 82 static int sh_xpath(char *fin, char *fout, char *xpath) { 83 char buf[BLEN]; 84 strcpy(buf, xpath); 85 fix_xpath(buf); 86 return systemf("xmlstarlet sel -t -c \"%s\" <%s >%s", buf, fin, fout); 87 } 88 89 static int fix_namespace(char *fin, char *fout) { 90 // TODO pyx + p2x fragile, use something better 91 return systemf("cat %s | xmlstarlet pyx | grep -v Axmlns | xmlstarlet p2x >%s", fin, fout); 92 } 93 94 static int xpath_filter(char *fin, char *fout, char *xpath) { 95 if(sh_tidy(fin, fout)) { 96 if(fix_xml(fin, fout)) 97 return 1; 98 else { 99 sh_cp(fout, fin); 100 if(sh_tidy(fin, fout)) 101 return 1; 102 } 103 } 104 sh_cp(fout, fin); 105 if(fix_namespace(fin, fout)) 106 return 1; 107 return sh_xpath(fin, fout, xpath); 108 } 109 110 static int custom_filter(char *fin, char *fout, char *cmd) { 111 return systemf("~/.w3mail/filter/%s <%s >%s", cmd, fin, fout); 112 } 113 114 static int default_filter(char *fin, char *fout) { 115 return systemf("~/.w3mail/filter/default <%s >%s", fin, fout); 116 } 117 118 struct tidy { 119 char *fin; 120 char *fout; 121 char *url; 122 }; 123 124 static void cb_tidy(void *udata, FILE* in) { 125 struct tidy *x = udata; 126 while(!feof(in)) { 127 char line[BLEN]; 128 if(!fgets(line, BLEN, in)) break; 129 rtrim(line); 130 if('#' == line[0]) continue; 131 char name[BLEN], url[BLEN], type[BLEN]; 132 int n; 133 sscanf(line, "%s %s %s %n", name, url, type, &n); 134 if(url[0] && head(url, x->url)) { 135 if(equal("xpath", type)) { 136 if(!xpath_filter(x->fin, x->fout, &line[n])) 137 return; 138 } 139 else 140 if(equal("filter", type)) 141 if(!custom_filter(x->fin, x->fout, &line[n])) 142 return; 143 } 144 } 145 if(default_filter(x->fin, x->fout)) 146 sh_cp(x->fin, x->fout); 147 } 148 149 static void tidy(char *fin, char *fout, char *url) { 150 struct tidy x = {fin, fout, url}; 151 in(&x, cb_tidy, "cat ~/.w3mail/tidy"); 152 } 153 154 struct xtidy { 155 FILE *out; 156 char *fname; 157 char *url; 158 }; 159 160 static void cb_xtidy(void *udata, char *fname) { 161 struct xtidy *x = udata; 162 tidy(x->fname, fname, x->url); 163 in(x->out, cb_echo, "base64 %s", fname); 164 //in(x->out, cb_echo, "w3m -dump -T text/html %s", fname); 165 } 166 167 #define pr(...) {fprintf(out, __VA_ARGS__); fprintf(out, "\n");} 168 169 struct sendmail { 170 char *from; 171 char *to; 172 char *id; 173 char *subj; 174 char *url; 175 char *mime; 176 int html; 177 char *fname; 178 }; 179 180 static void cb_sendmail(void *udata, FILE* out) { 181 struct sendmail *x = udata; 182 pr("From: %s", x->from); 183 pr("To: %s", x->to); 184 pr("Message-ID: %s", x->id); 185 pr("Subject: %s", x->subj); 186 pr("User-Agent: w3mail"); 187 pr("MIME-Version: 1.0"); 188 //pr("Content-Type: %s", x->html ? "text/plain; charset=utf8" : x->mime); 189 pr("Content-Type: %s", x->mime); 190 char buf[BLEN]; 191 now(buf); 192 pr("Date: %s", buf); // TODO from feed? 193 //pr("Content-Transfer-Encoding: %s", x->html ? "8bit" : "base64"); 194 pr("Content-Transfer-Encoding: %s", "base64"); 195 pr("%s", ""); 196 if(x->html) { 197 struct xtidy y = {out, x->fname, x->url}; 198 tmpf(&y, cb_xtidy, "/tmp/w3mail-XXXXXX"); 199 } 200 else in(out, cb_echo, "base64 %s", x->fname); 201 } 202 203 static void cb_read_first_line(void *udata, FILE* in) { 204 rtrim(fgets(udata, BLEN, in)); 205 } 206 207 static int is_html(char *elt) { 208 return equal("html", elt) || equal("xhtml", elt); 209 } 210 211 struct w3mail { 212 char *cmd; 213 char *from; 214 char *to; 215 char *host; 216 char *url; 217 }; 218 219 static void cb_w3mail(void *udata, char *fname) { 220 struct w3mail *x = udata; 221 char sum[BLEN], id[BLEN], subj[BLEN], mime[BLEN]; 222 int html = 0; 223 wget(x->url, fname); 224 in(mime, cb_read_first_line, "file -bi %s", fname); 225 if(head("application/xml;", mime)) { 226 char elt[BLEN]; 227 in(elt, cb_read_first_line, "xmlstarlet el %s", fname); 228 if(equal("rss", elt)) { 229 // TODO handle rss feed 230 } 231 if(equal("atom", elt)) { 232 // TODO handle atom feed 233 } 234 else if(is_html(elt)) { 235 // TODO fix mime application/xml to html 236 html = 1; 237 } 238 else html = is_html(elt); 239 } 240 else html = head("text/html;", mime); 241 md5sum(fname, sum); 242 snprintf(id, BLEN, "<%s@%s>", sum, x->host); 243 snprintf(subj, BLEN, "[w3mail] %s", x->url); // TODO from feed? 244 struct sendmail y = {x->from, x->to, id, subj, x->url, mime, html, fname}; 245 out(&y, cb_sendmail, "%s", x->cmd); 246 } 247 248 static void cb_config(void *udata, FILE* in) { 249 struct w3mail *x = udata; 250 rtrim(fgets(x->cmd, BLEN, in)); 251 rtrim(fgets(x->from, BLEN, in)); 252 rtrim(fgets(x->to, BLEN, in)); 253 rtrim(fgets(x->host, BLEN, in)); 254 } 255 256 static void run(struct w3mail *x) { 257 tmpf(x, cb_w3mail, "/tmp/w3mail-XXXXXX"); 258 } 259 260 int main(int argc, char *argv[]) { 261 if(argc == 1) { 262 char cmd[BLEN], from[BLEN], to[BLEN], host[BLEN], url[BLEN]; 263 struct w3mail x = {cmd, from, to, host, url}; 264 in(&x, cb_config, "cat ~/.w3mail/config"); 265 while(!feof(stdin)) { 266 char *line = fgets(url, BLEN, stdin); 267 if(!line) break; 268 rtrim(line); 269 run(&x); 270 } 271 } 272 else if(argc == 2) { 273 char cmd[BLEN], from[BLEN], to[BLEN], host[BLEN]; 274 struct w3mail x = {cmd, from, to, host, argv[1]}; 275 in(&x, cb_config, "cat ~/.w3mail/config"); 276 run(&x); 277 } 278 else if(argc == 6) { 279 struct w3mail x = {argv[1], argv[2], argv[3], argv[4], argv[5]}; 280 run(&x); 281 } 282 else quit(1, "usage: %s [[cmd from to host] url]\n", argv[0]); 283 return 0; 284 }