masqmail-0.2

annotate src/get.c @ 0:08114f7dcc23

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