masqmail

annotate src/masqmail.c @ 156:ee2afbf92428

require host_name to be set in config file exit otherwise there is no portable way to determine the hostname (actually the hostname that masqmail should use) thus it must be set by the administrator
author meillo@marmaro.de
date Thu, 08 Jul 2010 09:49:05 +0200
parents 5ec5e6637049
children 5b621742b2e7
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 1999-2001 Oliver Kurth
meillo@76 3 Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@0 4
meillo@0 5 This program is free software; you can redistribute it and/or modify
meillo@0 6 it under the terms of the GNU General Public License as published by
meillo@0 7 the Free Software Foundation; either version 2 of the License, or
meillo@0 8 (at your option) any later version.
meillo@0 9
meillo@0 10 This program is distributed in the hope that it will be useful,
meillo@0 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 13 GNU General Public License for more details.
meillo@0 14
meillo@0 15 You should have received a copy of the GNU General Public License
meillo@0 16 along with this program; if not, write to the Free Software
meillo@0 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 18 */
meillo@0 19
meillo@0 20 #include <stdio.h>
meillo@0 21 #include <errno.h>
meillo@0 22 #include <stdlib.h>
meillo@0 23 #include <string.h>
meillo@0 24 #include <unistd.h>
meillo@0 25 #include <sys/types.h>
meillo@0 26 #include <sys/socket.h>
meillo@0 27 #include <sys/time.h>
meillo@0 28 #include <netinet/in.h>
meillo@0 29 #include <netdb.h>
meillo@0 30 #include <syslog.h>
meillo@0 31 #include <signal.h>
meillo@0 32
meillo@0 33 #include <glib.h>
meillo@0 34
meillo@0 35 #include "masqmail.h"
meillo@0 36
meillo@0 37 /* mutually exclusive modes. Note that there is neither a 'get' mode
meillo@0 38 nor a 'queue daemon' mode. These, as well as the distinction beween
meillo@0 39 the two (non exclusive) daemon (queue and listen) modes are handled
meillo@0 40 by flags.*/
meillo@10 41 typedef enum _mta_mode {
meillo@10 42 MODE_ACCEPT = 0, /* accept message on stdin */
meillo@10 43 MODE_DAEMON, /* run as daemon */
meillo@10 44 MODE_RUNQUEUE, /* single queue run, online or offline */
meillo@10 45 MODE_GET_DAEMON, /* run as get (retrieve) daemon */
meillo@10 46 MODE_SMTP, /* accept SMTP on stdin */
meillo@10 47 MODE_LIST, /* list queue */
meillo@10 48 MODE_MCMD, /* do queue manipulation */
meillo@10 49 MODE_VERSION, /* show version */
meillo@10 50 MODE_BI, /* fake ;-) */
meillo@10 51 MODE_NONE /* to prevent default MODE_ACCEPT */
meillo@10 52 } mta_mode;
meillo@0 53
meillo@0 54 char *pidfile = NULL;
meillo@0 55 volatile int sigterm_in_progress = 0;
meillo@0 56
meillo@10 57 static void
meillo@10 58 sigterm_handler(int sig)
meillo@0 59 {
meillo@10 60 if (sigterm_in_progress)
meillo@10 61 raise(sig);
meillo@10 62 sigterm_in_progress = 1;
meillo@0 63
meillo@10 64 if (pidfile) {
meillo@10 65 uid_t uid;
meillo@10 66 uid = seteuid(0);
meillo@10 67 if (unlink(pidfile) != 0)
meillo@10 68 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
meillo@10 69 seteuid(uid); /* we exit anyway after this, just to be sure */
meillo@10 70 }
meillo@0 71
meillo@10 72 signal(sig, SIG_DFL);
meillo@10 73 raise(sig);
meillo@0 74 }
meillo@0 75
meillo@10 76 #ifdef ENABLE_IDENT /* so far used for that only */
meillo@10 77 static gboolean
meillo@10 78 is_in_netlist(gchar * host, GList * netlist)
meillo@0 79 {
meillo@10 80 guint hostip = inet_addr(host);
meillo@10 81 struct in_addr addr;
meillo@0 82
meillo@10 83 addr.s_addr = hostip;
meillo@10 84 if (addr.s_addr != INADDR_NONE) {
meillo@10 85 GList *node;
meillo@10 86 foreach(netlist, node) {
meillo@10 87 struct in_addr *net = (struct in_addr *) (node->data);
meillo@10 88 if ((addr.s_addr & net->s_addr) == net->s_addr)
meillo@10 89 return TRUE;
meillo@10 90 }
meillo@10 91 }
meillo@10 92 return FALSE;
meillo@0 93 }
meillo@0 94 #endif
meillo@0 95
meillo@10 96 gchar*
meillo@10 97 get_optarg(char *argv[], gint argc, gint * argp, gint * pos)
meillo@0 98 {
meillo@10 99 if (argv[*argp][*pos])
meillo@10 100 return &(argv[*argp][*pos]);
meillo@10 101 else {
meillo@10 102 if (*argp + 1 < argc) {
meillo@10 103 if (argv[(*argp) + 1][0] != '-') {
meillo@10 104 (*argp)++;
meillo@10 105 *pos = 0;
meillo@10 106 return &(argv[*argp][*pos]);
meillo@10 107 }
meillo@10 108 }
meillo@10 109 }
meillo@10 110 return NULL;
meillo@0 111 }
meillo@0 112
meillo@10 113 gchar*
meillo@10 114 get_progname(gchar * arg0)
meillo@0 115 {
meillo@10 116 gchar *p = arg0 + strlen(arg0) - 1;
meillo@10 117 while (p > arg0) {
meillo@10 118 if (*p == '/')
meillo@10 119 return p + 1;
meillo@10 120 p--;
meillo@10 121 }
meillo@10 122 return p;
meillo@0 123 }
meillo@0 124
meillo@10 125 gboolean
meillo@10 126 write_pidfile(gchar * name)
meillo@0 127 {
meillo@10 128 FILE *fptr;
meillo@0 129
meillo@10 130 if ((fptr = fopen(name, "wt"))) {
meillo@10 131 fprintf(fptr, "%d\n", getpid());
meillo@10 132 fclose(fptr);
meillo@10 133 pidfile = strdup(name);
meillo@10 134 return TRUE;
meillo@10 135 }
meillo@10 136 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
meillo@10 137 return FALSE;
meillo@10 138 }
meillo@0 139
meillo@10 140 static void
meillo@10 141 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
meillo@10 142 {
meillo@10 143 guint pid;
meillo@0 144
meillo@10 145 /* daemon */
meillo@10 146 if (!conf.run_as_user) {
meillo@10 147 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 148 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@10 149 exit(EXIT_FAILURE);
meillo@10 150 }
meillo@10 151 }
meillo@0 152
meillo@74 153 /* reparent to init only if init is not already the parent */
meillo@74 154 if (getppid() != 1) {
meillo@74 155 if ((pid = fork()) > 0) {
meillo@74 156 exit(EXIT_SUCCESS);
meillo@74 157 } else if (pid < 0) {
meillo@74 158 logwrite(LOG_ALERT, "could not fork!");
meillo@74 159 exit(EXIT_FAILURE);
meillo@74 160 }
meillo@10 161 }
meillo@0 162
meillo@10 163 signal(SIGTERM, sigterm_handler);
meillo@10 164 write_pidfile(PIDFILEDIR "/masqmail.pid");
meillo@0 165
meillo@10 166 conf.do_verbose = FALSE;
meillo@10 167
meillo@72 168 /* closing and reopening the log ensures that it is open afterwards
meillo@72 169 because it is possible that the log is assigned to fd 1 and gets
meillo@72 170 thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 171 */
meillo@72 172 logclose();
meillo@10 173 fclose(stdin);
meillo@10 174 fclose(stdout);
meillo@10 175 fclose(stderr);
meillo@72 176 logopen();
meillo@10 177
meillo@10 178 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
meillo@0 179 }
meillo@0 180
meillo@0 181 #ifdef ENABLE_POP3
meillo@10 182 static void
meillo@10 183 mode_get_daemon(gint get_interval, char *argv[])
meillo@0 184 {
meillo@10 185 guint pid;
meillo@0 186
meillo@10 187 /* daemon */
meillo@10 188 if (!conf.run_as_user) {
meillo@10 189 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 190 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@10 191 exit(EXIT_FAILURE);
meillo@10 192 }
meillo@10 193 }
meillo@0 194
meillo@74 195 /* reparent to init only if init is not already the parent */
meillo@74 196 if (getppid() != 1) {
meillo@74 197 if ((pid = fork()) > 0) {
meillo@74 198 exit(EXIT_SUCCESS);
meillo@74 199 } else if (pid < 0) {
meillo@74 200 logwrite(LOG_ALERT, "could not fork!");
meillo@74 201 exit(EXIT_FAILURE);
meillo@74 202 }
meillo@10 203 }
meillo@0 204
meillo@10 205 signal(SIGTERM, sigterm_handler);
meillo@10 206 write_pidfile(PIDFILEDIR "/masqmail-get.pid");
meillo@0 207
meillo@10 208 conf.do_verbose = FALSE;
meillo@0 209
meillo@72 210 /* closing and reopening the log ensures that it is open afterwards
meillo@72 211 because it is possible that the log is assigned to fd 1 and gets
meillo@72 212 thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 213 */
meillo@72 214 logclose();
meillo@10 215 fclose(stdin);
meillo@10 216 fclose(stdout);
meillo@10 217 fclose(stderr);
meillo@72 218 logopen();
meillo@0 219
meillo@10 220 get_daemon(get_interval, argv);
meillo@0 221 }
meillo@0 222 #endif
meillo@0 223
meillo@0 224 #ifdef ENABLE_SMTP_SERVER
meillo@10 225 static void
meillo@10 226 mode_smtp()
meillo@0 227 {
meillo@10 228 /* accept smtp message on stdin */
meillo@10 229 /* write responses to stderr. */
meillo@0 230
meillo@10 231 struct sockaddr_in saddr;
meillo@10 232 gchar *peername = NULL;
meillo@10 233 int dummy = sizeof(saddr);
meillo@0 234
meillo@10 235 conf.do_verbose = FALSE;
meillo@0 236
meillo@10 237 if (!conf.run_as_user) {
meillo@10 238 seteuid(conf.orig_uid);
meillo@10 239 setegid(conf.orig_gid);
meillo@10 240 }
meillo@0 241
meillo@10 242 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 243
meillo@10 244 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 245 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 246 } else if (errno != ENOTSOCK)
meillo@10 247 exit(EXIT_FAILURE);
meillo@0 248
meillo@10 249 smtp_in(stdin, stderr, peername, NULL);
meillo@0 250 }
meillo@0 251 #endif
meillo@0 252
meillo@10 253 static void
meillo@10 254 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
meillo@0 255 {
meillo@10 256 /* accept message on stdin */
meillo@10 257 accept_error err;
meillo@10 258 message *msg = create_message();
meillo@10 259 gint i;
meillo@0 260
meillo@83 261 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@96 262 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@83 263 exit(EXIT_FAILURE);
meillo@10 264 }
meillo@0 265
meillo@10 266 if (!conf.run_as_user) {
meillo@10 267 seteuid(conf.orig_uid);
meillo@10 268 setegid(conf.orig_gid);
meillo@10 269 }
meillo@0 270
meillo@10 271 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 272
meillo@10 273 msg->received_prot = PROT_LOCAL;
meillo@10 274 for (i = 0; i < addr_cnt; i++) {
meillo@10 275 if (addresses[i][0] != '|')
meillo@10 276 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
meillo@10 277 else {
meillo@10 278 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
meillo@10 279 exit(EXIT_FAILURE);
meillo@10 280 }
meillo@10 281 }
meillo@0 282
meillo@10 283 /* -f option */
meillo@10 284 msg->return_path = return_path;
meillo@0 285
meillo@10 286 /* -F option */
meillo@10 287 msg->full_sender_name = full_sender_name;
meillo@0 288
meillo@10 289 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
meillo@10 290 if (spool_write(msg, TRUE)) {
meillo@10 291 pid_t pid;
meillo@10 292 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@0 293
meillo@10 294 if (!conf.do_queue) {
meillo@10 295 if ((pid = fork()) == 0) {
meillo@10 296 conf.do_verbose = FALSE;
meillo@10 297 fclose(stdin);
meillo@10 298 fclose(stdout);
meillo@10 299 fclose(stderr);
meillo@10 300 if (deliver(msg)) {
meillo@10 301 exit(EXIT_SUCCESS);
meillo@10 302 } else
meillo@10 303 exit(EXIT_FAILURE);
meillo@10 304 } else if (pid < 0) {
meillo@10 305 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
meillo@10 306 }
meillo@10 307 }
meillo@10 308 } else {
meillo@10 309 fprintf(stderr, "Could not write spool file\n");
meillo@10 310 exit(EXIT_FAILURE);
meillo@10 311 }
meillo@10 312 } else {
meillo@10 313 switch (err) {
meillo@10 314 case AERR_EOF:
meillo@10 315 fprintf(stderr, "unexpected EOF.\n");
meillo@10 316 exit(EXIT_FAILURE);
meillo@10 317 case AERR_NORCPT:
meillo@10 318 fprintf(stderr, "no recipients.\n");
meillo@10 319 exit(EXIT_FAILURE);
meillo@117 320 case AERR_SIZE:
meillo@117 321 fprintf(stderr, "max message size exceeded.\n");
meillo@117 322 exit(EXIT_FAILURE);
meillo@10 323 default:
meillo@10 324 /* should never happen: */
meillo@10 325 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@10 326 exit(EXIT_FAILURE);
meillo@10 327 }
meillo@10 328 exit(EXIT_FAILURE);
meillo@0 329 }
meillo@0 330 }
meillo@0 331
meillo@0 332 int
meillo@0 333 main(int argc, char *argv[])
meillo@0 334 {
meillo@10 335 /* cmd line flags */
meillo@10 336 gchar *conf_file = CONF_FILE;
meillo@10 337 gint arg = 1;
meillo@10 338 gboolean do_get = FALSE;
meillo@10 339 gboolean do_get_online = FALSE;
meillo@0 340
meillo@10 341 gboolean do_listen = FALSE;
meillo@10 342 gboolean do_runq = FALSE;
meillo@10 343 gboolean do_runq_online = FALSE;
meillo@0 344
meillo@10 345 gboolean do_queue = FALSE;
meillo@0 346
meillo@10 347 gboolean do_verbose = FALSE;
meillo@10 348 gint debug_level = -1;
meillo@0 349
meillo@10 350 mta_mode mta_mode = MODE_ACCEPT;
meillo@0 351
meillo@10 352 gint queue_interval = 0;
meillo@10 353 gint get_interval = 0;
meillo@10 354 gboolean opt_t = FALSE;
meillo@10 355 gboolean opt_i = FALSE;
meillo@10 356 gboolean opt_odb = FALSE;
meillo@10 357 gboolean opt_oem = FALSE;
meillo@10 358 gboolean exit_failure = FALSE;
meillo@0 359
meillo@10 360 gchar *M_cmd = NULL;
meillo@0 361
meillo@10 362 gint exit_code = EXIT_SUCCESS;
meillo@10 363 gchar *route_name = NULL;
meillo@10 364 gchar *get_name = NULL;
meillo@10 365 gchar *progname;
meillo@10 366 gchar *f_address = NULL;
meillo@10 367 gchar *full_sender_name = NULL;
meillo@10 368 address *return_path = NULL; /* may be changed by -f option */
meillo@0 369
meillo@10 370 progname = get_progname(argv[0]);
meillo@0 371
meillo@10 372 if (strcmp(progname, "mailq") == 0) {
meillo@10 373 mta_mode = MODE_LIST;
meillo@10 374 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 375 mta_mode = MODE_MCMD;
meillo@10 376 M_cmd = "rm";
meillo@10 377 } else if (strcmp(progname, "runq") == 0) {
meillo@10 378 mta_mode = MODE_RUNQUEUE;
meillo@10 379 do_runq = TRUE;
meillo@10 380 } else if (strcmp(progname, "rmail") == 0) {
meillo@89 381 /* the `rmail' alias should probably be removed now
meillo@89 382 that we have the rmail script. But let's keep it
meillo@89 383 for some while for compatibility. 2010-06-19 */
meillo@10 384 mta_mode = MODE_ACCEPT;
meillo@10 385 opt_i = TRUE;
meillo@10 386 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 387 mta_mode = MODE_SMTP;
meillo@10 388 }
meillo@0 389
meillo@10 390 /* parse cmd line */
meillo@10 391 while (arg < argc) {
meillo@10 392 gint pos = 0;
meillo@10 393 if ((argv[arg][pos] == '-') && (argv[arg][pos + 1] != '-')) {
meillo@10 394 pos++;
meillo@10 395 switch (argv[arg][pos++]) {
meillo@10 396 case 'b':
meillo@10 397 switch (argv[arg][pos++]) {
meillo@10 398 case 'd':
meillo@10 399 do_listen = TRUE;
meillo@10 400 mta_mode = MODE_DAEMON;
meillo@10 401 break;
meillo@10 402 case 'i':
meillo@10 403 /* ignored */
meillo@10 404 mta_mode = MODE_BI;
meillo@10 405 break;
meillo@10 406 case 's':
meillo@10 407 mta_mode = MODE_SMTP;
meillo@10 408 break;
meillo@10 409 case 'p':
meillo@10 410 mta_mode = MODE_LIST;
meillo@10 411 break;
meillo@10 412 case 'V':
meillo@10 413 mta_mode = MODE_VERSION;
meillo@10 414 break;
meillo@10 415 default:
meillo@10 416 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 417 exit(EXIT_FAILURE);
meillo@10 418 }
meillo@10 419 break;
meillo@10 420 case 'B':
meillo@10 421 /* we ignore this and throw the argument away */
meillo@10 422 get_optarg(argv, argc, &arg, &pos);
meillo@10 423 break;
meillo@10 424 case 'C':
meillo@10 425 if (!(conf_file = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 426 fprintf(stderr, "-C requires a filename as argument.\n");
meillo@10 427 exit(EXIT_FAILURE);
meillo@10 428 }
meillo@10 429 break;
meillo@10 430 case 'F':
meillo@10 431 {
meillo@10 432 full_sender_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 433 if (!full_sender_name) {
meillo@10 434 fprintf(stderr, "-F requires a name as an argument\n");
meillo@10 435 exit(EXIT_FAILURE);
meillo@10 436 }
meillo@10 437 }
meillo@10 438 break;
meillo@10 439 case 'd':
meillo@10 440 if (getuid() == 0) {
meillo@10 441 char *lvl = get_optarg(argv, argc, &arg, &pos);
meillo@10 442 if (lvl)
meillo@10 443 debug_level = atoi(lvl);
meillo@10 444 else {
meillo@10 445 fprintf(stderr, "-d requires a number as an argument.\n");
meillo@10 446 exit(EXIT_FAILURE);
meillo@10 447 }
meillo@10 448 } else {
meillo@10 449 fprintf(stderr, "only root may set the debug level.\n");
meillo@10 450 exit(EXIT_FAILURE);
meillo@10 451 }
meillo@10 452 break;
meillo@10 453 case 'f':
meillo@10 454 /* set return path */
meillo@10 455 {
meillo@10 456 gchar *address;
meillo@10 457 address = get_optarg(argv, argc, &arg, &pos);
meillo@10 458 if (address) {
meillo@10 459 f_address = g_strdup(address);
meillo@10 460 } else {
meillo@10 461 fprintf(stderr, "-f requires an address as an argument\n");
meillo@10 462 exit(EXIT_FAILURE);
meillo@10 463 }
meillo@10 464 }
meillo@10 465 break;
meillo@10 466 case 'g':
meillo@10 467 do_get = TRUE;
meillo@10 468 if (!mta_mode)
meillo@10 469 mta_mode = MODE_NONE; /* to prevent default MODE_ACCEPT */
meillo@10 470 if (argv[arg][pos] == 'o') {
meillo@10 471 pos++;
meillo@10 472 do_get_online = TRUE;
meillo@10 473 /* can be NULL, then we use online detection method */
meillo@10 474 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 475
meillo@10 476 if (route_name != NULL) {
meillo@10 477 if (isdigit(route_name[0])) {
meillo@10 478 get_interval = time_interval(route_name, &pos);
meillo@10 479 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 480 mta_mode = MODE_GET_DAEMON;
meillo@10 481 do_get = FALSE;
meillo@10 482 }
meillo@10 483 }
meillo@10 484 } else {
meillo@10 485 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 486 get_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 487 }
meillo@10 488 }
meillo@10 489 break;
meillo@10 490 case 'i':
meillo@10 491 if (argv[arg][pos] == 0) {
meillo@10 492 opt_i = TRUE;
meillo@10 493 exit_failure = FALSE; /* may override -oem */
meillo@10 494 } else {
meillo@10 495 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 496 exit(EXIT_FAILURE);
meillo@10 497 }
meillo@10 498 break;
meillo@10 499 case 'M':
meillo@10 500 {
meillo@10 501 mta_mode = MODE_MCMD;
meillo@10 502 M_cmd = g_strdup(&(argv[arg][pos]));
meillo@10 503 }
meillo@10 504 break;
meillo@10 505 case 'o':
meillo@10 506 switch (argv[arg][pos++]) {
meillo@10 507 case 'e':
meillo@10 508 if (argv[arg][pos++] == 'm') /* -oem */
meillo@10 509 if (!opt_i)
meillo@10 510 exit_failure = TRUE;
meillo@10 511 opt_oem = TRUE;
meillo@10 512 break;
meillo@10 513 case 'd':
meillo@10 514 if (argv[arg][pos] == 'b') /* -odb */
meillo@10 515 opt_odb = TRUE;
meillo@10 516 else if (argv[arg][pos] == 'q') /* -odq */
meillo@10 517 do_queue = TRUE;
meillo@10 518 break;
meillo@10 519 case 'i':
meillo@10 520 opt_i = TRUE;
meillo@10 521 exit_failure = FALSE; /* may override -oem */
meillo@10 522 break;
meillo@10 523 }
meillo@10 524 break;
meillo@10 525
meillo@10 526 case 'q':
meillo@10 527 {
meillo@10 528 gchar *optarg;
meillo@10 529
meillo@10 530 do_runq = TRUE;
meillo@10 531 mta_mode = MODE_RUNQUEUE;
meillo@10 532 if (argv[arg][pos] == 'o') {
meillo@10 533 pos++;
meillo@10 534 do_runq = FALSE;
meillo@10 535 do_runq_online = TRUE;
meillo@10 536 /* can be NULL, then we use online detection method */
meillo@10 537 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 538 } else
meillo@10 539 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 540 mta_mode = MODE_DAEMON;
meillo@10 541 queue_interval = time_interval(optarg, &pos);
meillo@10 542 }
meillo@10 543 }
meillo@10 544 break;
meillo@10 545 case 't':
meillo@10 546 if (argv[arg][pos] == 0) {
meillo@10 547 opt_t = TRUE;
meillo@10 548 } else {
meillo@10 549 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 550 exit(EXIT_FAILURE);
meillo@10 551 }
meillo@10 552 break;
meillo@10 553 case 'v':
meillo@10 554 do_verbose = TRUE;
meillo@10 555 break;
meillo@10 556 default:
meillo@10 557 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 558 exit(EXIT_FAILURE);
meillo@10 559 }
meillo@10 560 } else {
meillo@10 561 if (argv[arg][pos + 1] == '-') {
meillo@10 562 if (argv[arg][pos + 2] != '\0') {
meillo@10 563 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 564 exit(EXIT_FAILURE);
meillo@10 565 }
meillo@10 566 arg++;
meillo@10 567 }
meillo@10 568 break;
meillo@10 569 }
meillo@10 570 arg++;
meillo@0 571 }
meillo@0 572
meillo@10 573 if (mta_mode == MODE_VERSION) {
meillo@12 574 gchar *with_resolver = "";
meillo@12 575 gchar *with_smtp_server = "";
meillo@12 576 gchar *with_pop3 = "";
meillo@12 577 gchar *with_auth = "";
meillo@12 578 gchar *with_maildir = "";
meillo@12 579 gchar *with_ident = "";
meillo@12 580 gchar *with_mserver = "";
meillo@0 581
meillo@0 582 #ifdef ENABLE_RESOLVER
meillo@10 583 with_resolver = " +resolver";
meillo@0 584 #endif
meillo@0 585 #ifdef ENABLE_SMTP_SERVER
meillo@10 586 with_smtp_server = " +smtp-server";
meillo@0 587 #endif
meillo@0 588 #ifdef ENABLE_POP3
meillo@10 589 with_pop3 = " +pop3";
meillo@0 590 #endif
meillo@0 591 #ifdef ENABLE_AUTH
meillo@10 592 with_auth = " +auth";
meillo@0 593 #endif
meillo@0 594 #ifdef ENABLE_MAILDIR
meillo@10 595 with_maildir = " +maildir";
meillo@0 596 #endif
meillo@0 597 #ifdef ENABLE_IDENT
meillo@10 598 with_ident = " +ident";
meillo@0 599 #endif
meillo@0 600 #ifdef ENABLE_MSERVER
meillo@10 601 with_mserver = " +mserver";
meillo@0 602 #endif
meillo@0 603
meillo@10 604 printf("%s %s%s%s%s%s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_smtp_server,
meillo@10 605 with_pop3, with_auth, with_maildir, with_ident, with_mserver);
meillo@0 606
meillo@10 607 exit(EXIT_SUCCESS);
meillo@10 608 }
meillo@0 609
meillo@10 610 /* initialize random generator */
meillo@10 611 srand(time(NULL));
meillo@10 612 /* ignore SIGPIPE signal */
meillo@10 613 signal(SIGPIPE, SIG_IGN);
meillo@0 614
meillo@73 615 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 616 {
meillo@10 617 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 618
meillo@10 619 if (max_fd <= 0)
meillo@10 620 max_fd = 64;
meillo@10 621 for (i = 3; i < max_fd; i++)
meillo@10 622 close(i);
meillo@10 623 }
meillo@0 624
meillo@10 625 init_conf();
meillo@0 626
meillo@10 627 /* if we are not privileged, and the config file was changed we
meillo@10 628 implicetely set the the run_as_user flag and give up all
meillo@10 629 privileges.
meillo@0 630
meillo@10 631 So it is possible for a user to run his own daemon without
meillo@10 632 breaking security.
meillo@10 633 */
meillo@10 634 if (strcmp(conf_file, CONF_FILE) != 0) {
meillo@10 635 if (conf.orig_uid != 0) {
meillo@10 636 conf.run_as_user = TRUE;
meillo@10 637 seteuid(conf.orig_uid);
meillo@10 638 setegid(conf.orig_gid);
meillo@10 639 setuid(conf.orig_uid);
meillo@10 640 setgid(conf.orig_gid);
meillo@10 641 }
meillo@10 642 }
meillo@0 643
meillo@155 644 conf.log_dir = LOG_DIR;
meillo@155 645 logopen();
meillo@155 646 if (!read_conf(conf_file)) {
meillo@155 647 logwrite(LOG_ALERT, "SHUTTING DOWN due to problems reading config\n");
meillo@155 648 exit(5);
meillo@155 649 }
meillo@155 650 logclose();
meillo@0 651
meillo@10 652 if (do_queue)
meillo@10 653 conf.do_queue = TRUE;
meillo@10 654 if (do_verbose)
meillo@10 655 conf.do_verbose = TRUE;
meillo@10 656 if (debug_level >= 0) /* if >= 0, it was given by argument */
meillo@10 657 conf.debug_level = debug_level;
meillo@0 658
meillo@46 659 /* It appears that changing to / ensures that we are never in
meillo@46 660 a directory which we cannot access. This situation could be
meillo@46 661 possible after changing identity.
meillo@46 662 Maybe we should only change to / if we not run as user, to
meillo@46 663 allow relative paths for log files in test setups for
meillo@46 664 instance.
meillo@46 665 */
meillo@10 666 chdir("/");
meillo@0 667
meillo@10 668 if (!conf.run_as_user) {
meillo@10 669 if (setgid(0) != 0) {
meillo@10 670 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 671 exit(EXIT_FAILURE);
meillo@10 672 }
meillo@10 673 if (setuid(0) != 0) {
meillo@10 674 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 675 exit(EXIT_FAILURE);
meillo@10 676 }
meillo@10 677 }
meillo@0 678
meillo@10 679 if (!logopen()) {
meillo@10 680 fprintf(stderr, "could not open log file\n");
meillo@10 681 exit(EXIT_FAILURE);
meillo@10 682 }
meillo@0 683
meillo@10 684 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
meillo@0 685
meillo@10 686 DEBUG(5) {
meillo@10 687 gchar **str = argv;
meillo@10 688 debugf("args: \n");
meillo@10 689 while (*str) {
meillo@10 690 debugf("%s \n", *str);
meillo@10 691 str++;
meillo@10 692 }
meillo@10 693 }
meillo@10 694 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 695
meillo@10 696 if (f_address) {
meillo@10 697 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
meillo@10 698 g_free(f_address);
meillo@10 699 if (!return_path) {
meillo@10 700 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
meillo@10 701 exit(EXIT_FAILURE);
meillo@10 702 }
meillo@10 703 }
meillo@10 704
meillo@10 705 if (do_get) {
meillo@0 706 #ifdef ENABLE_POP3
meillo@10 707 if ((mta_mode == MODE_NONE) || (mta_mode == MODE_RUNQUEUE)) {
meillo@10 708 set_identity(conf.orig_uid, "getting mail");
meillo@10 709 if (do_get_online) {
meillo@10 710 if (route_name != NULL) {
meillo@10 711 conf.online_detect = g_strdup("argument");
meillo@10 712 set_online_name(route_name);
meillo@10 713 }
meillo@10 714 get_online();
meillo@10 715 } else {
meillo@10 716 if (get_name)
meillo@10 717 get_from_name(get_name);
meillo@10 718 else
meillo@10 719 get_all();
meillo@10 720 }
meillo@10 721 } else {
meillo@10 722 logwrite(LOG_ALERT, "get (-g) only allowed alone or together with queue run (-q)\n");
meillo@10 723 }
meillo@10 724 #else
meillo@10 725 fprintf(stderr, "get (pop) support not compiled in\n");
meillo@10 726 #endif
meillo@10 727 }
meillo@0 728
meillo@10 729 switch (mta_mode) {
meillo@10 730 case MODE_DAEMON:
meillo@10 731 mode_daemon(do_listen, queue_interval, argv);
meillo@10 732 break;
meillo@10 733 case MODE_RUNQUEUE:
meillo@10 734 {
meillo@10 735 /* queue runs */
meillo@10 736 set_identity(conf.orig_uid, "queue run");
meillo@0 737
meillo@10 738 if (do_runq)
meillo@10 739 exit_code = queue_run() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 740
meillo@10 741 if (do_runq_online) {
meillo@10 742 if (route_name != NULL) {
meillo@10 743 conf.online_detect = g_strdup("argument");
meillo@10 744 set_online_name(route_name);
meillo@10 745 }
meillo@10 746 exit_code =
meillo@10 747 queue_run_online() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 748 }
meillo@10 749 }
meillo@10 750 break;
meillo@10 751 case MODE_GET_DAEMON:
meillo@10 752 #ifdef ENABLE_POP3
meillo@10 753 if (route_name != NULL) {
meillo@10 754 conf.online_detect = g_strdup("argument");
meillo@10 755 set_online_name(route_name);
meillo@10 756 }
meillo@10 757 mode_get_daemon(get_interval, argv);
meillo@10 758 #endif
meillo@10 759 break;
meillo@10 760
meillo@10 761 case MODE_SMTP:
meillo@10 762 #ifdef ENABLE_SMTP_SERVER
meillo@10 763 mode_smtp();
meillo@10 764 #else
meillo@10 765 fprintf(stderr, "smtp server support not compiled in\n");
meillo@10 766 #endif
meillo@10 767 break;
meillo@10 768
meillo@10 769 case MODE_LIST:
meillo@10 770 queue_list();
meillo@10 771 break;
meillo@10 772
meillo@10 773 case MODE_BI:
meillo@10 774 exit(EXIT_SUCCESS);
meillo@10 775 break; /* well... */
meillo@10 776
meillo@10 777 case MODE_MCMD:
meillo@10 778 if (strcmp(M_cmd, "rm") == 0) {
meillo@10 779 gboolean ok = FALSE;
meillo@10 780
meillo@10 781 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@10 782
meillo@10 783 if (is_privileged_user(conf.orig_uid)) {
meillo@10 784 for (; arg < argc; arg++) {
meillo@10 785 if (queue_delete(argv[arg]))
meillo@10 786 ok = TRUE;
meillo@10 787 }
meillo@10 788 } else {
meillo@10 789 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@10 790 if (pw) {
meillo@10 791 for (; arg < argc; arg++) {
meillo@10 792 message *msg = msg_spool_read(argv[arg], FALSE);
meillo@10 793 #ifdef ENABLE_IDENT
meillo@10 794 if (((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL))
meillo@10 795 || is_in_netlist(msg->received_host, conf.ident_trusted_nets)) {
meillo@10 796 #else
meillo@10 797 if ((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL)) {
meillo@10 798 #endif
meillo@10 799 if (msg->ident) {
meillo@10 800 if (strcmp(pw->pw_name, msg->ident) == 0) {
meillo@10 801 if (queue_delete(argv[arg]))
meillo@10 802 ok = TRUE;
meillo@10 803 } else {
meillo@10 804 fprintf(stderr, "you do not own message id %s\n", argv[arg]);
meillo@10 805 }
meillo@10 806 } else
meillo@10 807 fprintf(stderr, "message %s does not have an ident.\n", argv[arg]);
meillo@10 808 } else {
meillo@10 809 fprintf(stderr, "message %s was not received locally or from a trusted network.\n", argv[arg]);
meillo@10 810 }
meillo@10 811 }
meillo@10 812 } else {
meillo@10 813 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n", conf.orig_uid, strerror(errno));
meillo@10 814 }
meillo@10 815 }
meillo@10 816 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
meillo@10 817 } else {
meillo@10 818 fprintf(stderr, "unknown command %s\n", M_cmd);
meillo@10 819 exit(EXIT_FAILURE);
meillo@10 820 }
meillo@10 821 break;
meillo@10 822
meillo@10 823 case MODE_ACCEPT:
meillo@10 824 {
meillo@107 825 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
meillo@110 826 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@10 827 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
meillo@10 828 exit(exit_failure ? EXIT_FAILURE : EXIT_SUCCESS);
meillo@10 829 }
meillo@10 830 break;
meillo@10 831 case MODE_NONE:
meillo@10 832 break;
meillo@10 833 default:
meillo@10 834 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 835 break;
meillo@0 836 }
meillo@0 837
meillo@10 838 logclose();
meillo@0 839
meillo@10 840 exit(exit_code);
meillo@0 841 }