masqmail

annotate src/masqmail.c @ 378:5781ba87df95

Removed ident. This had been discussed on the mailing list in Oct 2011. Ident is hardly useful in typical setups for masqmail. Probably Oliver had used it in his setup; that would make sense. Now, I know of nobody who needs it.
author markus schnalke <meillo@marmaro.de>
date Sat, 14 Jan 2012 21:36:58 +0100
parents b27f66555ba8
children cdd16614c1f5
rev   line source
meillo@367 1 /*
meillo@367 2 ** MasqMail
meillo@367 3 ** Copyright (C) 1999-2001 Oliver Kurth
meillo@367 4 ** Copyright (C) 2010 markus schnalke <meillo@marmaro.de>
meillo@367 5 **
meillo@367 6 ** This program is free software; you can redistribute it and/or modify
meillo@367 7 ** it under the terms of the GNU General Public License as published by
meillo@367 8 ** the Free Software Foundation; either version 2 of the License, or
meillo@367 9 ** (at your option) any later version.
meillo@367 10 **
meillo@367 11 ** This program is distributed in the hope that it will be useful,
meillo@367 12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@367 13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@367 14 ** GNU General Public License for more details.
meillo@367 15 **
meillo@367 16 ** You should have received a copy of the GNU General Public License
meillo@367 17 ** along with this program; if not, write to the Free Software
meillo@367 18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
meillo@0 19 */
meillo@0 20
meillo@0 21 #include <stdio.h>
meillo@0 22 #include <errno.h>
meillo@0 23 #include <stdlib.h>
meillo@0 24 #include <string.h>
meillo@0 25 #include <unistd.h>
meillo@0 26 #include <sys/types.h>
meillo@0 27 #include <sys/socket.h>
meillo@0 28 #include <sys/time.h>
meillo@0 29 #include <netinet/in.h>
meillo@0 30 #include <netdb.h>
meillo@0 31 #include <syslog.h>
meillo@0 32 #include <signal.h>
meillo@0 33
meillo@0 34 #include <glib.h>
meillo@0 35
meillo@0 36 #include "masqmail.h"
meillo@0 37
meillo@367 38 /*
meillo@367 39 ** mutually exclusive modes. Note that there is no 'queue daemon' mode.
meillo@367 40 ** It, as well as the distinction beween the two (non exclusive) daemon
meillo@367 41 ** (queue and listen) modes, is handled by flags.
meillo@367 42 */
meillo@281 43 enum mta_mode {
meillo@281 44 MODE_NONE = 0, /* to check if a mode was set */
meillo@281 45 MODE_ACCEPT, /* accept message on stdin (fallback mode) */
meillo@10 46 MODE_DAEMON, /* run as daemon */
meillo@10 47 MODE_RUNQUEUE, /* single queue run, online or offline */
meillo@10 48 MODE_SMTP, /* accept SMTP on stdin */
meillo@10 49 MODE_LIST, /* list queue */
meillo@10 50 MODE_MCMD, /* do queue manipulation */
meillo@10 51 MODE_VERSION, /* show version */
meillo@10 52 MODE_BI, /* fake ;-) */
meillo@281 53 };
meillo@281 54 enum mta_mode mta_mode = MODE_NONE;
meillo@0 55
meillo@0 56 char *pidfile = NULL;
meillo@0 57 volatile int sigterm_in_progress = 0;
meillo@0 58
meillo@10 59 static void
meillo@10 60 sigterm_handler(int sig)
meillo@0 61 {
meillo@10 62 if (sigterm_in_progress)
meillo@10 63 raise(sig);
meillo@10 64 sigterm_in_progress = 1;
meillo@0 65
meillo@10 66 if (pidfile) {
meillo@331 67 uid_t uid = geteuid();
meillo@331 68 if (seteuid(0) != 0) {
meillo@331 69 logwrite(LOG_ALERT, "sigterm_handler: could not set euid to %d: %s\n", 0, strerror(errno));
meillo@331 70 }
meillo@10 71 if (unlink(pidfile) != 0)
meillo@10 72 logwrite(LOG_WARNING, "could not delete pid file %s: %s\n", pidfile, strerror(errno));
meillo@10 73 seteuid(uid); /* we exit anyway after this, just to be sure */
meillo@10 74 }
meillo@0 75
meillo@10 76 signal(sig, SIG_DFL);
meillo@10 77 raise(sig);
meillo@0 78 }
meillo@0 79
meillo@249 80 /*
meillo@367 81 ** argv: the original argv
meillo@367 82 ** argp: number of arg (may get modified!)
meillo@367 83 ** cp: pointing to the char after the option
meillo@367 84 ** e.g. `-d 6' `-d6'
meillo@367 85 ** ^ ^
meillo@249 86 */
meillo@10 87 gchar*
meillo@366 88 get_optarg(char *argv[], gint *argp, char *cp)
meillo@0 89 {
meillo@249 90 if (*cp) {
meillo@249 91 /* this kind: -xval */
meillo@249 92 return cp;
meillo@249 93 }
meillo@249 94 cp = argv[*argp+1];
meillo@249 95 if (cp && (*cp != '-')) {
meillo@249 96 /* this kind: -x val */
meillo@249 97 (*argp)++;
meillo@249 98 return cp;
meillo@10 99 }
meillo@10 100 return NULL;
meillo@0 101 }
meillo@0 102
meillo@10 103 gboolean
meillo@366 104 write_pidfile(gchar *name)
meillo@0 105 {
meillo@10 106 FILE *fptr;
meillo@0 107
meillo@10 108 if ((fptr = fopen(name, "wt"))) {
meillo@10 109 fprintf(fptr, "%d\n", getpid());
meillo@10 110 fclose(fptr);
meillo@10 111 pidfile = strdup(name);
meillo@10 112 return TRUE;
meillo@10 113 }
meillo@10 114 logwrite(LOG_WARNING, "could not write pid file: %s\n", strerror(errno));
meillo@10 115 return FALSE;
meillo@10 116 }
meillo@0 117
meillo@264 118 /* on -bd or if -q has an argument */
meillo@10 119 static void
meillo@10 120 mode_daemon(gboolean do_listen, gint queue_interval, char *argv[])
meillo@10 121 {
meillo@10 122 guint pid;
meillo@0 123
meillo@10 124 /* daemon */
meillo@10 125 if (!conf.run_as_user) {
meillo@10 126 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 127 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@262 128 exit(1);
meillo@10 129 }
meillo@10 130 }
meillo@0 131
meillo@74 132 /* reparent to init only if init is not already the parent */
meillo@74 133 if (getppid() != 1) {
meillo@74 134 if ((pid = fork()) > 0) {
meillo@262 135 exit(0);
meillo@74 136 } else if (pid < 0) {
meillo@208 137 logwrite(LOG_ALERT, "could not fork!\n");
meillo@262 138 exit(1);
meillo@74 139 }
meillo@10 140 }
meillo@0 141
meillo@10 142 signal(SIGTERM, sigterm_handler);
meillo@10 143 write_pidfile(PIDFILEDIR "/masqmail.pid");
meillo@0 144
meillo@10 145 conf.do_verbose = FALSE;
meillo@10 146
meillo@367 147 /*
meillo@367 148 ** closing and reopening the log ensures that it is open afterwards
meillo@367 149 ** because it is possible that the log is assigned to fd 1 and gets
meillo@367 150 ** thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 151 */
meillo@72 152 logclose();
meillo@10 153 fclose(stdin);
meillo@10 154 fclose(stdout);
meillo@10 155 fclose(stderr);
meillo@72 156 logopen();
meillo@10 157
meillo@208 158 logwrite(LOG_NOTICE, "%s %s daemon starting\n", PACKAGE, VERSION);
meillo@10 159 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
meillo@0 160 }
meillo@0 161
meillo@264 162 /* -bs or called as smtpd or in.smtpd */
meillo@10 163 static void
meillo@10 164 mode_smtp()
meillo@0 165 {
meillo@10 166 /* accept smtp message on stdin */
meillo@10 167 /* write responses to stderr. */
meillo@0 168
meillo@10 169 struct sockaddr_in saddr;
meillo@10 170 gchar *peername = NULL;
meillo@10 171 int dummy = sizeof(saddr);
meillo@0 172
meillo@10 173 conf.do_verbose = FALSE;
meillo@0 174
meillo@10 175 if (!conf.run_as_user) {
meillo@331 176 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@10 177 }
meillo@0 178
meillo@10 179 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 180
meillo@10 181 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 182 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 183 } else if (errno != ENOTSOCK)
meillo@262 184 exit(1);
meillo@0 185
meillo@10 186 smtp_in(stdin, stderr, peername, NULL);
meillo@0 187 }
meillo@0 188
meillo@264 189 /* default mode if address args or -t is specified, or called as rmail */
meillo@10 190 static void
meillo@367 191 mode_accept(address *return_path, gchar *full_sender_name, guint accept_flags,
meillo@367 192 char **addresses, int addr_cnt)
meillo@0 193 {
meillo@10 194 /* accept message on stdin */
meillo@10 195 accept_error err;
meillo@10 196 message *msg = create_message();
meillo@10 197 gint i;
meillo@266 198 pid_t pid;
meillo@0 199
meillo@83 200 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@96 201 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@262 202 exit(1);
meillo@10 203 }
meillo@0 204
meillo@10 205 if (!conf.run_as_user) {
meillo@331 206 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@10 207 }
meillo@0 208
meillo@10 209 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 210
meillo@10 211 msg->received_prot = PROT_LOCAL;
meillo@276 212
meillo@276 213 /* warn if -t option and cmdline addr args */
meillo@276 214 if (addr_cnt && (accept_flags & ACC_RCPT_FROM_HEAD)) {
meillo@276 215 logwrite(LOG_ALERT, "command line address arguments are now *added* to the mail header\\\n");
meillo@276 216 logwrite(LOG_ALERT, " recipient addresses (instead of substracted) when -t is given.\\\n");
meillo@276 217 logwrite(LOG_ALERT, " this changed with version 0.3.1\n");
meillo@276 218 }
meillo@276 219
meillo@10 220 for (i = 0; i < addr_cnt; i++) {
meillo@266 221 if (addresses[i][0] == '|') {
meillo@10 222 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
meillo@276 223 /* should we better ignore this one addr? */
meillo@262 224 exit(1);
meillo@10 225 }
meillo@264 226 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
meillo@10 227 }
meillo@0 228
meillo@10 229 /* -f option */
meillo@10 230 msg->return_path = return_path;
meillo@0 231
meillo@10 232 /* -F option */
meillo@10 233 msg->full_sender_name = full_sender_name;
meillo@0 234
meillo@266 235 err = accept_message(stdin, msg, accept_flags);
meillo@0 236
meillo@266 237 switch (err) {
meillo@266 238 case AERR_OK:
meillo@266 239 /* to continue; all other cases exit */
meillo@266 240 break;
meillo@266 241 case AERR_EOF:
meillo@266 242 fprintf(stderr, "unexpected EOF.\n");
meillo@266 243 exit(1);
meillo@266 244 case AERR_NORCPT:
meillo@266 245 fprintf(stderr, "no recipients.\n");
meillo@266 246 exit(1);
meillo@266 247 case AERR_SIZE:
meillo@266 248 fprintf(stderr, "max message size exceeded.\n");
meillo@266 249 exit(1);
meillo@266 250 default:
meillo@266 251 /* should never happen: */
meillo@266 252 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@266 253 exit(1);
meillo@266 254 }
meillo@266 255
meillo@266 256 if (!spool_write(msg, TRUE)) {
meillo@266 257 fprintf(stderr, "Could not write spool file\n");
meillo@266 258 exit(1);
meillo@266 259 }
meillo@266 260
meillo@267 261 /* here the mail is queued and thus in our responsibility */
meillo@266 262 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@266 263
meillo@266 264 if (conf.do_queue) {
meillo@266 265 /* we're finished as we only need to queue it */
meillo@266 266 return;
meillo@266 267 }
meillo@266 268
meillo@266 269 /* deliver at once */
meillo@266 270 if ((pid = fork()) < 0) {
meillo@266 271 logwrite(LOG_ALERT, "could not fork for delivery, id = %s\n", msg->uid);
meillo@266 272 } else if (pid == 0) {
meillo@266 273 conf.do_verbose = FALSE;
meillo@266 274 fclose(stdin);
meillo@266 275 fclose(stdout);
meillo@266 276 fclose(stderr);
meillo@266 277 if (deliver(msg)) {
meillo@266 278 exit(0);
meillo@10 279 } else {
meillo@267 280 /*
meillo@367 281 ** TODO: Should we really fail here? Because the
meillo@367 282 ** mail is queued already. If we fail the client
meillo@367 283 ** might submit it again. If at-once-delivery
meillo@367 284 ** is seen as an additional best-effort service,
meillo@367 285 ** then we should still exit successful here.
meillo@267 286 */
meillo@262 287 exit(1);
meillo@10 288 }
meillo@0 289 }
meillo@0 290 }
meillo@0 291
meillo@250 292 /*
meillo@367 293 ** if -Mrm is given
meillo@367 294 **
meillo@367 295 ** currently only the `rm' command is supported
meillo@367 296 ** until this changes, we don't need any facility for further commands
meillo@367 297 ** return success if at least one message had been deleted
meillo@250 298 */
meillo@250 299 static int
meillo@366 300 manipulate_queue(char *cmd, char *id[])
meillo@250 301 {
meillo@250 302 gboolean ok = FALSE;
meillo@250 303
meillo@250 304 if (strcmp(cmd, "rm") != 0) {
meillo@250 305 fprintf(stderr, "unknown command %s\n", cmd);
meillo@250 306 return FALSE;
meillo@250 307 }
meillo@250 308
meillo@250 309 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@250 310
meillo@250 311 /* privileged users may delete any mail */
meillo@250 312 if (is_privileged_user(conf.orig_uid)) {
meillo@250 313 for (; *id; id++) {
meillo@250 314 fprintf(stderr, "id: %s\n", *id);
meillo@250 315 if (queue_delete(*id)) {
meillo@250 316 ok = TRUE;
meillo@250 317 }
meillo@250 318 }
meillo@250 319 return ok;
meillo@250 320 }
meillo@250 321
meillo@250 322 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@250 323 if (!pw) {
meillo@250 324 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n",
meillo@250 325 conf.orig_uid, strerror(errno));
meillo@250 326 return FALSE;
meillo@250 327 }
meillo@250 328
meillo@250 329 /* non-privileged users may only delete their own messages */
meillo@250 330 for (; *id; id++) {
meillo@349 331 message *msg = msg_spool_read(*id);
meillo@250 332
meillo@250 333 fprintf(stderr, "id: %s\n", *id);
meillo@250 334
meillo@250 335 if (!msg->ident) {
meillo@250 336 fprintf(stderr, "message %s does not have an ident\n", *id);
meillo@250 337 continue;
meillo@250 338 }
meillo@250 339 if (strcmp(pw->pw_name, msg->ident) != 0) {
meillo@250 340 fprintf(stderr, "you do not own message id %s\n", *id);
meillo@250 341 continue;
meillo@250 342 }
meillo@250 343
meillo@378 344 if (msg->received_host || (msg->received_prot != PROT_LOCAL)) {
meillo@378 345 fprintf(stderr, "message %s was not received locally\n", *id);
meillo@250 346 continue;
meillo@250 347 }
meillo@250 348
meillo@250 349 ok = queue_delete(*id);
meillo@250 350 }
meillo@250 351 return ok;
meillo@250 352 }
meillo@250 353
meillo@264 354 /* -qo, -q (without argument), or called as runq */
meillo@251 355 static int
meillo@366 356 run_queue(gboolean do_runq, gboolean do_runq_online, char *route_name)
meillo@251 357 {
meillo@251 358 int ret;
meillo@251 359
meillo@251 360 /* queue runs */
meillo@251 361 set_identity(conf.orig_uid, "queue run");
meillo@251 362
meillo@251 363 if (do_runq) {
meillo@251 364 ret = queue_run();
meillo@251 365 }
meillo@251 366
meillo@251 367 if (do_runq_online) {
meillo@251 368 if (route_name) {
meillo@310 369 conf.online_query = g_strdup_printf("/bin/echo %s", route_name);
meillo@251 370 }
meillo@367 371 /*
meillo@367 372 ** TODO: change behavior of `-qo without argument'?
meillo@367 373 ** Because that behavior is included in -q.
meillo@367 374 */
meillo@251 375 ret = queue_run_online();
meillo@251 376 }
meillo@251 377 return ret;
meillo@251 378 }
meillo@251 379
meillo@264 380 /* -bV or default mode if neither addr arg nor -t */
meillo@261 381 static void
meillo@261 382 mode_version(void)
meillo@261 383 {
meillo@261 384 gchar *with_resolver = "";
meillo@261 385 gchar *with_auth = "";
meillo@261 386
meillo@261 387 #ifdef ENABLE_RESOLVER
meillo@261 388 with_resolver = " +resolver";
meillo@261 389 #endif
meillo@261 390 #ifdef ENABLE_AUTH
meillo@261 391 with_auth = " +auth";
meillo@261 392 #endif
meillo@261 393
meillo@378 394 printf("%s %s%s%s\n", PACKAGE, VERSION, with_resolver, with_auth);
meillo@261 395 }
meillo@261 396
meillo@281 397 void
meillo@281 398 set_mode(enum mta_mode mode)
meillo@281 399 {
meillo@281 400 if (mta_mode && mta_mode!=mode) {
meillo@281 401 fprintf(stderr, "operation mode was already specified (%d vs. %d)\n", mta_mode, mode);
meillo@281 402 exit(1);
meillo@281 403 }
meillo@281 404
meillo@281 405 mta_mode = mode;
meillo@281 406 return;
meillo@281 407 }
meillo@281 408
meillo@0 409 int
meillo@0 410 main(int argc, char *argv[])
meillo@0 411 {
meillo@251 412 gchar *progname;
meillo@366 413 char *opt;
meillo@249 414 gint arg;
meillo@0 415
meillo@10 416 gboolean do_listen = FALSE;
meillo@10 417 gboolean do_runq = FALSE;
meillo@10 418 gboolean do_runq_online = FALSE;
meillo@10 419 gboolean do_queue = FALSE;
meillo@10 420 gint queue_interval = 0;
meillo@251 421 gchar *M_cmd = NULL;
meillo@10 422 gboolean opt_t = FALSE;
meillo@10 423 gboolean opt_i = FALSE;
meillo@251 424 gchar *conf_file = CONF_FILE;
meillo@10 425 gchar *route_name = NULL;
meillo@10 426 gchar *f_address = NULL;
meillo@251 427 address *return_path = NULL; /* may be changed by -f option */
meillo@10 428 gchar *full_sender_name = NULL;
meillo@251 429 gboolean do_verbose = FALSE;
meillo@251 430 gint debug_level = -1;
meillo@0 431
meillo@260 432 /* strip the path part */
meillo@261 433 progname = strrchr(argv[0], '/');
meillo@261 434 progname = (progname) ? progname+1 : argv[0];
meillo@0 435
meillo@10 436 if (strcmp(progname, "mailq") == 0) {
meillo@10 437 mta_mode = MODE_LIST;
meillo@10 438 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 439 mta_mode = MODE_MCMD;
meillo@10 440 M_cmd = "rm";
meillo@284 441 } else if (strcmp(progname, "newaliases") == 0) {
meillo@284 442 mta_mode = MODE_BI;
meillo@10 443 } else if (strcmp(progname, "rmail") == 0) {
meillo@367 444 /*
meillo@367 445 ** the `rmail' alias should probably be removed now
meillo@367 446 ** that we have the rmail script. But let's keep it
meillo@367 447 ** for some while for compatibility. 2010-06-19
meillo@367 448 */
meillo@10 449 mta_mode = MODE_ACCEPT;
meillo@10 450 opt_i = TRUE;
meillo@284 451 } else if (strcmp(progname, "runq") == 0) {
meillo@284 452 mta_mode = MODE_RUNQUEUE;
meillo@284 453 do_runq = TRUE;
meillo@284 454 } else if (strcmp(progname, "smtpd") == 0
meillo@284 455 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 456 mta_mode = MODE_SMTP;
meillo@10 457 }
meillo@0 458
meillo@10 459 /* parse cmd line */
meillo@249 460 for (arg=1; arg<argc && argv[arg][0]=='-'; arg++) {
meillo@251 461 opt = argv[arg] + 1; /* points to the char after the dash */
meillo@10 462
meillo@249 463 if (strcmp(opt, "-") == 0) {
meillo@249 464 /* everything after `--' are address arguments */
meillo@249 465 arg++;
meillo@249 466 break;
meillo@10 467
meillo@278 468 } else if (strcmp(opt, "bm") == 0) {
meillo@281 469 set_mode(MODE_ACCEPT);
meillo@278 470
meillo@249 471 } else if (strcmp(opt, "bd") == 0) {
meillo@281 472 set_mode(MODE_DAEMON);
meillo@249 473 do_listen = TRUE;
meillo@249 474
meillo@249 475 } else if (strcmp(opt, "bi") == 0) {
meillo@281 476 set_mode(MODE_BI);
meillo@249 477
meillo@249 478 } else if (strcmp(opt, "bs") == 0) {
meillo@281 479 set_mode(MODE_SMTP);
meillo@249 480
meillo@249 481 } else if (strcmp(opt, "bp") == 0) {
meillo@281 482 set_mode(MODE_LIST);
meillo@249 483
meillo@249 484 } else if (strcmp(opt, "bV") == 0) {
meillo@281 485 set_mode(MODE_VERSION);
meillo@249 486
meillo@249 487 } else if (strncmp(opt, "B", 1) == 0) {
meillo@249 488 /* we ignore this and throw the argument away */
meillo@249 489 get_optarg(argv, &arg, opt+1);
meillo@249 490
meillo@249 491 } else if (strncmp(opt, "C", 1) == 0) {
meillo@251 492 conf_file = get_optarg(argv, &arg, opt+1);
meillo@251 493 if (!conf_file) {
meillo@249 494 fprintf(stderr, "-C requires a filename as argument.\n");
meillo@262 495 exit(1);
meillo@10 496 }
meillo@249 497
meillo@249 498 } else if (strncmp(opt, "d", 1) == 0) {
meillo@249 499 if (getuid() != 0) {
meillo@249 500 fprintf(stderr, "only root may set the debug level.\n");
meillo@262 501 exit(1);
meillo@249 502 }
meillo@249 503 char *lvl = get_optarg(argv, &arg, opt+1);
meillo@249 504 if (!lvl) {
meillo@249 505 fprintf(stderr, "-d requires a number argument.\n");
meillo@262 506 exit(1);
meillo@249 507 }
meillo@249 508 debug_level = atoi(lvl);
meillo@249 509
meillo@249 510 } else if (strncmp(opt, "f", 1) == 0) {
meillo@249 511 /* set return path */
meillo@249 512 gchar *address = get_optarg(argv, &arg, opt+1);
meillo@249 513 if (!address) {
meillo@249 514 fprintf(stderr, "-f requires an address argument\n");
meillo@262 515 exit(1);
meillo@249 516 }
meillo@249 517 f_address = g_strdup(address);
meillo@249 518
meillo@249 519 } else if (strncmp(opt, "F", 1) == 0) {
meillo@249 520 full_sender_name = get_optarg(argv, &arg, opt+1);
meillo@249 521 if (!full_sender_name) {
meillo@249 522 fprintf(stderr, "-F requires a name argument\n");
meillo@262 523 exit(1);
meillo@249 524 }
meillo@249 525
meillo@249 526 } else if (strcmp(opt, "i") == 0) {
meillo@249 527 opt_i = TRUE;
meillo@249 528
meillo@249 529 } else if (strcmp(opt, "m") == 0) {
meillo@249 530 /* ignore -m (me too) switch (see man page) */
meillo@249 531
meillo@249 532 } else if (strcmp(opt, "Mrm") == 0) {
meillo@281 533 set_mode(MODE_MCMD);
meillo@249 534 M_cmd = "rm";
meillo@249 535
meillo@249 536 } else if (strcmp(opt, "odq") == 0) {
meillo@249 537 do_queue = TRUE;
meillo@249 538
meillo@249 539 } else if (strcmp(opt, "oi") == 0) {
meillo@257 540 opt_i = TRUE;
meillo@249 541
meillo@249 542 } else if (strncmp(opt, "o", 1) == 0) {
meillo@249 543 /* ignore all other -oXXX options */
meillo@249 544
meillo@249 545 } else if (strncmp(opt, "qo", 2) == 0) {
meillo@278 546 /* must be before the `q' check */
meillo@281 547 set_mode(MODE_RUNQUEUE);
meillo@249 548 do_runq_online = TRUE;
meillo@249 549 /* can be NULL, then we use online detection method */
meillo@290 550 /* TODO: behavior might change if it is NULL */
meillo@249 551 route_name = get_optarg(argv, &arg, opt+2);
meillo@290 552 if (!route_name) {
meillo@290 553 fprintf(stderr, "Please do not use -qo without argument anymore; use -q instead.\n");
meillo@290 554 fprintf(stderr, "The behavior for -qo without argument is likely to change.\n");
meillo@290 555 }
meillo@249 556
meillo@249 557 } else if (strncmp(opt, "q", 1) == 0) {
meillo@249 558 /* must be after the `qo' check */
meillo@249 559 gchar *optarg;
meillo@249 560
meillo@251 561 optarg = get_optarg(argv, &arg, opt+1);
meillo@251 562 if (optarg) {
meillo@251 563 /* not just one single queue run but regular runs */
meillo@281 564 set_mode(MODE_DAEMON);
meillo@254 565 queue_interval = time_interval(optarg);
meillo@281 566 } else {
meillo@281 567 set_mode(MODE_RUNQUEUE);
meillo@281 568 do_runq = TRUE;
meillo@249 569 }
meillo@249 570
meillo@249 571 } else if (strcmp(opt, "t") == 0) {
meillo@249 572 opt_t = TRUE;
meillo@249 573
meillo@249 574 } else if (strcmp(opt, "v") == 0) {
meillo@249 575 do_verbose = TRUE;
meillo@249 576
meillo@10 577 } else {
meillo@249 578 fprintf(stderr, "unrecognized option `-%s'\n", opt);
meillo@262 579 exit(1);
meillo@10 580 }
meillo@0 581 }
meillo@0 582
meillo@281 583 if (!mta_mode && arg==argc && !opt_t) {
meillo@278 584 /*
meillo@367 585 ** In this case no rcpts can be found, thus no mail
meillo@367 586 ** can be sent, thus masqmail will always fail. We
meillo@367 587 ** rather do something better instead. This covers
meillo@367 588 ** also the case of calling masqmail without args.
meillo@278 589 */
meillo@281 590 mode_version();
meillo@281 591 exit(0);
meillo@259 592 }
meillo@259 593
meillo@10 594 if (mta_mode == MODE_VERSION) {
meillo@261 595 mode_version();
meillo@262 596 exit(0);
meillo@10 597 }
meillo@0 598
meillo@281 599 if (!mta_mode) {
meillo@281 600 mta_mode = MODE_ACCEPT;
meillo@281 601 }
meillo@281 602
meillo@10 603 /* initialize random generator */
meillo@10 604 srand(time(NULL));
meillo@10 605 /* ignore SIGPIPE signal */
meillo@10 606 signal(SIGPIPE, SIG_IGN);
meillo@0 607
meillo@73 608 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 609 {
meillo@10 610 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 611
meillo@251 612 if (max_fd <= 0) {
meillo@10 613 max_fd = 64;
meillo@251 614 }
meillo@251 615 for (i=3; i<max_fd; i++) {
meillo@10 616 close(i);
meillo@251 617 }
meillo@10 618 }
meillo@0 619
meillo@10 620 init_conf();
meillo@0 621
meillo@367 622 /*
meillo@367 623 ** if we are not privileged, and the config file was changed we
meillo@367 624 ** implicetely set the the run_as_user flag and give up all
meillo@367 625 ** privileges.
meillo@367 626 **
meillo@367 627 ** So it is possible for a user to run his own daemon without
meillo@367 628 ** breaking security.
meillo@367 629 */
meillo@251 630 if ((strcmp(conf_file, CONF_FILE) != 0) && (conf.orig_uid != 0)) {
meillo@251 631 conf.run_as_user = TRUE;
meillo@331 632 set_euidgid(conf.orig_uid, conf.orig_gid, NULL, NULL);
meillo@331 633 if (setgid(conf.orig_gid)) {
meillo@331 634 logwrite(LOG_ALERT, "could not set gid to %d: %s\n", conf.orig_gid, strerror(errno));
meillo@331 635 exit(1);
meillo@331 636 }
meillo@331 637 if (setuid(conf.orig_uid)) {
meillo@331 638 logwrite(LOG_ALERT, "could not set uid to %d: %s\n", conf.orig_uid, strerror(errno));
meillo@331 639 exit(1);
meillo@331 640 }
meillo@10 641 }
meillo@0 642
meillo@155 643 conf.log_dir = LOG_DIR;
meillo@288 644 /* FIXME: fails if we run as user */
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@251 652 if (do_queue) {
meillo@10 653 conf.do_queue = TRUE;
meillo@251 654 }
meillo@251 655 if (do_verbose) {
meillo@10 656 conf.do_verbose = TRUE;
meillo@251 657 }
meillo@251 658 if (debug_level >= 0) { /* if >= 0, it was given by argument */
meillo@10 659 conf.debug_level = debug_level;
meillo@251 660 }
meillo@0 661
meillo@367 662 /*
meillo@367 663 ** It appears that changing to / ensures that we are never in
meillo@367 664 ** a directory which we cannot access. This situation could be
meillo@367 665 ** possible after changing identity.
meillo@367 666 ** Maybe we should only change to / if we not run as user, to
meillo@367 667 ** allow relative paths for log files in test setups for
meillo@367 668 ** instance.
meillo@46 669 */
meillo@10 670 chdir("/");
meillo@0 671
meillo@10 672 if (!conf.run_as_user) {
meillo@10 673 if (setgid(0) != 0) {
meillo@10 674 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
meillo@262 675 exit(1);
meillo@10 676 }
meillo@10 677 if (setuid(0) != 0) {
meillo@10 678 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
meillo@262 679 exit(1);
meillo@10 680 }
meillo@10 681 }
meillo@0 682
meillo@10 683 if (!logopen()) {
meillo@10 684 fprintf(stderr, "could not open log file\n");
meillo@262 685 exit(1);
meillo@10 686 }
meillo@0 687
meillo@10 688 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
meillo@0 689
meillo@10 690 DEBUG(5) {
meillo@10 691 gchar **str = argv;
meillo@10 692 debugf("args: \n");
meillo@10 693 while (*str) {
meillo@10 694 debugf("%s \n", *str);
meillo@10 695 str++;
meillo@10 696 }
meillo@10 697 }
meillo@10 698 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 699
meillo@10 700 if (f_address) {
meillo@10 701 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
meillo@10 702 g_free(f_address);
meillo@10 703 if (!return_path) {
meillo@10 704 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
meillo@262 705 exit(1);
meillo@10 706 }
meillo@10 707 }
meillo@10 708
meillo@10 709 switch (mta_mode) {
meillo@10 710 case MODE_DAEMON:
meillo@10 711 mode_daemon(do_listen, queue_interval, argv);
meillo@10 712 break;
meillo@251 713
meillo@10 714 case MODE_RUNQUEUE:
meillo@251 715 exit(run_queue(do_runq, do_runq_online, route_name) ? 0 : 1);
meillo@10 716 break;
meillo@10 717
meillo@10 718 case MODE_SMTP:
meillo@10 719 mode_smtp();
meillo@10 720 break;
meillo@10 721
meillo@10 722 case MODE_LIST:
meillo@10 723 queue_list();
meillo@10 724 break;
meillo@10 725
meillo@10 726 case MODE_BI:
meillo@262 727 exit(0);
meillo@10 728 break; /* well... */
meillo@10 729
meillo@10 730 case MODE_MCMD:
meillo@250 731 exit(manipulate_queue(M_cmd, &argv[arg]) ? 0 : 1);
meillo@10 732 break;
meillo@10 733
meillo@10 734 case MODE_ACCEPT:
meillo@10 735 {
meillo@268 736 guint accept_flags = (opt_t ? ACC_RCPT_FROM_HEAD : 0)
meillo@110 737 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@10 738 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
meillo@258 739 exit(0);
meillo@10 740 }
meillo@10 741 break;
meillo@251 742
meillo@10 743 default:
meillo@10 744 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 745 break;
meillo@0 746 }
meillo@0 747
meillo@10 748 logclose();
meillo@0 749
meillo@263 750 exit(0);
meillo@0 751 }