w3mail

program to send a web page by email
git clone https://logand.com/git/w3mail.git/
Log | Files | Refs | README | LICENSE

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 }