masqmail

annotate src/get.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 26e34ae9a3e3
children dc89737b27aa
rev   line source
meillo@0 1 /* MasqMail
meillo@0 2 Copyright (C) 2000-2002 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 <sys/wait.h>
meillo@0 20 #include <sys/file.h>
meillo@0 21 #include <sys/types.h>
meillo@0 22
meillo@0 23 #include "masqmail.h"
meillo@0 24 #include "pop3_in.h"
meillo@0 25
meillo@0 26 #ifdef ENABLE_POP3
meillo@0 27
meillo@0 28 static int volatile sighup_seen = 0;
meillo@0 29
meillo@10 30 static void
meillo@10 31 sighup_handler(int sig)
meillo@0 32 {
meillo@10 33 sighup_seen = 1;
meillo@10 34 signal(SIGHUP, sighup_handler);
meillo@0 35 }
meillo@0 36
meillo@10 37 static void
meillo@10 38 sigchld_handler(int sig)
meillo@0 39 {
meillo@10 40 pid_t pid;
meillo@10 41 int status;
meillo@10 42
meillo@10 43 pid = waitpid(0, &status, 0);
meillo@10 44 if (pid > 0) {
meillo@10 45 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 46 logwrite(LOG_WARNING, "process %d exited with %d\n", pid, WEXITSTATUS(status));
meillo@10 47 if (WIFSIGNALED(status))
meillo@10 48 logwrite(LOG_WARNING, "process with pid %d got signal: %d\n", pid, WTERMSIG(status));
meillo@10 49 }
meillo@10 50 signal(SIGCHLD, sigchld_handler);
meillo@0 51 }
meillo@0 52
meillo@10 53 static int
meillo@10 54 get_lock(get_conf * gc)
meillo@0 55 {
meillo@0 56 #ifdef USE_DOTLOCK
meillo@10 57 gboolean ok = FALSE;
meillo@10 58 gchar *hitch_name;
meillo@10 59 gchar *lock_name;
meillo@0 60
meillo@10 61 /* the name of the lock is constructed from the user
meillo@10 62 and the server name, to prevent more than one connection at the same time
meillo@10 63 to the same server and the same user. This way concurrent connections
meillo@10 64 are possible to different servers or different users */
meillo@15 65 hitch_name = g_strdup_printf("%s/masqmail-get-%s@%s-%d.lock",
meillo@15 66 conf.lock_dir, gc->login_user, gc->server_name, getpid());
meillo@15 67 lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
meillo@15 68 conf.lock_dir, gc->login_user, gc->server_name);
meillo@0 69
meillo@10 70 ok = dot_lock(lock_name, hitch_name);
meillo@10 71 if (!ok)
meillo@10 72 logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);
meillo@0 73
meillo@10 74 g_free(lock_name);
meillo@10 75 g_free(hitch_name);
meillo@10 76
meillo@10 77 return ok;
meillo@0 78 #else
meillo@10 79 gchar *lock_name;
meillo@10 80 int fd;
meillo@0 81
meillo@10 82 lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock", conf.lock_dir, gc->login_user, gc->server_name);
meillo@0 83
meillo@10 84 if ((fd = open(lock_name, O_WRONLY | O_NDELAY | O_APPEND | O_CREAT, 0600)) >= 0) {
meillo@10 85 if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
meillo@10 86 close(fd);
meillo@10 87 logwrite(LOG_WARNING, "getting mail for %s@%s is locked\n", gc->login_user, gc->server_name);
meillo@10 88 fd = -1;
meillo@10 89 }
meillo@10 90 } else
meillo@10 91 logwrite(LOG_WARNING, "could not open lock %s: %s\n", lock_name, strerror(errno));
meillo@0 92
meillo@10 93 g_free(lock_name);
meillo@0 94
meillo@10 95 return fd;
meillo@0 96 #endif
meillo@0 97 }
meillo@0 98
meillo@0 99 #ifdef USE_DOTLOCK
meillo@10 100 static gboolean
meillo@10 101 get_unlock(get_conf * gc)
meillo@0 102 {
meillo@15 103 gchar *lock_name lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
meillo@15 104 conf.lock_dir, gc->login_user, gc->server_name);
meillo@0 105
meillo@10 106 dot_unlock(lock_name);
meillo@10 107 g_free(lock_name);
meillo@0 108
meillo@10 109 return TRUE;
meillo@0 110 }
meillo@0 111 #else
meillo@10 112 static void
meillo@10 113 get_unlock(get_conf * gc, int fd)
meillo@0 114 {
meillo@15 115 gchar *lock_name = g_strdup_printf("%s/masqmail-get-%s@%s.lock",
meillo@15 116 conf.lock_dir, gc->login_user, gc->server_name);
meillo@0 117
meillo@10 118 flock(fd, LOCK_UN);
meillo@10 119 close(fd);
meillo@0 120
meillo@10 121 unlink(lock_name);
meillo@10 122 g_free(lock_name);
meillo@0 123 }
meillo@0 124 #endif
meillo@0 125
meillo@10 126 gboolean
meillo@10 127 get_from_file(gchar * fname)
meillo@0 128 {
meillo@10 129 guint flags = 0;
meillo@10 130 get_conf *gc = read_get_conf(fname);
meillo@10 131 gboolean ok = TRUE;
meillo@10 132 int lock;
meillo@0 133
meillo@10 134 if (gc) {
meillo@10 135 if (!gc->do_keep)
meillo@10 136 flags |= POP3_FLAG_DELETE;
meillo@10 137 if (gc->do_uidl)
meillo@10 138 flags |= POP3_FLAG_UIDL;
meillo@10 139 if (gc->do_uidl_dele)
meillo@10 140 flags |= POP3_FLAG_UIDL_DELE;
meillo@0 141
meillo@10 142 if (!(gc->server_name)) {
meillo@10 143 logwrite(LOG_ALERT, "no server name given in %s\n", fname);
meillo@10 144 return FALSE;
meillo@10 145 }
meillo@10 146 if (!(gc->address)) {
meillo@10 147 logwrite(LOG_ALERT, "no address given in %s\n", fname);
meillo@10 148 return FALSE;
meillo@10 149 }
meillo@10 150 if (!(gc->login_user)) {
meillo@10 151 logwrite(LOG_ALERT, "no user name given in %s\n", fname);
meillo@10 152 return FALSE;
meillo@10 153 }
meillo@10 154 if (!(gc->login_pass)) {
meillo@10 155 logwrite(LOG_ALERT, "no password given in %s\n", fname);
meillo@10 156 return FALSE;
meillo@10 157 }
meillo@0 158
meillo@10 159 DEBUG(3) debugf("flags = %d\n", flags);
meillo@10 160
meillo@10 161 if ((strcmp(gc->protocol, "pop3") == 0) || (strcmp(gc->protocol, "apop") == 0)) {
meillo@10 162 pop3_base *popb = NULL;
meillo@10 163
meillo@10 164 if (strcmp(gc->protocol, "apop") == 0) {
meillo@10 165 flags |= POP3_FLAG_APOP;
meillo@10 166 DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with apop\n",
meillo@10 167 gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
meillo@10 168 } else {
meillo@10 169 DEBUG(3) debugf("attempting to get mail for user %s at host %s for %s@%s with pop3\n",
meillo@10 170 gc->login_user, gc->server_name, gc->address->local_part, gc->address->domain);
meillo@10 171 }
meillo@0 172 #ifdef USE_DOTLOCK
meillo@10 173 if ((lock = get_lock(gc))) {
meillo@0 174 #else
meillo@10 175 if ((lock = get_lock(gc)) >= 0) {
meillo@0 176 #endif
meillo@10 177 if (gc->wrapper) {
meillo@10 178 popb = pop3_in_open_child(gc->wrapper, flags);
meillo@10 179 /* quick hack */
meillo@10 180 popb->remote_host = gc->server_name;
meillo@10 181 } else {
meillo@10 182 popb = pop3_in_open(gc->server_name, gc->server_port, gc->resolve_list, flags);
meillo@10 183 }
meillo@10 184 if (popb) {
meillo@10 185 ok = pop3_get(popb, gc->login_user, gc->login_pass, gc->address, gc->return_path,
meillo@10 186 gc->max_count, gc->max_size, gc->max_size_delete);
meillo@10 187 pop3_in_close(popb);
meillo@10 188 } else {
meillo@10 189 ok = FALSE;
meillo@10 190 logwrite(LOG_ALERT, "failed to connect to host %s\n", gc->server_name);
meillo@10 191 }
meillo@10 192 #ifdef USE_DOTLOCK
meillo@10 193 get_unlock(gc);
meillo@10 194 #else
meillo@10 195 get_unlock(gc, lock);
meillo@10 196 #endif
meillo@10 197 }
meillo@10 198 } else {
meillo@10 199 logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
meillo@10 200 ok = FALSE;
meillo@10 201 }
meillo@10 202
meillo@10 203 destroy_get_conf(gc);
meillo@0 204 }
meillo@10 205 return ok;
meillo@0 206 }
meillo@0 207
meillo@10 208 gboolean
meillo@10 209 get_from_name(gchar * name)
meillo@0 210 {
meillo@10 211 gchar *fname = (gchar *) table_find(conf.get_names, name);
meillo@10 212 if (fname)
meillo@10 213 return get_from_file(fname);
meillo@10 214 return FALSE;
meillo@0 215 }
meillo@0 216
meillo@10 217 gboolean
meillo@10 218 get_all()
meillo@0 219 {
meillo@10 220 GList *get_table = conf.get_names;
meillo@10 221 GList *get_node;
meillo@10 222 void (*old_signal) (int);
meillo@0 223
meillo@10 224 old_signal = signal(SIGCHLD, SIG_DFL);
meillo@0 225
meillo@10 226 foreach(get_table, get_node) {
meillo@10 227 table_pair *pair = (table_pair *) (get_node->data);
meillo@10 228 gchar *fname = (gchar *) pair->value;
meillo@10 229 pid_t pid;
meillo@0 230
meillo@10 231 pid = fork();
meillo@10 232 if (pid == 0) {
meillo@10 233 signal(SIGCHLD, old_signal);
meillo@10 234 exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
meillo@10 235 } else if (pid > 0) {
meillo@10 236 int status;
meillo@10 237 waitpid(pid, &status, 0);
meillo@10 238 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 239 logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
meillo@10 240 if (WIFSIGNALED(status))
meillo@10 241 logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
meillo@10 242 } else
meillo@10 243 logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
meillo@10 244 }
meillo@0 245
meillo@10 246 signal(SIGCHLD, old_signal);
meillo@10 247
meillo@10 248 return TRUE;
meillo@0 249 }
meillo@0 250
meillo@10 251 void
meillo@10 252 get_online()
meillo@0 253 {
meillo@10 254 GList *gf_list = NULL;
meillo@10 255 gchar *connect_name = detect_online();
meillo@0 256
meillo@10 257 if (connect_name != NULL) {
meillo@10 258 void (*old_signal) (int);
meillo@0 259
meillo@10 260 old_signal = signal(SIGCHLD, SIG_DFL);
meillo@0 261
meillo@10 262 logwrite(LOG_NOTICE, "detected online configuration %s\n", connect_name);
meillo@10 263 /* we are online! */
meillo@10 264 gf_list = (GList *) table_find(conf.online_gets, connect_name);
meillo@10 265 if (gf_list != NULL) {
meillo@10 266 GList *node;
meillo@10 267 foreach(gf_list, node) {
meillo@10 268 gchar *fname = (gchar *) (node->data);
meillo@10 269 pid_t pid;
meillo@0 270
meillo@10 271 if (fname[0] != '/')
meillo@10 272 fname = (gchar *) table_find(conf.get_names, fname);
meillo@0 273
meillo@10 274 if (fname != NULL) {
meillo@10 275 pid = fork();
meillo@10 276 if (pid == 0) {
meillo@10 277 signal(SIGCHLD, old_signal);
meillo@10 278 exit(get_from_file(fname) ? EXIT_SUCCESS : EXIT_FAILURE);
meillo@10 279 } else if (pid > 0) {
meillo@10 280 int status;
meillo@10 281 waitpid(pid, &status, 0);
meillo@10 282 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 283 logwrite(LOG_WARNING, "child returned %d\n", WEXITSTATUS(status));
meillo@10 284 if (WIFSIGNALED(status))
meillo@10 285 logwrite(LOG_WARNING, "child got signal: %d\n", WTERMSIG(status));
meillo@10 286 } else
meillo@10 287 logwrite(LOG_WARNING, "forking child failed: %s\n", strerror(errno));
meillo@10 288 }
meillo@10 289 }
meillo@10 290 }
meillo@10 291 signal(SIGCHLD, old_signal);
meillo@0 292 }
meillo@0 293 }
meillo@0 294
meillo@10 295 void
meillo@10 296 get_daemon(gint gival, char *argv[])
meillo@0 297 {
meillo@10 298 struct timeval tm;
meillo@10 299 time_t time_before, time_now;
meillo@10 300 int sel_ret;
meillo@0 301
meillo@10 302 /* setup handler for HUP signal: */
meillo@10 303 signal(SIGHUP, sighup_handler);
meillo@0 304
meillo@10 305 /* we can give up root privileges */
meillo@10 306 if (!conf.run_as_user) {
meillo@10 307 if (setegid(conf.mail_gid) != 0) {
meillo@10 308 logwrite(LOG_ALERT, "could not change gid to %d: %s\n", conf.mail_gid, strerror(errno));
meillo@10 309 exit(EXIT_FAILURE);
meillo@10 310 }
meillo@10 311 if (seteuid(conf.mail_uid) != 0) {
meillo@10 312 logwrite(LOG_ALERT, "could not change uid to %d: %s\n", conf.mail_uid, strerror(errno));
meillo@10 313 exit(EXIT_FAILURE);
meillo@10 314 }
meillo@10 315 }
meillo@0 316
meillo@10 317 /* sel_ret = 0; */
meillo@10 318 time(&time_before);
meillo@10 319 time_before -= gival;
meillo@10 320 sel_ret = -1;
meillo@0 321
meillo@10 322 while (1) {
meillo@10 323 /* see listen_port() in listen.c */
meillo@10 324 if (gival > 0) {
meillo@10 325 time(&time_now);
meillo@10 326 if (sel_ret == 0) { /* we are either just starting or did a queue run */
meillo@10 327 tm.tv_sec = gival;
meillo@10 328 tm.tv_usec = 0;
meillo@10 329 time_before = time_now;
meillo@10 330 } else {
meillo@10 331 tm.tv_sec = gival - (time_now - time_before);
meillo@10 332 tm.tv_usec = 0;
meillo@0 333
meillo@10 334 /* race condition, very unlikely (but possible): */
meillo@10 335 if (tm.tv_sec < 0)
meillo@10 336 tm.tv_sec = 0;
meillo@10 337 }
meillo@10 338 }
meillo@0 339
meillo@10 340 if ((sel_ret = select(0, NULL, NULL, NULL, &tm)) < 0) {
meillo@10 341 if (errno != EINTR) {
meillo@10 342 logwrite(LOG_ALERT, "select: (terminating): %s\n", strerror(errno));
meillo@10 343 exit(EXIT_FAILURE);
meillo@10 344 } else {
meillo@10 345 if (sighup_seen) {
meillo@10 346 logwrite(LOG_NOTICE, "HUP signal received. Restarting daemon\n");
meillo@0 347
meillo@10 348 if (argv == NULL)
meillo@10 349 exit(EXIT_SUCCESS);
meillo@0 350
meillo@10 351 execv(argv[0], &(argv[0]));
meillo@10 352 logwrite(LOG_ALERT, "restarting failed: %s\n", strerror(errno));
meillo@10 353 exit(EXIT_FAILURE);
meillo@0 354
meillo@10 355 }
meillo@10 356 }
meillo@10 357 } else {
meillo@15 358 /* If select returns 0, the interval time has elapsed. We start a new get process */
meillo@10 359 int pid;
meillo@10 360 signal(SIGCHLD, sigchld_handler);
meillo@10 361 if ((pid = fork()) == 0) {
meillo@10 362 get_online();
meillo@10 363
meillo@10 364 _exit(EXIT_SUCCESS);
meillo@10 365 } else if (pid < 0) {
meillo@10 366 logwrite(LOG_ALERT, "could not fork for get run");
meillo@10 367 }
meillo@10 368 }
meillo@0 369 }
meillo@0 370 }
meillo@0 371
meillo@10 372 gboolean
meillo@10 373 pop_before_smtp(gchar * fname)
meillo@0 374 {
meillo@10 375 gboolean ok = FALSE;
meillo@10 376 GList *resolve_list = NULL;
meillo@10 377 get_conf *gc = read_get_conf(fname);
meillo@10 378 guint flags = 0;
meillo@0 379
meillo@0 380 #ifdef ENABLE_RESOLVER
meillo@10 381 resolve_list = g_list_append(resolve_list, resolve_dns_a);
meillo@0 382 #endif
meillo@10 383 resolve_list = g_list_append(resolve_list, resolve_byname);
meillo@0 384
meillo@10 385 if (strcmp(gc->protocol, "pop3") == 0) {
meillo@10 386 DEBUG(3) debugf("attempting to login for user %s, host = %s with pop3\n", gc->login_user, gc->server_name);
meillo@10 387 ok = pop3_login(gc->server_name, gc->server_port, resolve_list, gc->login_user, gc->login_pass, flags);
meillo@10 388 } else if (strcmp(gc->protocol, "apop") == 0) {
meillo@10 389 DEBUG(3) debugf ("attempting to login for user %s, host = %s with apop\n", gc->login_user, gc->server_name);
meillo@15 390 ok = pop3_login(gc->server_name, gc->server_port, resolve_list, gc->login_user,
meillo@15 391 gc->login_pass, flags | POP3_FLAG_APOP);
meillo@10 392 } else {
meillo@10 393 logwrite(LOG_ALERT, "get protocol %s unknown\n", gc->protocol);
meillo@10 394 }
meillo@10 395 return ok;
meillo@0 396 }
meillo@0 397
meillo@0 398 #endif