masqmail

annotate src/pop3_in.c @ 153:51d8eadf3c79

local_hosts defaults to `localhost' now this is a very basic setting you really should set it to something like "localhost;foo;foo.example.org"
author meillo@marmaro.de
date Wed, 07 Jul 2010 13:38:33 +0200
parents c93023f58cc7
children dc89737b27aa
rev   line source
meillo@0 1 /* pop3_in.c, Copyright (C) 2000 by Oliver Kurth,
meillo@0 2 *
meillo@0 3 * This program is free software; you can redistribute it and/or modify
meillo@0 4 * it under the terms of the GNU General Public License as published by
meillo@0 5 * the Free Software Foundation; either version 2 of the License, or
meillo@0 6 * (at your option) any later version.
meillo@10 7 *
meillo@0 8 * This program is distributed in the hope that it will be useful,
meillo@0 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
meillo@0 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
meillo@0 11 * GNU General Public License for more details.
meillo@0 12 *
meillo@0 13 * You should have received a copy of the GNU General Public License
meillo@0 14 * along with this program; if not, write to the Free Software
meillo@0 15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
meillo@0 16 */
meillo@0 17
meillo@0 18 /* see RFC 1725 */
meillo@0 19
meillo@15 20 #include <sys/wait.h>
meillo@15 21 #include <sys/stat.h>
meillo@15 22
meillo@0 23 #include "masqmail.h"
meillo@0 24 #include "pop3_in.h"
meillo@0 25 #include "readsock.h"
meillo@0 26
meillo@0 27 #ifdef USE_LIB_CRYPTO
meillo@0 28 #include <openssl/md5.h>
meillo@0 29 #else
meillo@0 30 #include "md5/global.h"
meillo@0 31 #include "md5/md5.h"
meillo@0 32 #endif
meillo@0 33
meillo@0 34 #ifdef ENABLE_POP3
meillo@0 35
meillo@0 36 /* experimental feature */
meillo@0 37 #define DO_WRITE_UIDL_EARLY 1
meillo@0 38
meillo@10 39 static gchar*
meillo@10 40 MD5String(char *string)
meillo@0 41 {
meillo@10 42 MD5_CTX context;
meillo@10 43 unsigned char digest[16];
meillo@10 44 char str_digest[33];
meillo@10 45 int i;
meillo@0 46
meillo@0 47 #ifdef USE_LIB_CRYPTO
meillo@10 48 MD5(string, strlen(string), digest);
meillo@0 49 #else
meillo@10 50 MD5Init(&context);
meillo@10 51 MD5Update(&context, string, strlen(string));
meillo@10 52 MD5Final(digest, &context);
meillo@0 53 #endif
meillo@10 54 for (i = 0; i < 16; i++)
meillo@10 55 sprintf(str_digest + 2 * i, "%02x", digest[i]);
meillo@0 56
meillo@10 57 return g_strdup(str_digest);
meillo@0 58 }
meillo@0 59
meillo@10 60 static pop3_base*
meillo@10 61 create_pop3base(gint sock, guint flags)
meillo@0 62 {
meillo@10 63 gint dup_sock;
meillo@0 64
meillo@10 65 pop3_base *popb = (pop3_base *) g_malloc(sizeof(pop3_base));
meillo@10 66 if (popb) {
meillo@10 67 memset(popb, 0, sizeof(pop3_base));
meillo@0 68
meillo@10 69 popb->error = pop3_ok;
meillo@0 70
meillo@10 71 popb->buffer = (gchar *) g_malloc(POP3_BUF_LEN);
meillo@0 72
meillo@10 73 dup_sock = dup(sock);
meillo@10 74 popb->out = fdopen(sock, "w");
meillo@10 75 popb->in = fdopen(dup_sock, "r");
meillo@10 76
meillo@10 77 popb->flags = flags;
meillo@10 78 }
meillo@10 79 return popb;
meillo@0 80 }
meillo@0 81
meillo@10 82 static void
meillo@10 83 pop3_printf(FILE * out, gchar * fmt, ...)
meillo@0 84 {
meillo@10 85 va_list args;
meillo@10 86 va_start(args, fmt);
meillo@0 87
meillo@10 88 DEBUG(4) {
meillo@10 89 gchar buf[256];
meillo@10 90 va_list args_copy;
meillo@0 91
meillo@10 92 va_copy(args_copy, args);
meillo@10 93 vsnprintf(buf, 255, fmt, args_copy);
meillo@10 94 va_end(args_copy);
meillo@0 95
meillo@10 96 debugf(">>>%s", buf);
meillo@10 97 }
meillo@0 98
meillo@10 99 vfprintf(out, fmt, args);
meillo@10 100 fflush(out);
meillo@0 101
meillo@10 102 va_end(args);
meillo@0 103 }
meillo@0 104
meillo@10 105 static gboolean
meillo@10 106 find_uid(pop3_base * popb, gchar * str)
meillo@0 107 {
meillo@10 108 GList *node, *node_next;
meillo@0 109
meillo@10 110 for (node = popb->list_uid_old; node; node = node_next) {
meillo@10 111 gchar *uid = (gchar *) (node->data);
meillo@10 112 node_next = node->next;
meillo@10 113 if (strcmp(uid, str) == 0) {
meillo@0 114 #if 1
meillo@10 115 popb->list_uid_old = g_list_remove_link(popb->list_uid_old, node);
meillo@10 116 g_list_free_1(node);
meillo@10 117 g_free(uid);
meillo@0 118 #endif
meillo@10 119 return TRUE;
meillo@10 120 }
meillo@10 121 }
meillo@10 122 return FALSE;
meillo@0 123 }
meillo@0 124
meillo@10 125 static gboolean
meillo@10 126 write_uidl(pop3_base * popb, gchar * user)
meillo@0 127 {
meillo@10 128 gboolean ok = FALSE;
meillo@10 129 GList *node;
meillo@10 130 gchar *filename = g_strdup_printf("%s/popuidl/%s@%s", conf.spool_dir, user, popb->remote_host);
meillo@10 131 gchar *tmpname = g_strdup_printf("%s.tmp", filename);
meillo@10 132 FILE *fptr = fopen(tmpname, "wt");
meillo@0 133
meillo@10 134 if (fptr) {
meillo@10 135 foreach(popb->drop_list, node) {
meillo@10 136 msg_info *info = (msg_info *) (node->data);
meillo@10 137 if (info->is_fetched || info->is_in_uidl)
meillo@10 138 fprintf(fptr, "%s\n", info->uid);
meillo@10 139 }
meillo@10 140 fclose(fptr);
meillo@10 141 ok = (rename(tmpname, filename) != -1);
meillo@10 142 }
meillo@10 143
meillo@10 144 g_free(tmpname);
meillo@10 145 g_free(filename);
meillo@10 146 return ok;
meillo@0 147 }
meillo@0 148
meillo@10 149 static gboolean
meillo@10 150 read_uidl_fname(pop3_base * popb, gchar * filename)
meillo@0 151 {
meillo@10 152 gboolean ok = FALSE;
meillo@10 153 FILE *fptr = fopen(filename, "rt");
meillo@10 154 gchar buf[256];
meillo@0 155
meillo@10 156 if (fptr) {
meillo@10 157 popb->list_uid_old = NULL;
meillo@10 158 while (fgets(buf, 255, fptr)) {
meillo@10 159 if (buf[strlen(buf) - 1] == '\n') {
meillo@10 160 g_strchomp(buf);
meillo@10 161 popb->list_uid_old = g_list_append(popb->list_uid_old, g_strdup(buf));
meillo@10 162 } else {
meillo@10 163 logwrite(LOG_ALERT, "broken uid: %s\n", buf);
meillo@10 164 break;
meillo@10 165 }
meillo@10 166 }
meillo@10 167 fclose(fptr);
meillo@10 168 ok = TRUE;
meillo@10 169 } else
meillo@10 170 logwrite(LOG_ALERT, "opening of %s failed: %s", filename, strerror(errno));
meillo@10 171 return ok;
meillo@0 172 }
meillo@0 173
meillo@10 174 static gboolean
meillo@10 175 read_uidl(pop3_base * popb, gchar * user)
meillo@0 176 {
meillo@10 177 gboolean ok = FALSE;
meillo@10 178 struct stat statbuf;
meillo@10 179 gchar *filename = g_strdup_printf("%s/popuidl/%s@%s", conf.spool_dir, user, popb->remote_host);
meillo@0 180
meillo@10 181 if (stat(filename, &statbuf) == 0) {
meillo@10 182 ok = read_uidl_fname(popb, filename);
meillo@10 183 if (ok) {
meillo@10 184 GList *drop_node;
meillo@10 185 foreach(popb->drop_list, drop_node) {
meillo@10 186 msg_info *info = (msg_info *) (drop_node->data);
meillo@10 187 if (find_uid(popb, info->uid)) {
meillo@10 188 DEBUG(5) debugf("msg with uid '%s' already known\n", info->uid);
meillo@10 189 info->is_in_uidl = TRUE;
meillo@10 190 popb->uidl_known_cnt++;
meillo@10 191 } else
meillo@10 192 DEBUG(5) debugf("msg with uid '%s' not known\n", info->uid);
meillo@10 193 }
meillo@10 194 }
meillo@10 195 } else {
meillo@10 196 logwrite(LOG_DEBUG, "no uidl file '%s' found\n", filename);
meillo@10 197 ok = TRUE;
meillo@10 198 }
meillo@0 199
meillo@10 200 g_free(filename);
meillo@10 201 return ok; /* return code is irrelevant, do not check... */
meillo@0 202 }
meillo@0 203
meillo@10 204 static gboolean
meillo@10 205 read_response(pop3_base * popb, int timeout)
meillo@0 206 {
meillo@10 207 gint len;
meillo@0 208
meillo@10 209 len = read_sockline(popb->in, popb->buffer, POP3_BUF_LEN, timeout, READSOCKL_CHUG);
meillo@0 210
meillo@10 211 if (len == -3) {
meillo@10 212 popb->error = pop3_timeout;
meillo@10 213 return FALSE;
meillo@10 214 } else if (len == -2) {
meillo@10 215 popb->error = pop3_syntax;
meillo@10 216 return FALSE;
meillo@10 217 } else if (len == -1) {
meillo@10 218 popb->error = pop3_eof;
meillo@10 219 return FALSE;
meillo@10 220 }
meillo@10 221
meillo@10 222 return TRUE;
meillo@0 223 }
meillo@0 224
meillo@10 225 static gboolean
meillo@10 226 check_response(pop3_base * popb)
meillo@0 227 {
meillo@10 228 char c = popb->buffer[0];
meillo@0 229
meillo@10 230 if (c == '+') {
meillo@10 231 popb->error = pop3_ok;
meillo@10 232 return TRUE;
meillo@10 233 } else if (c == '-')
meillo@10 234 popb->error = pop3_fail;
meillo@10 235 else
meillo@10 236 popb->error = pop3_syntax;
meillo@10 237 return FALSE;
meillo@0 238 }
meillo@0 239
meillo@10 240 static gboolean
meillo@10 241 strtoi(gchar * p, gchar ** pend, gint * val)
meillo@0 242 {
meillo@10 243 gchar buf[12];
meillo@10 244 gint i = 0;
meillo@0 245
meillo@10 246 while (*p && isspace(*p))
meillo@10 247 p++;
meillo@10 248 if (*p) {
meillo@10 249 while ((i < 11) && isdigit(*p))
meillo@10 250 buf[i++] = *(p++);
meillo@10 251 buf[i] = 0;
meillo@10 252 *val = atoi(buf);
meillo@10 253 *pend = p;
meillo@10 254 return TRUE;
meillo@10 255 }
meillo@10 256 return FALSE;
meillo@0 257 }
meillo@0 258
meillo@10 259 static gboolean
meillo@10 260 check_response_int_int(pop3_base * popb, gint * arg0, gint * arg1)
meillo@0 261 {
meillo@10 262 if (check_response(popb)) {
meillo@10 263 gchar *p = &(popb->buffer[3]);
meillo@10 264 gchar *pe;
meillo@0 265
meillo@10 266 if (strtoi(p, &pe, arg0)) {
meillo@10 267 DEBUG(5) debugf("arg0 = %d\n", *arg0);
meillo@10 268 p = pe;
meillo@10 269 if (strtoi(p, &pe, arg1))
meillo@10 270 DEBUG(5) debugf("arg1 = %d\n", *arg1);
meillo@10 271 return TRUE;
meillo@113 272 /* FIXME: Paolo's code has the return stmt
meillo@113 273 inside the if block right above it. What
meillo@113 274 is correct? */
meillo@10 275 }
meillo@10 276 popb->error = pop3_syntax;
meillo@10 277 }
meillo@10 278 return FALSE;
meillo@0 279 }
meillo@0 280
meillo@10 281 static gboolean
meillo@10 282 get_drop_listing(pop3_base * popb)
meillo@0 283 {
meillo@10 284 gchar buf[64];
meillo@0 285
meillo@10 286 DEBUG(5) debugf("get_drop_listing() entered\n");
meillo@0 287
meillo@10 288 while (1) {
meillo@10 289 gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
meillo@10 290 if (len > 0) {
meillo@10 291 if (buf[0] == '.')
meillo@10 292 return TRUE;
meillo@10 293 else {
meillo@10 294 gint number, msg_size;
meillo@10 295 gchar *p = buf, *pe;
meillo@10 296 if (strtoi(p, &pe, &number)) {
meillo@10 297 p = pe;
meillo@10 298 if (strtoi(p, &pe, &msg_size)) {
meillo@10 299 msg_info *info = g_malloc(sizeof(msg_info));
meillo@10 300 info->number = number;
meillo@10 301 info->size = msg_size;
meillo@0 302
meillo@10 303 DEBUG(5) debugf ("get_drop_listing(), number = %d, msg_size = %d\n", number, msg_size);
meillo@0 304
meillo@10 305 info->uid = NULL;
meillo@10 306 info->is_fetched = FALSE;
meillo@10 307 info->is_in_uidl = FALSE;
meillo@10 308 popb->drop_list = g_list_append(popb->drop_list, info);
meillo@10 309 } else {
meillo@10 310 popb->error = pop3_syntax;
meillo@10 311 break;
meillo@10 312 }
meillo@10 313 } else {
meillo@10 314 popb->error = pop3_syntax;
meillo@10 315 break;
meillo@10 316 }
meillo@10 317 }
meillo@10 318 } else {
meillo@10 319 popb->error = (len == -1) ? pop3_eof : pop3_timeout;
meillo@10 320 return FALSE;
meillo@10 321 }
meillo@0 322 }
meillo@10 323 return FALSE;
meillo@0 324 }
meillo@0 325
meillo@10 326 static gboolean
meillo@10 327 get_uid_listing(pop3_base * popb)
meillo@0 328 {
meillo@10 329 gchar buf[64];
meillo@0 330
meillo@10 331 while (1) {
meillo@10 332 gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
meillo@10 333 if (len > 0) {
meillo@10 334 if (buf[0] == '.')
meillo@10 335 return TRUE;
meillo@10 336 else {
meillo@10 337 gint number;
meillo@10 338 gchar *p = buf, *pe;
meillo@10 339 if (strtoi(p, &pe, &number)) {
meillo@10 340 msg_info *info = NULL;
meillo@10 341 GList *drop_node;
meillo@0 342
meillo@10 343 p = pe;
meillo@10 344 while (*p && isspace(*p))
meillo@10 345 p++;
meillo@0 346
meillo@10 347 foreach(popb->drop_list, drop_node) {
meillo@10 348 msg_info *curr_info = (msg_info *) (drop_node->data);
meillo@10 349 if (curr_info->number == number) {
meillo@10 350 info = curr_info;
meillo@10 351 break;
meillo@10 352 }
meillo@10 353 }
meillo@10 354 if (info) {
meillo@10 355 info->uid = g_strdup(p);
meillo@10 356 g_strchomp(info->uid);
meillo@10 357 }
meillo@0 358
meillo@10 359 } else {
meillo@10 360 popb->error = pop3_syntax;
meillo@10 361 break;
meillo@10 362 }
meillo@10 363 }
meillo@10 364 }
meillo@0 365 }
meillo@10 366 return FALSE;
meillo@0 367 }
meillo@0 368
meillo@10 369 static gboolean
meillo@10 370 check_init_response(pop3_base * popb)
meillo@0 371 {
meillo@10 372 if (check_response(popb)) {
meillo@10 373 gchar buf[256];
meillo@10 374 gchar *p = popb->buffer;
meillo@10 375 gint i = 0;
meillo@10 376 if (*p) {
meillo@10 377 while (*p && (*p != '<'))
meillo@10 378 p++;
meillo@10 379 while (*p && (*p != '>') && (i < 254))
meillo@10 380 buf[i++] = *(p++);
meillo@10 381 buf[i++] = '>';
meillo@15 382 buf[i] = '\0';
meillo@0 383
meillo@10 384 popb->timestamp = g_strdup(buf);
meillo@0 385
meillo@10 386 return TRUE;
meillo@10 387 }
meillo@10 388 }
meillo@10 389 return FALSE;
meillo@0 390 }
meillo@0 391
meillo@10 392 void
meillo@10 393 pop3_in_close(pop3_base * popb)
meillo@0 394 {
meillo@10 395 GList *node;
meillo@0 396
meillo@10 397 fclose(popb->in);
meillo@10 398 fclose(popb->out);
meillo@0 399
meillo@10 400 close(popb->sock);
meillo@0 401
meillo@10 402 foreach(popb->list_uid_old, node) {
meillo@10 403 gchar *uid = (gchar *) (node->data);
meillo@10 404 g_free(uid);
meillo@10 405 }
meillo@10 406 g_list_free(popb->list_uid_old);
meillo@0 407
meillo@10 408 foreach(popb->drop_list, node) {
meillo@10 409 msg_info *info = (msg_info *) (node->data);
meillo@10 410 if (info->uid)
meillo@10 411 g_free(info->uid);
meillo@10 412 g_free(info);
meillo@10 413 }
meillo@10 414 g_list_free(popb->drop_list);
meillo@0 415
meillo@10 416 if (popb->buffer)
meillo@10 417 g_free(popb->buffer);
meillo@10 418 if (popb->timestamp)
meillo@10 419 g_free(popb->timestamp);
meillo@0 420 }
meillo@0 421
meillo@10 422 pop3_base*
meillo@10 423 pop3_in_open(gchar * host, gint port, GList * resolve_list, guint flags)
meillo@0 424 {
meillo@10 425 pop3_base *popb;
meillo@10 426 gint sock;
meillo@10 427 mxip_addr *addr;
meillo@0 428
meillo@10 429 DEBUG(5) debugf("pop3_in_open entered, host = %s\n", host);
meillo@0 430
meillo@10 431 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 432 /* create structure to hold status data: */
meillo@10 433 popb = create_pop3base(sock, flags);
meillo@10 434 popb->remote_host = addr->name;
meillo@0 435
meillo@10 436 DEBUG(5) {
meillo@10 437 struct sockaddr_in name;
meillo@10 438 int len;
meillo@10 439 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 440 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 441 }
meillo@10 442 return popb;
meillo@10 443 }
meillo@10 444 return NULL;
meillo@0 445 }
meillo@0 446
meillo@10 447 pop3_base*
meillo@10 448 pop3_in_open_child(gchar * cmd, guint flags)
meillo@0 449 {
meillo@10 450 pop3_base *popb;
meillo@10 451 gint sock;
meillo@0 452
meillo@10 453 DEBUG(5) debugf("pop3_in_open_child entered, cmd = %s\n", cmd);
meillo@10 454 sock = child(cmd);
meillo@10 455 if (sock > 0) {
meillo@10 456 popb = create_pop3base(sock, flags);
meillo@10 457 popb->remote_host = NULL;
meillo@10 458 return popb;
meillo@10 459 }
meillo@10 460 logwrite(LOG_ALERT, "child failed (sock = %d): %s\n", sock, strerror(errno));
meillo@0 461
meillo@10 462 return NULL;
meillo@0 463 }
meillo@0 464
meillo@10 465 gboolean
meillo@10 466 pop3_in_init(pop3_base * popb)
meillo@0 467 {
meillo@10 468 gboolean ok;
meillo@0 469
meillo@10 470 if ((ok = read_response(popb, POP3_INITIAL_TIMEOUT))) {
meillo@10 471 ok = check_init_response(popb);
meillo@10 472 }
meillo@10 473 if (!ok)
meillo@10 474 /* pop3_in_log_failure(popb, NULL); */
meillo@10 475 logwrite(LOG_ALERT, "pop3 failed\n");
meillo@10 476 return ok;
meillo@0 477 }
meillo@0 478
meillo@10 479 gboolean
meillo@10 480 pop3_in_login(pop3_base * popb, gchar * user, gchar * pass)
meillo@0 481 {
meillo@10 482 if (popb->flags & POP3_FLAG_APOP) {
meillo@0 483
meillo@10 484 gchar *string = g_strdup_printf("%s%s", popb->timestamp, pass);
meillo@10 485 gchar *digest = MD5String(string);
meillo@10 486 pop3_printf(popb->out, "APOP %s %s\r\n", user, digest);
meillo@10 487 g_free(string);
meillo@10 488 g_free(digest);
meillo@10 489 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 490 if (check_response(popb))
meillo@10 491 return TRUE;
meillo@10 492 else
meillo@10 493 popb->error = pop3_login_failure;
meillo@10 494 }
meillo@0 495
meillo@10 496 } else {
meillo@0 497
meillo@10 498 pop3_printf(popb->out, "USER %s\r\n", user);
meillo@10 499 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 500 if (check_response(popb)) {
meillo@10 501 pop3_printf(popb->out, "PASS %s\r\n", pass);
meillo@10 502 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 503 if (check_response(popb))
meillo@10 504 return TRUE;
meillo@10 505 else
meillo@10 506 popb->error = pop3_login_failure;
meillo@10 507 }
meillo@10 508 } else {
meillo@10 509 popb->error = pop3_login_failure;
meillo@10 510 }
meillo@10 511 }
meillo@0 512 }
meillo@10 513 return FALSE;
meillo@0 514 }
meillo@0 515
meillo@10 516 gboolean
meillo@10 517 pop3_in_stat(pop3_base * popb)
meillo@0 518 {
meillo@10 519 pop3_printf(popb->out, "STAT\r\n");
meillo@10 520 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 521 gint msg_cnt, mbox_size;
meillo@10 522 if (check_response_int_int(popb, &msg_cnt, &mbox_size)) {
meillo@10 523 popb->msg_cnt = msg_cnt;
meillo@10 524 popb->mbox_size = mbox_size;
meillo@0 525
meillo@10 526 return TRUE;
meillo@10 527 }
meillo@10 528 }
meillo@10 529 return FALSE;
meillo@0 530 }
meillo@0 531
meillo@10 532 gboolean
meillo@10 533 pop3_in_list(pop3_base * popb)
meillo@0 534 {
meillo@10 535 pop3_printf(popb->out, "LIST\r\n");
meillo@10 536 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 537 if (get_drop_listing(popb)) {
meillo@10 538 return TRUE;
meillo@10 539 }
meillo@10 540 }
meillo@10 541 return FALSE;
meillo@0 542 }
meillo@0 543
meillo@10 544 gboolean
meillo@10 545 pop3_in_dele(pop3_base * popb, gint number)
meillo@0 546 {
meillo@10 547 pop3_printf(popb->out, "DELE %d\r\n", number);
meillo@10 548 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 549 return TRUE;
meillo@10 550 }
meillo@10 551 return FALSE;
meillo@0 552 }
meillo@0 553
meillo@10 554 message*
meillo@10 555 pop3_in_retr(pop3_base * popb, gint number, address * rcpt)
meillo@0 556 {
meillo@10 557 accept_error err;
meillo@0 558
meillo@10 559 pop3_printf(popb->out, "RETR %d\r\n", number);
meillo@10 560 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 561 message *msg = create_message();
meillo@10 562 msg->received_host = popb->remote_host;
meillo@10 563 msg->received_prot = (popb->flags & POP3_FLAG_APOP) ? PROT_APOP : PROT_POP3;
meillo@10 564 msg->transfer_id = (popb->next_id)++;
meillo@10 565 msg->rcpt_list = g_list_append(NULL, copy_address(rcpt));
meillo@0 566
meillo@10 567 if ((err = accept_message(popb->in, msg, ACC_MAIL_FROM_HEAD
meillo@15 568 | (conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)))
meillo@10 569 == AERR_OK)
meillo@10 570 return msg;
meillo@0 571
meillo@10 572 destroy_message(msg);
meillo@10 573 }
meillo@10 574 return NULL;
meillo@0 575 }
meillo@0 576
meillo@10 577 gboolean
meillo@10 578 pop3_in_uidl(pop3_base * popb)
meillo@0 579 {
meillo@10 580 pop3_printf(popb->out, "UIDL\r\n");
meillo@10 581 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 582 if (get_uid_listing(popb)) {
meillo@10 583 return TRUE;
meillo@10 584 }
meillo@10 585 }
meillo@10 586 return FALSE;
meillo@10 587 }
meillo@0 588
meillo@10 589 gboolean
meillo@10 590 pop3_in_quit(pop3_base * popb)
meillo@10 591 {
meillo@10 592 pop3_printf(popb->out, "QUIT\r\n");
meillo@10 593 DEBUG(4) debugf("QUIT\n");
meillo@10 594 signal(SIGALRM, SIG_DFL);
meillo@10 595 return TRUE;
meillo@0 596 }
meillo@0 597
meillo@0 598 /* Send a DELE command for each message in (the old) uid listing.
meillo@0 599 This is to prevent mail from to be kept on server, if a previous
meillo@0 600 transaction was interupted. */
meillo@10 601 gboolean
meillo@10 602 pop3_in_uidl_dele(pop3_base * popb)
meillo@0 603 {
meillo@10 604 GList *drop_node;
meillo@0 605
meillo@10 606 foreach(popb->drop_list, drop_node) {
meillo@10 607 msg_info *info = (msg_info *) (drop_node->data);
meillo@10 608 /* if(find_uid(popb, info->uid)){ */
meillo@10 609 if (info->is_in_uidl) {
meillo@10 610 if (!pop3_in_dele(popb, info->number))
meillo@10 611 return FALSE;
meillo@10 612 /* TODO: it probably makes sense to also delete this uid from the listing */
meillo@10 613 }
meillo@10 614 }
meillo@10 615 return TRUE;
meillo@0 616 }
meillo@0 617
meillo@10 618 gboolean
meillo@15 619 pop3_get(pop3_base * popb, gchar * user, gchar * pass, address * rcpt, address * return_path,
meillo@15 620 gint max_count, gint max_size, gboolean max_size_delete)
meillo@0 621 {
meillo@10 622 gboolean ok = FALSE;
meillo@10 623 gint num_children = 0;
meillo@0 624
meillo@10 625 DEBUG(5) debugf("rcpt = %s@%s\n", rcpt->local_part, rcpt->domain);
meillo@0 626
meillo@10 627 signal(SIGCHLD, SIG_DFL);
meillo@0 628
meillo@10 629 if (pop3_in_init(popb)) {
meillo@10 630 if (pop3_in_login(popb, user, pass)) {
meillo@10 631 if (pop3_in_stat(popb)) {
meillo@10 632 if (popb->msg_cnt > 0) {
meillo@0 633
meillo@15 634 logwrite(LOG_NOTICE | LOG_VERBOSE, "%d message(s) for user %s at %s\n",
meillo@15 635 popb->msg_cnt, user, popb->remote_host);
meillo@0 636
meillo@10 637 if (pop3_in_list(popb)) {
meillo@10 638 gboolean do_get = !(popb->flags & POP3_FLAG_UIDL);
meillo@10 639 if (!do_get)
meillo@10 640 do_get = pop3_in_uidl(popb);
meillo@10 641 if (do_get) {
meillo@10 642 gint count = 0;
meillo@10 643 GList *drop_node;
meillo@0 644
meillo@10 645 if (popb->flags & POP3_FLAG_UIDL) {
meillo@10 646 read_uidl(popb, user);
meillo@10 647 logwrite(LOG_VERBOSE | LOG_NOTICE, "%d message(s) already in uidl.\n", popb->uidl_known_cnt);
meillo@10 648 }
meillo@10 649 if ((popb->flags & POP3_FLAG_UIDL) && (popb->flags & POP3_FLAG_UIDL_DELE))
meillo@10 650 pop3_in_uidl_dele(popb);
meillo@0 651
meillo@10 652 foreach(popb->drop_list, drop_node) {
meillo@0 653
meillo@10 654 msg_info *info = (msg_info *) (drop_node->data);
meillo@10 655 gboolean do_get_this = !(popb->flags & POP3_FLAG_UIDL);
meillo@10 656 /* if(!do_get_this) do_get_this = !find_uid(popb, info->uid); */
meillo@10 657 if (!do_get_this)
meillo@10 658 do_get_this = !(info->is_in_uidl);
meillo@10 659 if (do_get_this) {
meillo@0 660
meillo@10 661 if ((info->size < max_size) || (max_size == 0)) {
meillo@10 662 message *msg;
meillo@0 663
meillo@10 664 logwrite(LOG_VERBOSE | LOG_NOTICE, "receiving message %d\n", info->number);
meillo@10 665 msg = pop3_in_retr(popb, info->number, rcpt);
meillo@10 666
meillo@10 667 if (msg) {
meillo@10 668 if (return_path)
meillo@10 669 msg->return_path = copy_address(return_path);
meillo@10 670 if (spool_write(msg, TRUE)) {
meillo@10 671 pid_t pid;
meillo@10 672 logwrite(LOG_NOTICE, "%s <= %s host=%s with %s\n", msg->uid,
meillo@10 673 addr_string(msg->return_path), popb->remote_host,
meillo@10 674 (popb->flags & POP3_FLAG_APOP) ? prot_names [PROT_APOP] : prot_names [PROT_POP3]);
meillo@10 675 info->is_fetched = TRUE;
meillo@10 676 count++;
meillo@0 677 #if DO_WRITE_UIDL_EARLY
meillo@10 678 if (popb->flags & POP3_FLAG_UIDL)
meillo@10 679 write_uidl(popb, user);
meillo@0 680 #endif
meillo@10 681 if (!conf.do_queue) {
meillo@0 682
meillo@10 683 /* wait for child processes. If there are too many, we wait blocking, before we fork another one */
meillo@10 684 while (num_children > 0) {
meillo@10 685 int status, options = WNOHANG;
meillo@10 686 pid_t pid;
meillo@0 687
meillo@10 688 if (num_children >= POP3_MAX_CHILDREN) {
meillo@10 689 logwrite(LOG_NOTICE, "too many children - waiting\n");
meillo@10 690 options = 0;
meillo@10 691 }
meillo@10 692 if ((pid = waitpid(0, &status, options)) > 0) {
meillo@10 693 num_children--;
meillo@10 694 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 695 logwrite(LOG_WARNING, "delivery process with pid %d returned %d\n", pid, WEXITSTATUS (status));
meillo@10 696 if (WIFSIGNALED(status))
meillo@10 697 logwrite(LOG_WARNING, "delivery process with pid %d got signal: %d\n", pid, WTERMSIG (status));
meillo@10 698 } else if (pid < 0) {
meillo@10 699 logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
meillo@10 700 }
meillo@10 701 }
meillo@0 702
meillo@10 703 if ((pid = fork()) == 0) {
meillo@10 704 deliver(msg);
meillo@10 705 _exit(EXIT_SUCCESS);
meillo@10 706 } else if (pid < 0) {
meillo@10 707 logwrite(LOG_ALERT | LOG_VERBOSE, "could not fork for delivery, id = %s: %s\n", msg->uid, strerror(errno));
meillo@10 708 } else
meillo@10 709 num_children++;
meillo@10 710 } else {
meillo@10 711 DEBUG(1) debugf("queuing forced by configuration or option.\n");
meillo@10 712 }
meillo@10 713 if (popb->flags & POP3_FLAG_DELETE)
meillo@10 714 pop3_in_dele(popb, info->number);
meillo@0 715
meillo@10 716 destroy_message(msg);
meillo@10 717 } /* if(spool_write(msg, TRUE)) */
meillo@10 718 } else {
meillo@10 719 logwrite(LOG_ALERT, "retrieving of message %d failed: %d\n", info->number, popb->error);
meillo@10 720 }
meillo@132 721 } else {
meillo@132 722 /* info->size > max_size */
meillo@10 723 logwrite(LOG_NOTICE | LOG_VERBOSE, "size of message #%d (%d) > max_size (%d)\n", info->number, info->size, max_size);
meillo@10 724 if (max_size_delete)
meillo@10 725 if (popb->flags & POP3_FLAG_DELETE)
meillo@10 726 pop3_in_dele(popb, info->number);
meillo@10 727 }
meillo@10 728 } /* if(do_get_this) ... */
meillo@10 729 else {
meillo@10 730 if (popb->flags & POP3_FLAG_UIDL) {
meillo@10 731 info->is_fetched = TRUE; /* obsolete? */
meillo@10 732 logwrite(LOG_VERBOSE, "message %d already known\n", info->number);
meillo@10 733 DEBUG(1) debugf("message %d (uid = %s) not fetched\n", info->number, info->uid);
meillo@0 734 #if 0
meillo@0 735 #if DO_WRITE_UIDL_EARLY
meillo@10 736 write_uidl(popb, user); /* obsolete? */
meillo@0 737 #endif
meillo@0 738 #endif
meillo@10 739 }
meillo@10 740 }
meillo@10 741 if ((max_count != 0) && (count >= max_count))
meillo@10 742 break;
meillo@10 743 } /* foreach() */
meillo@0 744 #if DO_WRITE_UIDL_EARLY
meillo@0 745 #else
meillo@10 746 if (popb->flags & POP3_FLAG_UIDL)
meillo@10 747 write_uidl(popb, user);
meillo@0 748 #endif
meillo@10 749 } /* if(pop3_in_uidl(popb) ... */
meillo@10 750 } /* if(pop3_in_list(popb)) */
meillo@10 751 } /* if(popb->msg_cnt > 0) */
meillo@10 752 else {
meillo@10 753 logwrite(LOG_NOTICE | LOG_VERBOSE, "no messages for user %s at %s\n", user, popb->remote_host);
meillo@10 754 }
meillo@10 755 ok = TRUE;
meillo@10 756 }
meillo@10 757 pop3_in_quit(popb);
meillo@10 758 } else {
meillo@10 759 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 login failed for user %s, host = %s\n", user, popb->remote_host);
meillo@10 760 }
meillo@0 761 }
meillo@10 762 if (!ok) {
meillo@10 763 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 failed, error = %d\n", popb->error);
meillo@10 764 }
meillo@0 765
meillo@10 766 while (num_children > 0) {
meillo@10 767 int status;
meillo@10 768 pid_t pid;
meillo@10 769 if ((pid = wait(&status)) > 0) {
meillo@10 770 num_children--;
meillo@10 771 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 772 logwrite(LOG_WARNING, "delivery process with pid %d returned %d\n", pid, WEXITSTATUS(status));
meillo@10 773 if (WIFSIGNALED(status))
meillo@10 774 logwrite(LOG_WARNING, "delivery process with pid %d got signal: %d\n", pid, WTERMSIG(status));
meillo@10 775 } else {
meillo@10 776 logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
meillo@10 777 }
meillo@10 778 }
meillo@0 779
meillo@10 780 return ok;
meillo@0 781 }
meillo@0 782
meillo@0 783 /* function just to log into a pop server,
meillo@0 784 for pop_before_smtp (or is it smtp_after_pop?)
meillo@0 785 */
meillo@0 786
meillo@10 787 gboolean
meillo@10 788 pop3_login(gchar * host, gint port, GList * resolve_list, gchar * user, gchar * pass, guint flags)
meillo@0 789 {
meillo@10 790 gboolean ok = FALSE;
meillo@10 791 pop3_base *popb;
meillo@0 792
meillo@10 793 signal(SIGCHLD, SIG_IGN);
meillo@0 794
meillo@10 795 if ((popb = pop3_in_open(host, port, resolve_list, flags))) {
meillo@10 796 if (pop3_in_init(popb)) {
meillo@10 797 if (pop3_in_login(popb, user, pass))
meillo@10 798 ok = TRUE;
meillo@10 799 else
meillo@10 800 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 login failed for user %s, host = %s\n", user, host);
meillo@10 801 }
meillo@10 802 pop3_in_close(popb);
meillo@10 803 }
meillo@10 804 return ok;
meillo@0 805 }
meillo@0 806
meillo@0 807 #endif