masqmail-0.2

annotate src/masqmail.c @ 179:ec3fe72a3e99

Fixed an important bug with folded headers! g_strconcat() returns a *copy* of the string, but hdr->value still pointed to the old header (which probably was a memory leak, too). If the folded part had been quite small it was likely that the new string was at the same position as the old one, thus making everything go well. But if pretty long headers were folded several times it was likely that the new string was allocated somewhere else in memory, thus breaking things. In result mails to lots of recipients (folded header) were frequently only sent to the ones in the first line. Sorry for the inconvenience.
author meillo@marmaro.de
date Fri, 03 Jun 2011 09:52:17 +0200
parents 5ec5e6637049
children b3835b6b834b
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@151 178 logwrite(LOG_NOTICE, "%s %s daemon starting", PACKAGE, VERSION);
meillo@10 179 listen_port(do_listen ? conf.listen_addresses : NULL, queue_interval, argv);
meillo@0 180 }
meillo@0 181
meillo@0 182 #ifdef ENABLE_POP3
meillo@10 183 static void
meillo@10 184 mode_get_daemon(gint get_interval, char *argv[])
meillo@0 185 {
meillo@10 186 guint pid;
meillo@0 187
meillo@10 188 /* daemon */
meillo@10 189 if (!conf.run_as_user) {
meillo@10 190 if ((conf.orig_uid != 0) && (conf.orig_uid != conf.mail_uid)) {
meillo@10 191 fprintf(stderr, "must be root or %s for daemon.\n", DEF_MAIL_USER);
meillo@10 192 exit(EXIT_FAILURE);
meillo@10 193 }
meillo@10 194 }
meillo@0 195
meillo@74 196 /* reparent to init only if init is not already the parent */
meillo@74 197 if (getppid() != 1) {
meillo@74 198 if ((pid = fork()) > 0) {
meillo@74 199 exit(EXIT_SUCCESS);
meillo@74 200 } else if (pid < 0) {
meillo@74 201 logwrite(LOG_ALERT, "could not fork!");
meillo@74 202 exit(EXIT_FAILURE);
meillo@74 203 }
meillo@10 204 }
meillo@0 205
meillo@10 206 signal(SIGTERM, sigterm_handler);
meillo@10 207 write_pidfile(PIDFILEDIR "/masqmail-get.pid");
meillo@0 208
meillo@10 209 conf.do_verbose = FALSE;
meillo@0 210
meillo@72 211 /* closing and reopening the log ensures that it is open afterwards
meillo@72 212 because it is possible that the log is assigned to fd 1 and gets
meillo@72 213 thus closes by fclose(stdout). Similar for the debugfile.
meillo@72 214 */
meillo@72 215 logclose();
meillo@10 216 fclose(stdin);
meillo@10 217 fclose(stdout);
meillo@10 218 fclose(stderr);
meillo@72 219 logopen();
meillo@0 220
meillo@10 221 get_daemon(get_interval, argv);
meillo@0 222 }
meillo@0 223 #endif
meillo@0 224
meillo@0 225 #ifdef ENABLE_SMTP_SERVER
meillo@10 226 static void
meillo@10 227 mode_smtp()
meillo@0 228 {
meillo@10 229 /* accept smtp message on stdin */
meillo@10 230 /* write responses to stderr. */
meillo@0 231
meillo@10 232 struct sockaddr_in saddr;
meillo@10 233 gchar *peername = NULL;
meillo@10 234 int dummy = sizeof(saddr);
meillo@0 235
meillo@10 236 conf.do_verbose = FALSE;
meillo@0 237
meillo@10 238 if (!conf.run_as_user) {
meillo@10 239 seteuid(conf.orig_uid);
meillo@10 240 setegid(conf.orig_gid);
meillo@10 241 }
meillo@0 242
meillo@10 243 DEBUG(5) debugf("accepting smtp message on stdin\n");
meillo@0 244
meillo@10 245 if (getpeername(0, (struct sockaddr *) (&saddr), &dummy) == 0) {
meillo@10 246 peername = g_strdup(inet_ntoa(saddr.sin_addr));
meillo@10 247 } else if (errno != ENOTSOCK)
meillo@10 248 exit(EXIT_FAILURE);
meillo@0 249
meillo@10 250 smtp_in(stdin, stderr, peername, NULL);
meillo@0 251 }
meillo@0 252 #endif
meillo@0 253
meillo@10 254 static void
meillo@10 255 mode_accept(address * return_path, gchar * full_sender_name, guint accept_flags, char **addresses, int addr_cnt)
meillo@0 256 {
meillo@10 257 /* accept message on stdin */
meillo@10 258 accept_error err;
meillo@10 259 message *msg = create_message();
meillo@10 260 gint i;
meillo@0 261
meillo@83 262 if (return_path && !is_privileged_user(conf.orig_uid)) {
meillo@96 263 fprintf(stderr, "must be root, %s or in group %s for setting return path.\n", DEF_MAIL_USER, DEF_MAIL_GROUP);
meillo@83 264 exit(EXIT_FAILURE);
meillo@10 265 }
meillo@0 266
meillo@10 267 if (!conf.run_as_user) {
meillo@10 268 seteuid(conf.orig_uid);
meillo@10 269 setegid(conf.orig_gid);
meillo@10 270 }
meillo@0 271
meillo@10 272 DEBUG(5) debugf("accepting message on stdin\n");
meillo@0 273
meillo@10 274 msg->received_prot = PROT_LOCAL;
meillo@10 275 for (i = 0; i < addr_cnt; i++) {
meillo@10 276 if (addresses[i][0] != '|')
meillo@10 277 msg->rcpt_list = g_list_append(msg->rcpt_list, create_address_qualified(addresses[i], TRUE, conf.host_name));
meillo@10 278 else {
meillo@10 279 logwrite(LOG_ALERT, "no pipe allowed as recipient address: %s\n", addresses[i]);
meillo@10 280 exit(EXIT_FAILURE);
meillo@10 281 }
meillo@10 282 }
meillo@0 283
meillo@10 284 /* -f option */
meillo@10 285 msg->return_path = return_path;
meillo@0 286
meillo@10 287 /* -F option */
meillo@10 288 msg->full_sender_name = full_sender_name;
meillo@0 289
meillo@10 290 if ((err = accept_message(stdin, msg, accept_flags)) == AERR_OK) {
meillo@10 291 if (spool_write(msg, TRUE)) {
meillo@10 292 pid_t pid;
meillo@10 293 logwrite(LOG_NOTICE, "%s <= %s with %s\n", msg->uid, addr_string(msg->return_path), prot_names[PROT_LOCAL]);
meillo@0 294
meillo@10 295 if (!conf.do_queue) {
meillo@10 296 if ((pid = fork()) == 0) {
meillo@10 297 conf.do_verbose = FALSE;
meillo@10 298 fclose(stdin);
meillo@10 299 fclose(stdout);
meillo@10 300 fclose(stderr);
meillo@10 301 if (deliver(msg)) {
meillo@10 302 exit(EXIT_SUCCESS);
meillo@10 303 } else
meillo@10 304 exit(EXIT_FAILURE);
meillo@10 305 } else if (pid < 0) {
meillo@10 306 logwrite(LOG_ALERT, "could not fork for delivery, id = %s", msg->uid);
meillo@10 307 }
meillo@10 308 }
meillo@10 309 } else {
meillo@10 310 fprintf(stderr, "Could not write spool file\n");
meillo@10 311 exit(EXIT_FAILURE);
meillo@10 312 }
meillo@10 313 } else {
meillo@10 314 switch (err) {
meillo@10 315 case AERR_EOF:
meillo@10 316 fprintf(stderr, "unexpected EOF.\n");
meillo@10 317 exit(EXIT_FAILURE);
meillo@10 318 case AERR_NORCPT:
meillo@10 319 fprintf(stderr, "no recipients.\n");
meillo@10 320 exit(EXIT_FAILURE);
meillo@117 321 case AERR_SIZE:
meillo@117 322 fprintf(stderr, "max message size exceeded.\n");
meillo@117 323 exit(EXIT_FAILURE);
meillo@10 324 default:
meillo@10 325 /* should never happen: */
meillo@10 326 fprintf(stderr, "Unknown error (%d)\r\n", err);
meillo@10 327 exit(EXIT_FAILURE);
meillo@10 328 }
meillo@10 329 exit(EXIT_FAILURE);
meillo@0 330 }
meillo@0 331 }
meillo@0 332
meillo@0 333 int
meillo@0 334 main(int argc, char *argv[])
meillo@0 335 {
meillo@10 336 /* cmd line flags */
meillo@10 337 gchar *conf_file = CONF_FILE;
meillo@10 338 gint arg = 1;
meillo@10 339 gboolean do_get = FALSE;
meillo@10 340 gboolean do_get_online = FALSE;
meillo@0 341
meillo@10 342 gboolean do_listen = FALSE;
meillo@10 343 gboolean do_runq = FALSE;
meillo@10 344 gboolean do_runq_online = FALSE;
meillo@0 345
meillo@10 346 gboolean do_queue = FALSE;
meillo@0 347
meillo@10 348 gboolean do_verbose = FALSE;
meillo@10 349 gint debug_level = -1;
meillo@0 350
meillo@10 351 mta_mode mta_mode = MODE_ACCEPT;
meillo@0 352
meillo@10 353 gint queue_interval = 0;
meillo@10 354 gint get_interval = 0;
meillo@10 355 gboolean opt_t = FALSE;
meillo@10 356 gboolean opt_i = FALSE;
meillo@10 357 gboolean opt_odb = FALSE;
meillo@10 358 gboolean opt_oem = FALSE;
meillo@10 359 gboolean exit_failure = FALSE;
meillo@0 360
meillo@10 361 gchar *M_cmd = NULL;
meillo@0 362
meillo@10 363 gint exit_code = EXIT_SUCCESS;
meillo@10 364 gchar *route_name = NULL;
meillo@10 365 gchar *get_name = NULL;
meillo@10 366 gchar *progname;
meillo@10 367 gchar *f_address = NULL;
meillo@10 368 gchar *full_sender_name = NULL;
meillo@10 369 address *return_path = NULL; /* may be changed by -f option */
meillo@0 370
meillo@10 371 progname = get_progname(argv[0]);
meillo@0 372
meillo@10 373 if (strcmp(progname, "mailq") == 0) {
meillo@10 374 mta_mode = MODE_LIST;
meillo@10 375 } else if (strcmp(progname, "mailrm") == 0) {
meillo@10 376 mta_mode = MODE_MCMD;
meillo@10 377 M_cmd = "rm";
meillo@10 378 } else if (strcmp(progname, "runq") == 0) {
meillo@10 379 mta_mode = MODE_RUNQUEUE;
meillo@10 380 do_runq = TRUE;
meillo@10 381 } else if (strcmp(progname, "rmail") == 0) {
meillo@89 382 /* the `rmail' alias should probably be removed now
meillo@89 383 that we have the rmail script. But let's keep it
meillo@89 384 for some while for compatibility. 2010-06-19 */
meillo@10 385 mta_mode = MODE_ACCEPT;
meillo@10 386 opt_i = TRUE;
meillo@10 387 } else if (strcmp(progname, "smtpd") == 0 || strcmp(progname, "in.smtpd") == 0) {
meillo@10 388 mta_mode = MODE_SMTP;
meillo@10 389 }
meillo@0 390
meillo@10 391 /* parse cmd line */
meillo@10 392 while (arg < argc) {
meillo@10 393 gint pos = 0;
meillo@10 394 if ((argv[arg][pos] == '-') && (argv[arg][pos + 1] != '-')) {
meillo@10 395 pos++;
meillo@10 396 switch (argv[arg][pos++]) {
meillo@10 397 case 'b':
meillo@10 398 switch (argv[arg][pos++]) {
meillo@10 399 case 'd':
meillo@10 400 do_listen = TRUE;
meillo@10 401 mta_mode = MODE_DAEMON;
meillo@10 402 break;
meillo@10 403 case 'i':
meillo@10 404 /* ignored */
meillo@10 405 mta_mode = MODE_BI;
meillo@10 406 break;
meillo@10 407 case 's':
meillo@10 408 mta_mode = MODE_SMTP;
meillo@10 409 break;
meillo@10 410 case 'p':
meillo@10 411 mta_mode = MODE_LIST;
meillo@10 412 break;
meillo@10 413 case 'V':
meillo@10 414 mta_mode = MODE_VERSION;
meillo@10 415 break;
meillo@10 416 default:
meillo@10 417 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 418 exit(EXIT_FAILURE);
meillo@10 419 }
meillo@10 420 break;
meillo@10 421 case 'B':
meillo@10 422 /* we ignore this and throw the argument away */
meillo@10 423 get_optarg(argv, argc, &arg, &pos);
meillo@10 424 break;
meillo@10 425 case 'C':
meillo@10 426 if (!(conf_file = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 427 fprintf(stderr, "-C requires a filename as argument.\n");
meillo@10 428 exit(EXIT_FAILURE);
meillo@10 429 }
meillo@10 430 break;
meillo@10 431 case 'F':
meillo@10 432 {
meillo@10 433 full_sender_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 434 if (!full_sender_name) {
meillo@10 435 fprintf(stderr, "-F requires a name as an argument\n");
meillo@10 436 exit(EXIT_FAILURE);
meillo@10 437 }
meillo@10 438 }
meillo@10 439 break;
meillo@10 440 case 'd':
meillo@10 441 if (getuid() == 0) {
meillo@10 442 char *lvl = get_optarg(argv, argc, &arg, &pos);
meillo@10 443 if (lvl)
meillo@10 444 debug_level = atoi(lvl);
meillo@10 445 else {
meillo@10 446 fprintf(stderr, "-d requires a number as an argument.\n");
meillo@10 447 exit(EXIT_FAILURE);
meillo@10 448 }
meillo@10 449 } else {
meillo@10 450 fprintf(stderr, "only root may set the debug level.\n");
meillo@10 451 exit(EXIT_FAILURE);
meillo@10 452 }
meillo@10 453 break;
meillo@10 454 case 'f':
meillo@10 455 /* set return path */
meillo@10 456 {
meillo@10 457 gchar *address;
meillo@10 458 address = get_optarg(argv, argc, &arg, &pos);
meillo@10 459 if (address) {
meillo@10 460 f_address = g_strdup(address);
meillo@10 461 } else {
meillo@10 462 fprintf(stderr, "-f requires an address as an argument\n");
meillo@10 463 exit(EXIT_FAILURE);
meillo@10 464 }
meillo@10 465 }
meillo@10 466 break;
meillo@10 467 case 'g':
meillo@10 468 do_get = TRUE;
meillo@10 469 if (!mta_mode)
meillo@10 470 mta_mode = MODE_NONE; /* to prevent default MODE_ACCEPT */
meillo@10 471 if (argv[arg][pos] == 'o') {
meillo@10 472 pos++;
meillo@10 473 do_get_online = TRUE;
meillo@10 474 /* can be NULL, then we use online detection method */
meillo@10 475 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 476
meillo@10 477 if (route_name != NULL) {
meillo@10 478 if (isdigit(route_name[0])) {
meillo@10 479 get_interval = time_interval(route_name, &pos);
meillo@10 480 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 481 mta_mode = MODE_GET_DAEMON;
meillo@10 482 do_get = FALSE;
meillo@10 483 }
meillo@10 484 }
meillo@10 485 } else {
meillo@10 486 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 487 get_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 488 }
meillo@10 489 }
meillo@10 490 break;
meillo@10 491 case 'i':
meillo@10 492 if (argv[arg][pos] == 0) {
meillo@10 493 opt_i = TRUE;
meillo@10 494 exit_failure = FALSE; /* may override -oem */
meillo@10 495 } else {
meillo@10 496 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 497 exit(EXIT_FAILURE);
meillo@10 498 }
meillo@10 499 break;
meillo@10 500 case 'M':
meillo@10 501 {
meillo@10 502 mta_mode = MODE_MCMD;
meillo@10 503 M_cmd = g_strdup(&(argv[arg][pos]));
meillo@10 504 }
meillo@10 505 break;
meillo@10 506 case 'o':
meillo@10 507 switch (argv[arg][pos++]) {
meillo@10 508 case 'e':
meillo@10 509 if (argv[arg][pos++] == 'm') /* -oem */
meillo@10 510 if (!opt_i)
meillo@10 511 exit_failure = TRUE;
meillo@10 512 opt_oem = TRUE;
meillo@10 513 break;
meillo@10 514 case 'd':
meillo@10 515 if (argv[arg][pos] == 'b') /* -odb */
meillo@10 516 opt_odb = TRUE;
meillo@10 517 else if (argv[arg][pos] == 'q') /* -odq */
meillo@10 518 do_queue = TRUE;
meillo@10 519 break;
meillo@10 520 case 'i':
meillo@10 521 opt_i = TRUE;
meillo@10 522 exit_failure = FALSE; /* may override -oem */
meillo@10 523 break;
meillo@10 524 }
meillo@10 525 break;
meillo@10 526
meillo@10 527 case 'q':
meillo@10 528 {
meillo@10 529 gchar *optarg;
meillo@10 530
meillo@10 531 do_runq = TRUE;
meillo@10 532 mta_mode = MODE_RUNQUEUE;
meillo@10 533 if (argv[arg][pos] == 'o') {
meillo@10 534 pos++;
meillo@10 535 do_runq = FALSE;
meillo@10 536 do_runq_online = TRUE;
meillo@10 537 /* can be NULL, then we use online detection method */
meillo@10 538 route_name = get_optarg(argv, argc, &arg, &pos);
meillo@10 539 } else
meillo@10 540 if ((optarg = get_optarg(argv, argc, &arg, &pos))) {
meillo@10 541 mta_mode = MODE_DAEMON;
meillo@10 542 queue_interval = time_interval(optarg, &pos);
meillo@10 543 }
meillo@10 544 }
meillo@10 545 break;
meillo@10 546 case 't':
meillo@10 547 if (argv[arg][pos] == 0) {
meillo@10 548 opt_t = TRUE;
meillo@10 549 } else {
meillo@10 550 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 551 exit(EXIT_FAILURE);
meillo@10 552 }
meillo@10 553 break;
meillo@10 554 case 'v':
meillo@10 555 do_verbose = TRUE;
meillo@10 556 break;
meillo@10 557 default:
meillo@10 558 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 559 exit(EXIT_FAILURE);
meillo@10 560 }
meillo@10 561 } else {
meillo@10 562 if (argv[arg][pos + 1] == '-') {
meillo@10 563 if (argv[arg][pos + 2] != '\0') {
meillo@10 564 fprintf(stderr, "unrecognized option '%s'\n", argv[arg]);
meillo@10 565 exit(EXIT_FAILURE);
meillo@10 566 }
meillo@10 567 arg++;
meillo@10 568 }
meillo@10 569 break;
meillo@10 570 }
meillo@10 571 arg++;
meillo@0 572 }
meillo@0 573
meillo@10 574 if (mta_mode == MODE_VERSION) {
meillo@12 575 gchar *with_resolver = "";
meillo@12 576 gchar *with_smtp_server = "";
meillo@12 577 gchar *with_pop3 = "";
meillo@12 578 gchar *with_auth = "";
meillo@12 579 gchar *with_maildir = "";
meillo@12 580 gchar *with_ident = "";
meillo@12 581 gchar *with_mserver = "";
meillo@0 582
meillo@0 583 #ifdef ENABLE_RESOLVER
meillo@10 584 with_resolver = " +resolver";
meillo@0 585 #endif
meillo@0 586 #ifdef ENABLE_SMTP_SERVER
meillo@10 587 with_smtp_server = " +smtp-server";
meillo@0 588 #endif
meillo@0 589 #ifdef ENABLE_POP3
meillo@10 590 with_pop3 = " +pop3";
meillo@0 591 #endif
meillo@0 592 #ifdef ENABLE_AUTH
meillo@10 593 with_auth = " +auth";
meillo@0 594 #endif
meillo@0 595 #ifdef ENABLE_MAILDIR
meillo@10 596 with_maildir = " +maildir";
meillo@0 597 #endif
meillo@0 598 #ifdef ENABLE_IDENT
meillo@10 599 with_ident = " +ident";
meillo@0 600 #endif
meillo@0 601 #ifdef ENABLE_MSERVER
meillo@10 602 with_mserver = " +mserver";
meillo@0 603 #endif
meillo@0 604
meillo@10 605 printf("%s %s%s%s%s%s%s%s%s\n", PACKAGE, VERSION, with_resolver, with_smtp_server,
meillo@10 606 with_pop3, with_auth, with_maildir, with_ident, with_mserver);
meillo@0 607
meillo@10 608 exit(EXIT_SUCCESS);
meillo@10 609 }
meillo@0 610
meillo@10 611 /* initialize random generator */
meillo@10 612 srand(time(NULL));
meillo@10 613 /* ignore SIGPIPE signal */
meillo@10 614 signal(SIGPIPE, SIG_IGN);
meillo@0 615
meillo@73 616 /* close all possibly open file descriptors, except std{in,out,err} */
meillo@10 617 {
meillo@10 618 int i, max_fd = sysconf(_SC_OPEN_MAX);
meillo@0 619
meillo@10 620 if (max_fd <= 0)
meillo@10 621 max_fd = 64;
meillo@10 622 for (i = 3; i < max_fd; i++)
meillo@10 623 close(i);
meillo@10 624 }
meillo@0 625
meillo@10 626 init_conf();
meillo@0 627
meillo@10 628 /* if we are not privileged, and the config file was changed we
meillo@10 629 implicetely set the the run_as_user flag and give up all
meillo@10 630 privileges.
meillo@0 631
meillo@10 632 So it is possible for a user to run his own daemon without
meillo@10 633 breaking security.
meillo@10 634 */
meillo@10 635 if (strcmp(conf_file, CONF_FILE) != 0) {
meillo@10 636 if (conf.orig_uid != 0) {
meillo@10 637 conf.run_as_user = TRUE;
meillo@10 638 seteuid(conf.orig_uid);
meillo@10 639 setegid(conf.orig_gid);
meillo@10 640 setuid(conf.orig_uid);
meillo@10 641 setgid(conf.orig_gid);
meillo@10 642 }
meillo@10 643 }
meillo@0 644
meillo@10 645 read_conf(conf_file);
meillo@0 646
meillo@10 647 if (do_queue)
meillo@10 648 conf.do_queue = TRUE;
meillo@10 649 if (do_verbose)
meillo@10 650 conf.do_verbose = TRUE;
meillo@10 651 if (debug_level >= 0) /* if >= 0, it was given by argument */
meillo@10 652 conf.debug_level = debug_level;
meillo@0 653
meillo@46 654 /* It appears that changing to / ensures that we are never in
meillo@46 655 a directory which we cannot access. This situation could be
meillo@46 656 possible after changing identity.
meillo@46 657 Maybe we should only change to / if we not run as user, to
meillo@46 658 allow relative paths for log files in test setups for
meillo@46 659 instance.
meillo@46 660 */
meillo@10 661 chdir("/");
meillo@0 662
meillo@10 663 if (!conf.run_as_user) {
meillo@10 664 if (setgid(0) != 0) {
meillo@10 665 fprintf(stderr, "could not set gid to 0. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 666 exit(EXIT_FAILURE);
meillo@10 667 }
meillo@10 668 if (setuid(0) != 0) {
meillo@10 669 fprintf(stderr, "could not gain root privileges. Is the setuid bit set? : %s\n", strerror(errno));
meillo@10 670 exit(EXIT_FAILURE);
meillo@10 671 }
meillo@10 672 }
meillo@0 673
meillo@10 674 if (!logopen()) {
meillo@10 675 fprintf(stderr, "could not open log file\n");
meillo@10 676 exit(EXIT_FAILURE);
meillo@10 677 }
meillo@0 678
meillo@10 679 DEBUG(1) debugf("masqmail %s starting\n", VERSION);
meillo@0 680
meillo@10 681 DEBUG(5) {
meillo@10 682 gchar **str = argv;
meillo@10 683 debugf("args: \n");
meillo@10 684 while (*str) {
meillo@10 685 debugf("%s \n", *str);
meillo@10 686 str++;
meillo@10 687 }
meillo@10 688 }
meillo@10 689 DEBUG(5) debugf("queue_interval = %d\n", queue_interval);
meillo@0 690
meillo@10 691 if (f_address) {
meillo@10 692 return_path = create_address_qualified(f_address, TRUE, conf.host_name);
meillo@10 693 g_free(f_address);
meillo@10 694 if (!return_path) {
meillo@10 695 fprintf(stderr, "invalid RFC821 address: %s\n", f_address);
meillo@10 696 exit(EXIT_FAILURE);
meillo@10 697 }
meillo@10 698 }
meillo@10 699
meillo@10 700 if (do_get) {
meillo@0 701 #ifdef ENABLE_POP3
meillo@10 702 if ((mta_mode == MODE_NONE) || (mta_mode == MODE_RUNQUEUE)) {
meillo@10 703 set_identity(conf.orig_uid, "getting mail");
meillo@10 704 if (do_get_online) {
meillo@10 705 if (route_name != NULL) {
meillo@10 706 conf.online_detect = g_strdup("argument");
meillo@10 707 set_online_name(route_name);
meillo@10 708 }
meillo@10 709 get_online();
meillo@10 710 } else {
meillo@10 711 if (get_name)
meillo@10 712 get_from_name(get_name);
meillo@10 713 else
meillo@10 714 get_all();
meillo@10 715 }
meillo@10 716 } else {
meillo@10 717 logwrite(LOG_ALERT, "get (-g) only allowed alone or together with queue run (-q)\n");
meillo@10 718 }
meillo@10 719 #else
meillo@10 720 fprintf(stderr, "get (pop) support not compiled in\n");
meillo@10 721 #endif
meillo@10 722 }
meillo@0 723
meillo@10 724 switch (mta_mode) {
meillo@10 725 case MODE_DAEMON:
meillo@10 726 mode_daemon(do_listen, queue_interval, argv);
meillo@10 727 break;
meillo@10 728 case MODE_RUNQUEUE:
meillo@10 729 {
meillo@10 730 /* queue runs */
meillo@10 731 set_identity(conf.orig_uid, "queue run");
meillo@0 732
meillo@10 733 if (do_runq)
meillo@10 734 exit_code = queue_run() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 735
meillo@10 736 if (do_runq_online) {
meillo@10 737 if (route_name != NULL) {
meillo@10 738 conf.online_detect = g_strdup("argument");
meillo@10 739 set_online_name(route_name);
meillo@10 740 }
meillo@10 741 exit_code =
meillo@10 742 queue_run_online() ? EXIT_SUCCESS : EXIT_FAILURE;
meillo@10 743 }
meillo@10 744 }
meillo@10 745 break;
meillo@10 746 case MODE_GET_DAEMON:
meillo@10 747 #ifdef ENABLE_POP3
meillo@10 748 if (route_name != NULL) {
meillo@10 749 conf.online_detect = g_strdup("argument");
meillo@10 750 set_online_name(route_name);
meillo@10 751 }
meillo@10 752 mode_get_daemon(get_interval, argv);
meillo@10 753 #endif
meillo@10 754 break;
meillo@10 755
meillo@10 756 case MODE_SMTP:
meillo@10 757 #ifdef ENABLE_SMTP_SERVER
meillo@10 758 mode_smtp();
meillo@10 759 #else
meillo@10 760 fprintf(stderr, "smtp server support not compiled in\n");
meillo@10 761 #endif
meillo@10 762 break;
meillo@10 763
meillo@10 764 case MODE_LIST:
meillo@10 765 queue_list();
meillo@10 766 break;
meillo@10 767
meillo@10 768 case MODE_BI:
meillo@10 769 exit(EXIT_SUCCESS);
meillo@10 770 break; /* well... */
meillo@10 771
meillo@10 772 case MODE_MCMD:
meillo@10 773 if (strcmp(M_cmd, "rm") == 0) {
meillo@10 774 gboolean ok = FALSE;
meillo@10 775
meillo@10 776 set_euidgid(conf.mail_uid, conf.mail_gid, NULL, NULL);
meillo@10 777
meillo@10 778 if (is_privileged_user(conf.orig_uid)) {
meillo@10 779 for (; arg < argc; arg++) {
meillo@10 780 if (queue_delete(argv[arg]))
meillo@10 781 ok = TRUE;
meillo@10 782 }
meillo@10 783 } else {
meillo@10 784 struct passwd *pw = getpwuid(conf.orig_uid);
meillo@10 785 if (pw) {
meillo@10 786 for (; arg < argc; arg++) {
meillo@10 787 message *msg = msg_spool_read(argv[arg], FALSE);
meillo@10 788 #ifdef ENABLE_IDENT
meillo@10 789 if (((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL))
meillo@10 790 || is_in_netlist(msg->received_host, conf.ident_trusted_nets)) {
meillo@10 791 #else
meillo@10 792 if ((msg->received_host == NULL) && (msg->received_prot == PROT_LOCAL)) {
meillo@10 793 #endif
meillo@10 794 if (msg->ident) {
meillo@10 795 if (strcmp(pw->pw_name, msg->ident) == 0) {
meillo@10 796 if (queue_delete(argv[arg]))
meillo@10 797 ok = TRUE;
meillo@10 798 } else {
meillo@10 799 fprintf(stderr, "you do not own message id %s\n", argv[arg]);
meillo@10 800 }
meillo@10 801 } else
meillo@10 802 fprintf(stderr, "message %s does not have an ident.\n", argv[arg]);
meillo@10 803 } else {
meillo@10 804 fprintf(stderr, "message %s was not received locally or from a trusted network.\n", argv[arg]);
meillo@10 805 }
meillo@10 806 }
meillo@10 807 } else {
meillo@10 808 fprintf(stderr, "could not find a passwd entry for uid %d: %s\n", conf.orig_uid, strerror(errno));
meillo@10 809 }
meillo@10 810 }
meillo@10 811 exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
meillo@10 812 } else {
meillo@10 813 fprintf(stderr, "unknown command %s\n", M_cmd);
meillo@10 814 exit(EXIT_FAILURE);
meillo@10 815 }
meillo@10 816 break;
meillo@10 817
meillo@10 818 case MODE_ACCEPT:
meillo@10 819 {
meillo@107 820 guint accept_flags = (opt_t ? ACC_DEL_RCPTS | ACC_RCPT_FROM_HEAD : 0)
meillo@110 821 | (opt_i ? ACC_DOT_IGNORE : ACC_NODOT_RELAX);
meillo@10 822 mode_accept(return_path, full_sender_name, accept_flags, &(argv[arg]), argc - arg);
meillo@10 823 exit(exit_failure ? EXIT_FAILURE : EXIT_SUCCESS);
meillo@10 824 }
meillo@10 825 break;
meillo@10 826 case MODE_NONE:
meillo@10 827 break;
meillo@10 828 default:
meillo@10 829 fprintf(stderr, "unknown mode: %d\n", mta_mode);
meillo@10 830 break;
meillo@0 831 }
meillo@0 832
meillo@10 833 logclose();
meillo@0 834
meillo@10 835 exit(exit_code);
meillo@0 836 }