| 1 |
%{ |
| 2 |
/* |
| 3 |
* rcfile_y.y -- Run control file parser for fetchmail |
| 4 |
* |
| 5 |
* For license terms, see the file COPYING in this directory. |
| 6 |
*/ |
| 7 |
|
| 8 |
#include "config.h" |
| 9 |
#include <stdio.h> |
| 10 |
#include <sys/types.h> |
| 11 |
#include <sys/file.h> |
| 12 |
#if defined(HAVE_SYS_WAIT_H) |
| 13 |
#include <sys/wait.h> |
| 14 |
#endif |
| 15 |
#include <sys/stat.h> |
| 16 |
#include <errno.h> |
| 17 |
#if defined(STDC_HEADERS) |
| 18 |
#include <stdlib.h> |
| 19 |
#endif |
| 20 |
#if defined(HAVE_UNISTD_H) |
| 21 |
#include <unistd.h> |
| 22 |
#endif |
| 23 |
#include <string.h> |
| 24 |
|
| 25 |
#if defined(__CYGWIN__) |
| 26 |
#include <sys/cygwin.h> |
| 27 |
#endif /* __CYGWIN__ */ |
| 28 |
|
| 29 |
#include "fetchmail.h" |
| 30 |
#include "i18n.h" |
| 31 |
|
| 32 |
/* parser reads these */ |
| 33 |
char *rcfile; /* path name of rc file */ |
| 34 |
struct query cmd_opts; /* where to put command-line info */ |
| 35 |
|
| 36 |
/* parser sets these */ |
| 37 |
struct query *querylist; /* head of server list (globally visible) */ |
| 38 |
|
| 39 |
int yydebug; /* in case we didn't generate with -- debug */ |
| 40 |
|
| 41 |
static struct query current; /* current server record */ |
| 42 |
static int prc_errflag; |
| 43 |
static struct hostdata *leadentry; |
| 44 |
static flag trailer; |
| 45 |
|
| 46 |
static void record_current(void); |
| 47 |
static void user_reset(void); |
| 48 |
static void reset_server(const char *name, int skip); |
| 49 |
|
| 50 |
/* these should be of size PATH_MAX */ |
| 51 |
char currentwd[1024] = "", rcfiledir[1024] = ""; |
| 52 |
|
| 53 |
/* using Bison, this arranges that yydebug messages will show actual tokens */ |
| 54 |
extern char * yytext; |
| 55 |
#define YYPRINT(fp, type, val) fprintf(fp, " = \"%s\"", yytext) |
| 56 |
%} |
| 57 |
|
| 58 |
%union { |
| 59 |
int proto; |
| 60 |
int number; |
| 61 |
char *sval; |
| 62 |
} |
| 63 |
|
| 64 |
%token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL |
| 65 |
%token AUTHENTICATE TIMEOUT KPOP SDPS ENVELOPE QVIRTUAL |
| 66 |
%token USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP |
| 67 |
%token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS |
| 68 |
%token INTERFACE MONITOR PLUGIN PLUGOUT |
| 69 |
%token IS HERE THERE TO MAP |
| 70 |
%token BATCHLIMIT FETCHLIMIT FETCHSIZELIMIT FASTUIDL EXPUNGE PROPERTIES |
| 71 |
%token SET LOGFILE DAEMON SYSLOG IDFILE PIDFILE INVISIBLE POSTMASTER BOUNCEMAIL |
| 72 |
%token SPAMBOUNCE SOFTBOUNCE SHOWDOTS |
| 73 |
%token BADHEADER ACCEPT REJECT_ |
| 74 |
%token <proto> PROTO AUTHTYPE |
| 75 |
%token <sval> STRING |
| 76 |
%token <number> NUMBER |
| 77 |
%token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS |
| 78 |
%token DROPSTATUS DROPDELIVERED |
| 79 |
%token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS |
| 80 |
%token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTFILE SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT |
| 81 |
%token PRINCIPAL ESMTPNAME ESMTPPASSWORD |
| 82 |
%token TRACEPOLLS |
| 83 |
|
| 84 |
%expect 2 |
| 85 |
|
| 86 |
%destructor { free ($$); } STRING |
| 87 |
|
| 88 |
%% |
| 89 |
|
| 90 |
rcfile : /* empty */ |
| 91 |
| statement_list |
| 92 |
; |
| 93 |
|
| 94 |
statement_list : statement |
| 95 |
| statement_list statement |
| 96 |
; |
| 97 |
|
| 98 |
optmap : MAP | /* EMPTY */; |
| 99 |
|
| 100 |
/* future global options should also have the form SET <name> optmap <value> */ |
| 101 |
statement : SET LOGFILE optmap STRING {run.logfile = prependdir ($4, rcfiledir); free($4);} |
| 102 |
| SET IDFILE optmap STRING {run.idfile = prependdir ($4, rcfiledir); free($4);} |
| 103 |
| SET PIDFILE optmap STRING {run.pidfile = prependdir ($4, rcfiledir); free($4);} |
| 104 |
| SET DAEMON optmap NUMBER {run.poll_interval = $4;} |
| 105 |
| SET POSTMASTER optmap STRING {run.postmaster = $4;} |
| 106 |
| SET BOUNCEMAIL {run.bouncemail = TRUE;} |
| 107 |
| SET NO BOUNCEMAIL {run.bouncemail = FALSE;} |
| 108 |
| SET SPAMBOUNCE {run.spambounce = TRUE;} |
| 109 |
| SET NO SPAMBOUNCE {run.spambounce = FALSE;} |
| 110 |
| SET SOFTBOUNCE {run.softbounce = TRUE;} |
| 111 |
| SET NO SOFTBOUNCE {run.softbounce = FALSE;} |
| 112 |
| SET PROPERTIES optmap STRING {run.properties = $4;} |
| 113 |
| SET SYSLOG {run.use_syslog = TRUE;} |
| 114 |
| SET NO SYSLOG {run.use_syslog = FALSE;} |
| 115 |
| SET INVISIBLE {run.invisible = TRUE;} |
| 116 |
| SET NO INVISIBLE {run.invisible = FALSE;} |
| 117 |
| SET SHOWDOTS {run.showdots = FLAG_TRUE;} |
| 118 |
| SET NO SHOWDOTS {run.showdots = FLAG_FALSE;} |
| 119 |
|
| 120 |
/* |
| 121 |
* The way the next two productions are written depends on the fact that |
| 122 |
* userspecs cannot be empty. It's a kluge to deal with files that set |
| 123 |
* up a load of defaults and then have poll statements following with no |
| 124 |
* user options at all. |
| 125 |
*/ |
| 126 |
| define_server serverspecs {record_current();} |
| 127 |
| define_server serverspecs userspecs |
| 128 |
|
| 129 |
/* detect and complain about the most common user error */ |
| 130 |
| define_server serverspecs userspecs serv_option |
| 131 |
{yyerror(GT_("server option after user options"));} |
| 132 |
; |
| 133 |
|
| 134 |
define_server : POLL STRING {reset_server($2, FALSE); free($2);} |
| 135 |
| SKIP STRING {reset_server($2, TRUE); free($2);} |
| 136 |
| DEFAULTS {reset_server("defaults", FALSE);} |
| 137 |
; |
| 138 |
|
| 139 |
serverspecs : /* EMPTY */ |
| 140 |
| serverspecs serv_option |
| 141 |
; |
| 142 |
|
| 143 |
alias_list : STRING {save_str(¤t.server.akalist,$1,0); free($1);} |
| 144 |
| alias_list STRING {save_str(¤t.server.akalist,$2,0); free($2);} |
| 145 |
; |
| 146 |
|
| 147 |
domain_list : STRING {save_str(¤t.server.localdomains,$1,0); free($1);} |
| 148 |
| domain_list STRING {save_str(¤t.server.localdomains,$2,0); free($2);} |
| 149 |
; |
| 150 |
|
| 151 |
serv_option : AKA alias_list |
| 152 |
| VIA STRING {current.server.via = $2;} |
| 153 |
| LOCALDOMAINS domain_list |
| 154 |
| PROTOCOL PROTO {current.server.protocol = $2;} |
| 155 |
| PROTOCOL KPOP { |
| 156 |
current.server.protocol = P_POP3; |
| 157 |
|
| 158 |
if (current.server.authenticate == A_PASSWORD) |
| 159 |
#ifdef KERBEROS_V5 |
| 160 |
current.server.authenticate = A_KERBEROS_V5; |
| 161 |
#else |
| 162 |
current.server.authenticate = A_KERBEROS_V4; |
| 163 |
#endif /* KERBEROS_V5 */ |
| 164 |
current.server.service = KPOP_PORT; |
| 165 |
} |
| 166 |
| PRINCIPAL STRING {current.server.principal = $2;} |
| 167 |
| ESMTPNAME STRING {current.server.esmtp_name = $2;} |
| 168 |
| ESMTPPASSWORD STRING {current.server.esmtp_password = $2;} |
| 169 |
| PROTOCOL SDPS { |
| 170 |
#ifdef SDPS_ENABLE |
| 171 |
current.server.protocol = P_POP3; |
| 172 |
current.server.sdps = TRUE; |
| 173 |
#else |
| 174 |
yyerror(GT_("SDPS not enabled.")); |
| 175 |
#endif /* SDPS_ENABLE */ |
| 176 |
} |
| 177 |
| UIDL {current.server.uidl = FLAG_TRUE;} |
| 178 |
| NO UIDL {current.server.uidl = FLAG_FALSE;} |
| 179 |
| CHECKALIAS {current.server.checkalias = FLAG_TRUE;} |
| 180 |
| NO CHECKALIAS {current.server.checkalias = FLAG_FALSE;} |
| 181 |
| SERVICE STRING { |
| 182 |
current.server.service = $2; |
| 183 |
} |
| 184 |
| SERVICE NUMBER { |
| 185 |
int port = $2; |
| 186 |
char buf[10]; |
| 187 |
snprintf(buf, sizeof buf, "%d", port); |
| 188 |
current.server.service = xstrdup(buf); |
| 189 |
} |
| 190 |
| PORT NUMBER { |
| 191 |
int port = $2; |
| 192 |
char buf[10]; |
| 193 |
snprintf(buf, sizeof buf, "%d", port); |
| 194 |
current.server.service = xstrdup(buf); |
| 195 |
} |
| 196 |
| INTERVAL NUMBER |
| 197 |
{current.server.interval = $2;} |
| 198 |
| AUTHENTICATE AUTHTYPE |
| 199 |
{current.server.authenticate = $2;} |
| 200 |
| TIMEOUT NUMBER |
| 201 |
{current.server.timeout = $2;} |
| 202 |
| ENVELOPE NUMBER STRING |
| 203 |
{ |
| 204 |
current.server.envelope = $3; |
| 205 |
current.server.envskip = $2; |
| 206 |
} |
| 207 |
| ENVELOPE STRING |
| 208 |
{ |
| 209 |
current.server.envelope = $2; |
| 210 |
current.server.envskip = 0; |
| 211 |
} |
| 212 |
|
| 213 |
| QVIRTUAL STRING {current.server.qvirtual = $2;} |
| 214 |
| INTERFACE STRING { |
| 215 |
#ifdef CAN_MONITOR |
| 216 |
interface_parse($2, ¤t.server); |
| 217 |
#else |
| 218 |
fprintf(stderr, GT_("fetchmail: interface option is only supported under Linux (without IPv6) and FreeBSD\n")); |
| 219 |
#endif |
| 220 |
free($2); |
| 221 |
} |
| 222 |
| MONITOR STRING { |
| 223 |
#ifdef CAN_MONITOR |
| 224 |
current.server.monitor = $2; |
| 225 |
#else |
| 226 |
fprintf(stderr, GT_("fetchmail: monitor option is only supported under Linux (without IPv6) and FreeBSD\n")); |
| 227 |
free($2); |
| 228 |
#endif |
| 229 |
} |
| 230 |
| PLUGIN STRING { current.server.plugin = $2; } |
| 231 |
| PLUGOUT STRING { current.server.plugout = $2; } |
| 232 |
| DNS {current.server.dns = FLAG_TRUE;} |
| 233 |
| NO DNS {current.server.dns = FLAG_FALSE;} |
| 234 |
| NO ENVELOPE {current.server.envelope = STRING_DISABLED;} |
| 235 |
| TRACEPOLLS {current.server.tracepolls = FLAG_TRUE;} |
| 236 |
| NO TRACEPOLLS {current.server.tracepolls = FLAG_FALSE;} |
| 237 |
| BADHEADER ACCEPT {current.server.badheader = BHACCEPT;} |
| 238 |
| BADHEADER REJECT_ {current.server.badheader = BHREJECT;} |
| 239 |
; |
| 240 |
|
| 241 |
userspecs : user1opts {record_current(); user_reset();} |
| 242 |
| explicits |
| 243 |
; |
| 244 |
|
| 245 |
explicits : explicitdef {record_current(); user_reset();} |
| 246 |
| explicits explicitdef {record_current(); user_reset();} |
| 247 |
; |
| 248 |
|
| 249 |
explicitdef : userdef user0opts |
| 250 |
; |
| 251 |
|
| 252 |
userdef : USERNAME STRING {current.remotename = $2;} |
| 253 |
| USERNAME mapping_list HERE |
| 254 |
| USERNAME STRING THERE {current.remotename = $2;} |
| 255 |
; |
| 256 |
|
| 257 |
user0opts : /* EMPTY */ |
| 258 |
| user0opts user_option |
| 259 |
; |
| 260 |
|
| 261 |
user1opts : user_option |
| 262 |
| user1opts user_option |
| 263 |
; |
| 264 |
|
| 265 |
mapping_list : mapping |
| 266 |
| mapping_list mapping |
| 267 |
; |
| 268 |
|
| 269 |
mapping : STRING {if (0 == strcmp($1, "*")) { |
| 270 |
current.wildcard = TRUE; |
| 271 |
} else { |
| 272 |
save_str_pair(¤t.localnames, $1, NULL); |
| 273 |
} |
| 274 |
free($1);} |
| 275 |
| STRING MAP STRING {save_str_pair(¤t.localnames, $1, $3); free($1); free($3);} |
| 276 |
; |
| 277 |
|
| 278 |
folder_list : STRING {save_str(¤t.mailboxes,$1,0); free($1);} |
| 279 |
| folder_list STRING {save_str(¤t.mailboxes,$2,0); free($2);} |
| 280 |
; |
| 281 |
|
| 282 |
smtp_list : STRING {save_str(¤t.smtphunt, $1,TRUE); free($1);} |
| 283 |
| smtp_list STRING {save_str(¤t.smtphunt, $2,TRUE); free($2);} |
| 284 |
; |
| 285 |
|
| 286 |
fetch_list : STRING {save_str(¤t.domainlist, $1,TRUE); free($1);} |
| 287 |
| fetch_list STRING {save_str(¤t.domainlist, $2,TRUE); free($2);} |
| 288 |
; |
| 289 |
|
| 290 |
num_list : NUMBER |
| 291 |
{ |
| 292 |
struct idlist *id; |
| 293 |
id = save_str(¤t.antispam,STRING_DUMMY,0); |
| 294 |
id->val.status.num = $1; |
| 295 |
} |
| 296 |
| num_list NUMBER |
| 297 |
{ |
| 298 |
struct idlist *id; |
| 299 |
id = save_str(¤t.antispam,STRING_DUMMY,0); |
| 300 |
id->val.status.num = $2; |
| 301 |
} |
| 302 |
; |
| 303 |
|
| 304 |
user_option : TO mapping_list HERE |
| 305 |
| TO mapping_list |
| 306 |
| IS mapping_list HERE |
| 307 |
| IS mapping_list |
| 308 |
|
| 309 |
| IS STRING THERE {current.remotename = $2;} |
| 310 |
| PASSWORD STRING {current.password = $2;} |
| 311 |
| FOLDER folder_list |
| 312 |
| SMTPHOST smtp_list |
| 313 |
| FETCHDOMAINS fetch_list |
| 314 |
| SMTPADDRESS STRING {current.smtpaddress = $2;} |
| 315 |
| SMTPNAME STRING {current.smtpname = $2;} |
| 316 |
| SPAMRESPONSE num_list |
| 317 |
| MDA STRING {current.mda = $2;} |
| 318 |
| BSMTP STRING {current.bsmtp = prependdir ($2, rcfiledir); free($2);} |
| 319 |
| LMTP {current.listener = LMTP_MODE;} |
| 320 |
| PRECONNECT STRING {current.preconnect = $2;} |
| 321 |
| POSTCONNECT STRING {current.postconnect = $2;} |
| 322 |
|
| 323 |
| KEEP {current.keep = FLAG_TRUE;} |
| 324 |
| FLUSH {current.flush = FLAG_TRUE;} |
| 325 |
| LIMITFLUSH {current.limitflush = FLAG_TRUE;} |
| 326 |
| FETCHALL {current.fetchall = FLAG_TRUE;} |
| 327 |
| REWRITE {current.rewrite = FLAG_TRUE;} |
| 328 |
| FORCECR {current.forcecr = FLAG_TRUE;} |
| 329 |
| STRIPCR {current.stripcr = FLAG_TRUE;} |
| 330 |
| PASS8BITS {current.pass8bits = FLAG_TRUE;} |
| 331 |
| DROPSTATUS {current.dropstatus = FLAG_TRUE;} |
| 332 |
| DROPDELIVERED {current.dropdelivered = FLAG_TRUE;} |
| 333 |
| MIMEDECODE {current.mimedecode = FLAG_TRUE;} |
| 334 |
| IDLE {current.idle = FLAG_TRUE;} |
| 335 |
|
| 336 |
| SSL { |
| 337 |
#ifdef SSL_ENABLE |
| 338 |
current.use_ssl = FLAG_TRUE; |
| 339 |
#else |
| 340 |
yyerror(GT_("SSL is not enabled")); |
| 341 |
#endif |
| 342 |
} |
| 343 |
| SSLKEY STRING {current.sslkey = prependdir ($2, rcfiledir); free($2);} |
| 344 |
| SSLCERT STRING {current.sslcert = prependdir ($2, rcfiledir); free($2);} |
| 345 |
| SSLPROTO STRING {current.sslproto = $2;} |
| 346 |
| SSLCERTCK {current.sslcertck = FLAG_TRUE;} |
| 347 |
| SSLCERTFILE STRING {current.sslcertfile = prependdir($2, rcfiledir); free($2);} |
| 348 |
| SSLCERTPATH STRING {current.sslcertpath = prependdir($2, rcfiledir); free($2);} |
| 349 |
| SSLCOMMONNAME STRING {current.sslcommonname = $2;} |
| 350 |
| SSLFINGERPRINT STRING {current.sslfingerprint = $2;} |
| 351 |
|
| 352 |
| NO KEEP {current.keep = FLAG_FALSE;} |
| 353 |
| NO FLUSH {current.flush = FLAG_FALSE;} |
| 354 |
| NO LIMITFLUSH {current.limitflush = FLAG_FALSE;} |
| 355 |
| NO FETCHALL {current.fetchall = FLAG_FALSE;} |
| 356 |
| NO REWRITE {current.rewrite = FLAG_FALSE;} |
| 357 |
| NO FORCECR {current.forcecr = FLAG_FALSE;} |
| 358 |
| NO STRIPCR {current.stripcr = FLAG_FALSE;} |
| 359 |
| NO PASS8BITS {current.pass8bits = FLAG_FALSE;} |
| 360 |
| NO DROPSTATUS {current.dropstatus = FLAG_FALSE;} |
| 361 |
| NO DROPDELIVERED {current.dropdelivered = FLAG_FALSE;} |
| 362 |
| NO MIMEDECODE {current.mimedecode = FLAG_FALSE;} |
| 363 |
| NO IDLE {current.idle = FLAG_FALSE;} |
| 364 |
|
| 365 |
| NO SSL {current.use_ssl = FLAG_FALSE;} |
| 366 |
|
| 367 |
| LIMIT NUMBER {current.limit = NUM_VALUE_IN($2);} |
| 368 |
| WARNINGS NUMBER {current.warnings = NUM_VALUE_IN($2);} |
| 369 |
| FETCHLIMIT NUMBER {current.fetchlimit = NUM_VALUE_IN($2);} |
| 370 |
| FETCHSIZELIMIT NUMBER {current.fetchsizelimit = NUM_VALUE_IN($2);} |
| 371 |
| FASTUIDL NUMBER {current.fastuidl = NUM_VALUE_IN($2);} |
| 372 |
| BATCHLIMIT NUMBER {current.batchlimit = NUM_VALUE_IN($2);} |
| 373 |
| EXPUNGE NUMBER {current.expunge = NUM_VALUE_IN($2);} |
| 374 |
|
| 375 |
| PROPERTIES STRING {current.properties = $2;} |
| 376 |
; |
| 377 |
%% |
| 378 |
|
| 379 |
/* lexer interface */ |
| 380 |
extern char *rcfile; |
| 381 |
extern int prc_lineno; |
| 382 |
extern char *yytext; |
| 383 |
extern FILE *yyin; |
| 384 |
|
| 385 |
static struct query *hosttail; /* where to add new elements */ |
| 386 |
|
| 387 |
void yyerror (const char *s) |
| 388 |
/* report a syntax error */ |
| 389 |
{ |
| 390 |
report_at_line(stderr, 0, rcfile, prc_lineno, GT_("%s at %s"), s, |
| 391 |
(yytext && yytext[0]) ? yytext : GT_("end of input")); |
| 392 |
prc_errflag++; |
| 393 |
} |
| 394 |
|
| 395 |
/** check that a configuration file is secure, returns PS_* status codes */ |
| 396 |
int prc_filecheck(const char *pathname, |
| 397 |
const flag securecheck /** shortcuts permission, filetype and uid tests if false */) |
| 398 |
{ |
| 399 |
#ifndef __EMX__ |
| 400 |
struct stat statbuf; |
| 401 |
|
| 402 |
errno = 0; |
| 403 |
|
| 404 |
/* special case useful for debugging purposes */ |
| 405 |
if (strcmp("/dev/null", pathname) == 0) |
| 406 |
return(PS_SUCCESS); |
| 407 |
|
| 408 |
/* pass through the special name for stdin */ |
| 409 |
if (strcmp("-", pathname) == 0) |
| 410 |
return(PS_SUCCESS); |
| 411 |
|
| 412 |
/* the run control file must have the same uid as the REAL uid of this |
| 413 |
process, it must have permissions no greater than 600, and it must not |
| 414 |
be a symbolic link. We check these conditions here. */ |
| 415 |
|
| 416 |
if (stat(pathname, &statbuf) < 0) { |
| 417 |
if (errno == ENOENT) |
| 418 |
return(PS_SUCCESS); |
| 419 |
else { |
| 420 |
report(stderr, "lstat: %s: %s\n", pathname, strerror(errno)); |
| 421 |
return(PS_IOERR); |
| 422 |
} |
| 423 |
} |
| 424 |
|
| 425 |
if (!securecheck) return PS_SUCCESS; |
| 426 |
|
| 427 |
if (!S_ISREG(statbuf.st_mode)) |
| 428 |
{ |
| 429 |
fprintf(stderr, GT_("File %s must be a regular file.\n"), pathname); |
| 430 |
return(PS_IOERR); |
| 431 |
} |
| 432 |
|
| 433 |
#ifndef __BEOS__ |
| 434 |
#ifdef __CYGWIN__ |
| 435 |
if (cygwin_internal(CW_CHECK_NTSEC, pathname)) |
| 436 |
#endif /* __CYGWIN__ */ |
| 437 |
if (statbuf.st_mode & (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | S_IXOTH)) |
| 438 |
{ |
| 439 |
fprintf(stderr, GT_("File %s must have no more than -rwx------ (0700) permissions.\n"), |
| 440 |
pathname); |
| 441 |
return(PS_IOERR); |
| 442 |
} |
| 443 |
#endif /* __BEOS__ */ |
| 444 |
|
| 445 |
#ifdef HAVE_GETEUID |
| 446 |
if (statbuf.st_uid != geteuid()) |
| 447 |
#else |
| 448 |
if (statbuf.st_uid != getuid()) |
| 449 |
#endif /* HAVE_GETEUID */ |
| 450 |
{ |
| 451 |
fprintf(stderr, GT_("File %s must be owned by you.\n"), pathname); |
| 452 |
return(PS_IOERR); |
| 453 |
} |
| 454 |
#endif |
| 455 |
return(PS_SUCCESS); |
| 456 |
} |
| 457 |
|
| 458 |
int prc_parse_file (const char *pathname, const flag securecheck) |
| 459 |
/* digest the configuration into a linked list of host records */ |
| 460 |
{ |
| 461 |
prc_errflag = 0; |
| 462 |
querylist = hosttail = (struct query *)NULL; |
| 463 |
|
| 464 |
errno = 0; |
| 465 |
|
| 466 |
/* Check that the file is secure */ |
| 467 |
if ( (prc_errflag = prc_filecheck(pathname, securecheck)) != 0 ) |
| 468 |
return(prc_errflag); |
| 469 |
|
| 470 |
/* |
| 471 |
* Croak if the configuration directory does not exist. |
| 472 |
* This probably means an NFS mount failed and we can't |
| 473 |
* see a configuration file that ought to be there. |
| 474 |
* Question: is this a portable check? It's not clear |
| 475 |
* that all implementations of lstat() will return ENOTDIR |
| 476 |
* rather than plain ENOENT in this case... |
| 477 |
*/ |
| 478 |
if (errno == ENOTDIR) |
| 479 |
return(PS_IOERR); |
| 480 |
else if (errno == ENOENT) |
| 481 |
return(PS_SUCCESS); |
| 482 |
|
| 483 |
/* Open the configuration file and feed it to the lexer. */ |
| 484 |
if (strcmp(pathname, "-") == 0) |
| 485 |
yyin = stdin; |
| 486 |
else if ((yyin = fopen(pathname,"r")) == (FILE *)NULL) { |
| 487 |
report(stderr, "open: %s: %s\n", pathname, strerror(errno)); |
| 488 |
return(PS_IOERR); |
| 489 |
} |
| 490 |
|
| 491 |
yyparse(); /* parse entire file */ |
| 492 |
|
| 493 |
fclose(yyin); /* not checking this should be safe, file mode was r */ |
| 494 |
|
| 495 |
if (prc_errflag) |
| 496 |
return(PS_SYNTAX); |
| 497 |
else |
| 498 |
return(PS_SUCCESS); |
| 499 |
} |
| 500 |
|
| 501 |
static void reset_server(const char *name, int skip) |
| 502 |
/* clear the entire global record and initialize it with a new name */ |
| 503 |
{ |
| 504 |
trailer = FALSE; |
| 505 |
memset(¤t,'\0',sizeof(current)); |
| 506 |
current.smtp_socket = -1; |
| 507 |
current.server.pollname = xstrdup(name); |
| 508 |
current.server.skip = skip; |
| 509 |
current.server.principal = (char *)NULL; |
| 510 |
} |
| 511 |
|
| 512 |
|
| 513 |
static void user_reset(void) |
| 514 |
/* clear the global current record (user parameters) used by the parser */ |
| 515 |
{ |
| 516 |
struct hostdata save; |
| 517 |
|
| 518 |
/* |
| 519 |
* Purpose of this code is to initialize the new server block, but |
| 520 |
* preserve whatever server name was previously set. Also |
| 521 |
* preserve server options unless the command-line explicitly |
| 522 |
* overrides them. |
| 523 |
*/ |
| 524 |
save = current.server; |
| 525 |
|
| 526 |
memset(¤t, '\0', sizeof(current)); |
| 527 |
current.smtp_socket = -1; |
| 528 |
|
| 529 |
current.server = save; |
| 530 |
} |
| 531 |
|
| 532 |
/** append a host record to the host list */ |
| 533 |
struct query *hostalloc(struct query *init /** pointer to block containing |
| 534 |
initial values */) |
| 535 |
{ |
| 536 |
struct query *node; |
| 537 |
|
| 538 |
/* allocate new node */ |
| 539 |
node = (struct query *) xmalloc(sizeof(struct query)); |
| 540 |
|
| 541 |
/* initialize it */ |
| 542 |
if (init) |
| 543 |
memcpy(node, init, sizeof(struct query)); |
| 544 |
else |
| 545 |
{ |
| 546 |
memset(node, '\0', sizeof(struct query)); |
| 547 |
node->smtp_socket = -1; |
| 548 |
} |
| 549 |
|
| 550 |
/* append to end of list */ |
| 551 |
if (hosttail != (struct query *) 0) |
| 552 |
hosttail->next = node; /* list contains at least one element */ |
| 553 |
else |
| 554 |
querylist = node; /* list is empty */ |
| 555 |
hosttail = node; |
| 556 |
|
| 557 |
if (trailer) |
| 558 |
node->server.lead_server = leadentry; |
| 559 |
else |
| 560 |
{ |
| 561 |
node->server.lead_server = NULL; |
| 562 |
leadentry = &node->server; |
| 563 |
} |
| 564 |
|
| 565 |
return(node); |
| 566 |
} |
| 567 |
|
| 568 |
static void record_current(void) |
| 569 |
/* register current parameters and append to the host list */ |
| 570 |
{ |
| 571 |
(void) hostalloc(¤t); |
| 572 |
trailer = TRUE; |
| 573 |
} |
| 574 |
|
| 575 |
char *prependdir (const char *file, const char *dir) |
| 576 |
/* if a filename is relative to dir, convert it to an absolute path */ |
| 577 |
{ |
| 578 |
char *newfile; |
| 579 |
if (!file[0] || /* null path */ |
| 580 |
file[0] == '/' || /* absolute path */ |
| 581 |
strcmp(file, "-") == 0 || /* stdin/stdout */ |
| 582 |
!dir[0]) /* we don't HAVE_GETCWD */ |
| 583 |
return xstrdup (file); |
| 584 |
newfile = (char *)xmalloc (strlen (dir) + 1 + strlen (file) + 1); |
| 585 |
if (dir[strlen(dir) - 1] != '/') |
| 586 |
sprintf (newfile, "%s/%s", dir, file); |
| 587 |
else |
| 588 |
sprintf (newfile, "%s%s", dir, file); |
| 589 |
return newfile; |
| 590 |
} |
| 591 |
|
| 592 |
/* rcfile_y.y ends here */ |