Blob
1 #include <u.h>2 #include <libc.h>3 #include <regexp.h>4 #include <libsec.h>5 #include <libString.h>6 #include <bio.h>7 #include "dat.h"9 int debug;11 enum12 {13 Tregexp= (1<<0), /* ~ */14 Texact= (1<<1), /* = */15 };17 typedef struct Pattern Pattern;18 struct Pattern19 {20 Pattern *next;21 int type;22 char *arg;23 int bang;24 };26 String *patternpath;27 Pattern *patterns;28 String *mbox;30 static void31 usage(void)32 {33 fprint(2, "usage: %s 'check|add' patternfile addr [addr*]\n", argv0);34 exits("usage");35 }37 /*38 * convert string to lower case39 */40 static void41 mklower(char *p)42 {43 int c;45 for(; *p; p++){46 c = *p;47 if(c <= 'Z' && c >= 'A')48 *p = c - 'A' + 'a';49 }50 }52 /*53 * simplify an address, reduce to a domain54 */55 static String*56 simplify(char *addr)57 {58 int dots;59 char *p, *at;60 String *s;62 mklower(addr);63 at = strchr(addr, '@');64 if(at == nil){65 /* local address, make it an exact match */66 s = s_copy("=");67 s_append(s, addr);68 return s;69 }71 /* copy up to the '@' sign */72 at++;73 s = s_copy("~");74 for(p = addr; p < at; p++){75 if(strchr(".*+?(|)\\[]^$", *p))76 s_putc(s, '\\');77 s_putc(s, *p);78 }80 /* just any address matching the two most significant domain elements */81 s_append(s, "(.*\\.)?");82 p = addr+strlen(addr);83 dots = 0;84 for(; p > at; p--){85 if(*p != '.')86 continue;87 if(dots++ > 0){88 p++;89 break;90 }91 }92 for(; *p; p++){93 if(strchr(".*+?(|)\\[]^$", *p) != 0)94 s_putc(s, '\\');95 s_putc(s, *p);96 }97 s_terminate(s);99 return s;100 }102 /*103 * link patterns in order104 */105 static int106 newpattern(int type, char *arg, int bang)107 {108 Pattern *p;109 static Pattern *last;111 mklower(arg);113 p = mallocz(sizeof *p, 1);114 if(p == nil)115 return -1;116 if(type == Tregexp){117 p->arg = malloc(strlen(arg)+3);118 if(p->arg == nil){119 free(p);120 return -1;121 }122 p->arg[0] = 0;123 strcat(p->arg, "^");124 strcat(p->arg, arg);125 strcat(p->arg, "$");126 } else {127 p->arg = strdup(arg);128 if(p->arg == nil){129 free(p);130 return -1;131 }132 }133 p->type = type;134 p->bang = bang;135 if(last == nil)136 patterns = p;137 else138 last->next = p;139 last = p;141 return 0;142 }144 /*145 * patterns are either146 * ~ regular expression147 * = exact match string148 *149 * all comparisons are case insensitive150 */151 static int152 readpatterns(char *path)153 {154 Biobuf *b;155 char *p;156 char *token[2];157 int n;158 int bang;160 b = Bopen(path, OREAD);161 if(b == nil)162 return -1;163 while((p = Brdline(b, '\n')) != nil){164 p[Blinelen(b)-1] = 0;165 n = tokenize(p, token, 2);166 if(n == 0)167 continue;169 mklower(token[0]);170 p = token[0];171 if(*p == '!'){172 p++;173 bang = 1;174 } else175 bang = 0;177 if(*p == '='){178 if(newpattern(Texact, p+1, bang) < 0)179 return -1;180 } else if(*p == '~'){181 if(newpattern(Tregexp, p+1, bang) < 0)182 return -1;183 } else if(strcmp(token[0], "#include") == 0 && n == 2)184 readpatterns(token[1]);185 }186 Bterm(b);187 return 0;188 }190 /* fuck, shit, bugger, damn */191 void regerror(char *err)192 {193 USED(err);194 }196 /*197 * check lower case version of address agains patterns198 */199 static Pattern*200 checkaddr(char *arg)201 {202 Pattern *p;203 Reprog *rp;204 String *s;206 s = s_copy(arg);207 mklower(s_to_c(s));209 for(p = patterns; p != nil; p = p->next)210 switch(p->type){211 case Texact:212 if(strcmp(p->arg, s_to_c(s)) == 0){213 free(s);214 return p;215 }216 break;217 case Tregexp:218 rp = regcomp(p->arg);219 if(rp == nil)220 continue;221 if(regexec(rp, s_to_c(s), nil, 0)){222 free(rp);223 free(s);224 return p;225 }226 free(rp);227 break;228 }229 s_free(s);230 return 0;231 }232 static char*233 check(int argc, char **argv)234 {235 int i;236 Addr *a;237 Pattern *p;238 int matchedbang;240 matchedbang = 0;241 for(i = 0; i < argc; i++){242 a = readaddrs(argv[i], nil);243 for(; a != nil; a = a->next){244 p = checkaddr(a->val);245 if(p == nil)246 continue;247 if(p->bang)248 matchedbang = 1;249 else250 return nil;251 }252 }253 if(matchedbang)254 return "!match";255 else256 return "no match";257 }259 /*260 * add anything that isn't already matched, all matches are lower case261 */262 static char*263 add(char *pp, int argc, char **argv)264 {265 int fd, i;266 String *s;267 char *cp;268 Addr *a;270 a = nil;271 for(i = 0; i < argc; i++)272 a = readaddrs(argv[i], a);274 fd = open(pp, OWRITE);275 seek(fd, 0, 2);276 for(; a != nil; a = a->next){277 if(checkaddr(a->val))278 continue;279 s = simplify(a->val);280 cp = s_to_c(s);281 fprint(fd, "%q\t%q\n", cp, a->val);282 if(*cp == '=')283 newpattern(Texact, cp+1, 0);284 else if(*cp == '~')285 newpattern(Tregexp, cp+1, 0);286 s_free(s);287 }288 close(fd);289 return nil;290 }292 void293 main(int argc, char **argv)294 {295 char *patternpath;297 ARGBEGIN {298 case 'd':299 debug++;300 break;301 } ARGEND;303 quotefmtinstall();305 if(argc < 3)306 usage();308 patternpath = argv[1];309 readpatterns(patternpath);310 if(strcmp(argv[0], "add") == 0)311 exits(add(patternpath, argc-2, argv+2));312 else if(strcmp(argv[0], "check") == 0)313 exits(check(argc-2, argv+2));314 else315 usage();316 }