%{ /* ckaliases - verify syntax of sendmail-style alias files Copyright (C) 2005, 2007 Sergey Poznyakoff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "alck.h" #include char *program_name; SLIST *cw_list; /* List of domain names pertaining to Sendmail 'w' class */ static int restricted; /* prohibit use of `special' aliases (pipes, file redirections and includes */ int verbose; /* Verbose mode */ int error_count; /* Number of errors detected so far */ %} %union { char *string; SLIST *slist; }; %token IDENT EMAIL STRING LHS %token CONT %token EOL %token INCLUDE %type string lhs %type rhs emails email %% input : list ; list : alias | list EOL alias | list error EOL { yyclearin; yyerrok; } ; alias : /* empty */ | lhs rhs { regalias($1, $2); } ; lhs : LHS ':' ; rhs : emails | rhs CONT emails { slist_append(&$1, $3); $$ = $1; } ; emails: email | emails ',' email { slist_append(&$1, $3); $$ = $1; } ; email : string { if (restricted && ($1[0] == '|' || $1[0] == '/')) { yyerror("Construct not allowed"); YYERROR; } $$ = NULL; slist_add(&$$, $1); } | EMAIL { $$ = NULL; slist_add(&$$, $1); } | INCLUDE string { if (restricted) { yyerror("Include statement is not allowed"); YYERROR; } $$ = NULL; read_include(&$$, $2); } ; string: IDENT | STRING ; %% int yyerror (char *s) { parserror(file_name, line_num, "%s", s); error_count++; } void * emalloc(size_t size) { void *p = malloc(size); if (!p) { error("not enough memory"); exit(1); } return p; } void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s: ", program_name); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } void syserror(int ec, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s: ", program_name); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(ec)); va_end(ap); } void parserror(const char *file, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s: %s:%d: ", program_name, file, line); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } void usage() { printf("usage: ckaliases [OPTIONS] [FILES...]\n"); printf("OPTIONS and FILES may be interspered.\n"); printf("Valid options are:\n"); printf(" -d SPEC Set debug level. SPEC consists of the following\n"); printf(" letters:\n"); printf(" y enable parser debugging\n"); printf(" l enable lexical analizer debugging\n"); printf(" Upper-case variants are also accepted. Prepending\n"); printf(" a letter with '-' reverts its sense\n"); printf(" -f FILE Read names of alias files from FILE\n"); printf(" -h Display this help list\n"); printf(" -r Restrict alias file syntax to aliases only (i.e.\n"); printf(" prohibit use of pipes and file redirections\n"); printf(" -u Revert the effect of the previous -r option\n"); printf(" -v Verbose mode\n"); printf(" -V print program version and exit\n"); printf(" -w FILE Read contents of Sendmail `w' class from the given\n"); printf(" file.\n"); printf("\n"); printf("Report bugs to <%s>\n", "gray@gnu.org"); } static char license[] = "\ License GPLv3+: GNU GPL version 3 or later \n\ This is free software: you are free to change and redistribute it.\n\ There is NO WARRANTY, to the extent permitted by law.\n"; #define VERSION "2.0" void version() { printf("alck %s\n", VERSION); printf("Copyright (C) 2005, 2007, 2013 Sergey Poznyakoff\n"); printf("%s\n", license); exit(0); } int main(int argc, char **argv) { char *p; int c; int file_count = 0; int true = 1; char *cwfile = "/etc/mail/sendmail.cw"; SLIST *file_list; /* List of files to be read */ struct string_list *s; init_lex(); program_name = argv[0]; while ((c = getopt(argc, argv, "-d:f:hp:ruVvw:")) != EOF) { switch (c) { case 1: if (!cw_list) read_include(&cw_list, cwfile); openaliases(optarg); yyparse(); file_count++; break; case 'd': for (p = optarg; *p; p++) { switch (*p) { case '-': true = 0; break; case 'y': case 'Y': yydebug = true; true = 1; break; case 'l': case 'L': lex_debug(true); true = 1; break; default: error("%s: unknown debug option %c", argv[0], c); } } break; case 'f': if (!cw_list) read_include(&cw_list, cwfile); file_list = NULL; read_include(&file_list, optarg); if (file_list) { for (s = file_list->head; s; s = s->next) { openaliases_prefix(optarg, s->str); yyparse(); file_count++; } slist_destroy(&file_list); } break; case 'h': usage(); exit(0); case 'r': restricted = 1; break; case 'u': restricted = 0; break; case 'v': verbose++; break; case 'V': version(); exit (0); case 'w': if (file_count) error("-w must be used before first non-option argument"); cwfile = optarg; break; default: exit (1); } } argc -= optind; argv += optind; if (!cw_list) read_include(&cw_list, cwfile); while (argc--) { openaliases(*argv++); yyparse(); file_count++; } if (!file_count) { error("no files specified"); exit(1); } if (verbose) printf("%d files\n", file_count); end_aliases(); check_aliases(); if (verbose) printf("%lu errors\n", error_count); exit(error_count!=0); }