masqmail-0.2

annotate src/get.c @ 122:4fb68ef566ad

Todo: int may have only 16bit This will probably lead to problems
author meillo@marmaro.de
date Thu, 01 Jul 2010 13:37:26 +0200
parents 26e34ae9a3e3
children
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