masqmail

annotate src/masqmail.c @ 75:257a9e6d1a8e

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