masqmail-0.2

annotate src/pop3_in.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 26e34ae9a3e3
children c93023f58cc7
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@10 272 }
meillo@10 273 popb->error = pop3_syntax;
meillo@10 274 }
meillo@10 275 return FALSE;
meillo@0 276 }
meillo@0 277
meillo@10 278 static gboolean
meillo@10 279 get_drop_listing(pop3_base * popb)
meillo@0 280 {
meillo@10 281 gchar buf[64];
meillo@0 282
meillo@10 283 DEBUG(5) debugf("get_drop_listing() entered\n");
meillo@0 284
meillo@10 285 while (1) {
meillo@10 286 gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
meillo@10 287 if (len > 0) {
meillo@10 288 if (buf[0] == '.')
meillo@10 289 return TRUE;
meillo@10 290 else {
meillo@10 291 gint number, msg_size;
meillo@10 292 gchar *p = buf, *pe;
meillo@10 293 if (strtoi(p, &pe, &number)) {
meillo@10 294 p = pe;
meillo@10 295 if (strtoi(p, &pe, &msg_size)) {
meillo@10 296 msg_info *info = g_malloc(sizeof(msg_info));
meillo@10 297 info->number = number;
meillo@10 298 info->size = msg_size;
meillo@0 299
meillo@10 300 DEBUG(5) debugf ("get_drop_listing(), number = %d, msg_size = %d\n", number, msg_size);
meillo@0 301
meillo@10 302 info->uid = NULL;
meillo@10 303 info->is_fetched = FALSE;
meillo@10 304 info->is_in_uidl = FALSE;
meillo@10 305 popb->drop_list = g_list_append(popb->drop_list, info);
meillo@10 306 } else {
meillo@10 307 popb->error = pop3_syntax;
meillo@10 308 break;
meillo@10 309 }
meillo@10 310 } else {
meillo@10 311 popb->error = pop3_syntax;
meillo@10 312 break;
meillo@10 313 }
meillo@10 314 }
meillo@10 315 } else {
meillo@10 316 popb->error = (len == -1) ? pop3_eof : pop3_timeout;
meillo@10 317 return FALSE;
meillo@10 318 }
meillo@0 319 }
meillo@10 320 return FALSE;
meillo@0 321 }
meillo@0 322
meillo@10 323 static gboolean
meillo@10 324 get_uid_listing(pop3_base * popb)
meillo@0 325 {
meillo@10 326 gchar buf[64];
meillo@0 327
meillo@10 328 while (1) {
meillo@10 329 gint len = read_sockline(popb->in, buf, 64, POP3_CMD_TIMEOUT, READSOCKL_CHUG);
meillo@10 330 if (len > 0) {
meillo@10 331 if (buf[0] == '.')
meillo@10 332 return TRUE;
meillo@10 333 else {
meillo@10 334 gint number;
meillo@10 335 gchar *p = buf, *pe;
meillo@10 336 if (strtoi(p, &pe, &number)) {
meillo@10 337 msg_info *info = NULL;
meillo@10 338 GList *drop_node;
meillo@0 339
meillo@10 340 p = pe;
meillo@10 341 while (*p && isspace(*p))
meillo@10 342 p++;
meillo@0 343
meillo@10 344 foreach(popb->drop_list, drop_node) {
meillo@10 345 msg_info *curr_info = (msg_info *) (drop_node->data);
meillo@10 346 if (curr_info->number == number) {
meillo@10 347 info = curr_info;
meillo@10 348 break;
meillo@10 349 }
meillo@10 350 }
meillo@10 351 if (info) {
meillo@10 352 info->uid = g_strdup(p);
meillo@10 353 g_strchomp(info->uid);
meillo@10 354 }
meillo@0 355
meillo@10 356 } else {
meillo@10 357 popb->error = pop3_syntax;
meillo@10 358 break;
meillo@10 359 }
meillo@10 360 }
meillo@10 361 }
meillo@0 362 }
meillo@10 363 return FALSE;
meillo@0 364 }
meillo@0 365
meillo@10 366 static gboolean
meillo@10 367 check_init_response(pop3_base * popb)
meillo@0 368 {
meillo@10 369 if (check_response(popb)) {
meillo@10 370 gchar buf[256];
meillo@10 371 gchar *p = popb->buffer;
meillo@10 372 gint i = 0;
meillo@10 373 if (*p) {
meillo@10 374 while (*p && (*p != '<'))
meillo@10 375 p++;
meillo@10 376 while (*p && (*p != '>') && (i < 254))
meillo@10 377 buf[i++] = *(p++);
meillo@10 378 buf[i++] = '>';
meillo@15 379 buf[i] = '\0';
meillo@0 380
meillo@10 381 popb->timestamp = g_strdup(buf);
meillo@0 382
meillo@10 383 return TRUE;
meillo@10 384 }
meillo@10 385 }
meillo@10 386 return FALSE;
meillo@0 387 }
meillo@0 388
meillo@10 389 void
meillo@10 390 pop3_in_close(pop3_base * popb)
meillo@0 391 {
meillo@10 392 GList *node;
meillo@0 393
meillo@10 394 fclose(popb->in);
meillo@10 395 fclose(popb->out);
meillo@0 396
meillo@10 397 close(popb->sock);
meillo@0 398
meillo@10 399 foreach(popb->list_uid_old, node) {
meillo@10 400 gchar *uid = (gchar *) (node->data);
meillo@10 401 g_free(uid);
meillo@10 402 }
meillo@10 403 g_list_free(popb->list_uid_old);
meillo@0 404
meillo@10 405 foreach(popb->drop_list, node) {
meillo@10 406 msg_info *info = (msg_info *) (node->data);
meillo@10 407 if (info->uid)
meillo@10 408 g_free(info->uid);
meillo@10 409 g_free(info);
meillo@10 410 }
meillo@10 411 g_list_free(popb->drop_list);
meillo@0 412
meillo@10 413 if (popb->buffer)
meillo@10 414 g_free(popb->buffer);
meillo@10 415 if (popb->timestamp)
meillo@10 416 g_free(popb->timestamp);
meillo@0 417 }
meillo@0 418
meillo@10 419 pop3_base*
meillo@10 420 pop3_in_open(gchar * host, gint port, GList * resolve_list, guint flags)
meillo@0 421 {
meillo@10 422 pop3_base *popb;
meillo@10 423 gint sock;
meillo@10 424 mxip_addr *addr;
meillo@0 425
meillo@10 426 DEBUG(5) debugf("pop3_in_open entered, host = %s\n", host);
meillo@0 427
meillo@10 428 if ((addr = connect_resolvelist(&sock, host, port, resolve_list))) {
meillo@10 429 /* create structure to hold status data: */
meillo@10 430 popb = create_pop3base(sock, flags);
meillo@10 431 popb->remote_host = addr->name;
meillo@0 432
meillo@10 433 DEBUG(5) {
meillo@10 434 struct sockaddr_in name;
meillo@10 435 int len;
meillo@10 436 getsockname(sock, (struct sockaddr *) (&name), &len);
meillo@10 437 debugf("socket: name.sin_addr = %s\n", inet_ntoa(name.sin_addr));
meillo@10 438 }
meillo@10 439 return popb;
meillo@10 440 }
meillo@10 441 return NULL;
meillo@0 442 }
meillo@0 443
meillo@10 444 pop3_base*
meillo@10 445 pop3_in_open_child(gchar * cmd, guint flags)
meillo@0 446 {
meillo@10 447 pop3_base *popb;
meillo@10 448 gint sock;
meillo@0 449
meillo@10 450 DEBUG(5) debugf("pop3_in_open_child entered, cmd = %s\n", cmd);
meillo@10 451 sock = child(cmd);
meillo@10 452 if (sock > 0) {
meillo@10 453 popb = create_pop3base(sock, flags);
meillo@10 454 popb->remote_host = NULL;
meillo@10 455 return popb;
meillo@10 456 }
meillo@10 457 logwrite(LOG_ALERT, "child failed (sock = %d): %s\n", sock, strerror(errno));
meillo@0 458
meillo@10 459 return NULL;
meillo@0 460 }
meillo@0 461
meillo@10 462 gboolean
meillo@10 463 pop3_in_init(pop3_base * popb)
meillo@0 464 {
meillo@10 465 gboolean ok;
meillo@0 466
meillo@10 467 if ((ok = read_response(popb, POP3_INITIAL_TIMEOUT))) {
meillo@10 468 ok = check_init_response(popb);
meillo@10 469 }
meillo@10 470 if (!ok)
meillo@10 471 /* pop3_in_log_failure(popb, NULL); */
meillo@10 472 logwrite(LOG_ALERT, "pop3 failed\n");
meillo@10 473 return ok;
meillo@0 474 }
meillo@0 475
meillo@10 476 gboolean
meillo@10 477 pop3_in_login(pop3_base * popb, gchar * user, gchar * pass)
meillo@0 478 {
meillo@10 479 if (popb->flags & POP3_FLAG_APOP) {
meillo@0 480
meillo@10 481 gchar *string = g_strdup_printf("%s%s", popb->timestamp, pass);
meillo@10 482 gchar *digest = MD5String(string);
meillo@10 483 pop3_printf(popb->out, "APOP %s %s\r\n", user, digest);
meillo@10 484 g_free(string);
meillo@10 485 g_free(digest);
meillo@10 486 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 487 if (check_response(popb))
meillo@10 488 return TRUE;
meillo@10 489 else
meillo@10 490 popb->error = pop3_login_failure;
meillo@10 491 }
meillo@0 492
meillo@10 493 } else {
meillo@0 494
meillo@10 495 pop3_printf(popb->out, "USER %s\r\n", user);
meillo@10 496 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 497 if (check_response(popb)) {
meillo@10 498 pop3_printf(popb->out, "PASS %s\r\n", pass);
meillo@10 499 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 500 if (check_response(popb))
meillo@10 501 return TRUE;
meillo@10 502 else
meillo@10 503 popb->error = pop3_login_failure;
meillo@10 504 }
meillo@10 505 } else {
meillo@10 506 popb->error = pop3_login_failure;
meillo@10 507 }
meillo@10 508 }
meillo@0 509 }
meillo@10 510 return FALSE;
meillo@0 511 }
meillo@0 512
meillo@10 513 gboolean
meillo@10 514 pop3_in_stat(pop3_base * popb)
meillo@0 515 {
meillo@10 516 pop3_printf(popb->out, "STAT\r\n");
meillo@10 517 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 518 gint msg_cnt, mbox_size;
meillo@10 519 if (check_response_int_int(popb, &msg_cnt, &mbox_size)) {
meillo@10 520 popb->msg_cnt = msg_cnt;
meillo@10 521 popb->mbox_size = mbox_size;
meillo@0 522
meillo@10 523 return TRUE;
meillo@10 524 }
meillo@10 525 }
meillo@10 526 return FALSE;
meillo@0 527 }
meillo@0 528
meillo@10 529 gboolean
meillo@10 530 pop3_in_list(pop3_base * popb)
meillo@0 531 {
meillo@10 532 pop3_printf(popb->out, "LIST\r\n");
meillo@10 533 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 534 if (get_drop_listing(popb)) {
meillo@10 535 return TRUE;
meillo@10 536 }
meillo@10 537 }
meillo@10 538 return FALSE;
meillo@0 539 }
meillo@0 540
meillo@10 541 gboolean
meillo@10 542 pop3_in_dele(pop3_base * popb, gint number)
meillo@0 543 {
meillo@10 544 pop3_printf(popb->out, "DELE %d\r\n", number);
meillo@10 545 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 546 return TRUE;
meillo@10 547 }
meillo@10 548 return FALSE;
meillo@0 549 }
meillo@0 550
meillo@10 551 message*
meillo@10 552 pop3_in_retr(pop3_base * popb, gint number, address * rcpt)
meillo@0 553 {
meillo@10 554 accept_error err;
meillo@0 555
meillo@10 556 pop3_printf(popb->out, "RETR %d\r\n", number);
meillo@10 557 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 558 message *msg = create_message();
meillo@10 559 msg->received_host = popb->remote_host;
meillo@10 560 msg->received_prot = (popb->flags & POP3_FLAG_APOP) ? PROT_APOP : PROT_POP3;
meillo@10 561 msg->transfer_id = (popb->next_id)++;
meillo@10 562 msg->rcpt_list = g_list_append(NULL, copy_address(rcpt));
meillo@0 563
meillo@10 564 if ((err = accept_message(popb->in, msg, ACC_MAIL_FROM_HEAD
meillo@15 565 | (conf.do_save_envelope_to ? ACC_SAVE_ENVELOPE_TO : 0)))
meillo@10 566 == AERR_OK)
meillo@10 567 return msg;
meillo@0 568
meillo@10 569 destroy_message(msg);
meillo@10 570 }
meillo@10 571 return NULL;
meillo@0 572 }
meillo@0 573
meillo@10 574 gboolean
meillo@10 575 pop3_in_uidl(pop3_base * popb)
meillo@0 576 {
meillo@10 577 pop3_printf(popb->out, "UIDL\r\n");
meillo@10 578 if (read_response(popb, POP3_CMD_TIMEOUT)) {
meillo@10 579 if (get_uid_listing(popb)) {
meillo@10 580 return TRUE;
meillo@10 581 }
meillo@10 582 }
meillo@10 583 return FALSE;
meillo@10 584 }
meillo@0 585
meillo@10 586 gboolean
meillo@10 587 pop3_in_quit(pop3_base * popb)
meillo@10 588 {
meillo@10 589 pop3_printf(popb->out, "QUIT\r\n");
meillo@10 590 DEBUG(4) debugf("QUIT\n");
meillo@10 591 signal(SIGALRM, SIG_DFL);
meillo@10 592 return TRUE;
meillo@0 593 }
meillo@0 594
meillo@0 595 /* Send a DELE command for each message in (the old) uid listing.
meillo@0 596 This is to prevent mail from to be kept on server, if a previous
meillo@0 597 transaction was interupted. */
meillo@10 598 gboolean
meillo@10 599 pop3_in_uidl_dele(pop3_base * popb)
meillo@0 600 {
meillo@10 601 GList *drop_node;
meillo@0 602
meillo@10 603 foreach(popb->drop_list, drop_node) {
meillo@10 604 msg_info *info = (msg_info *) (drop_node->data);
meillo@10 605 /* if(find_uid(popb, info->uid)){ */
meillo@10 606 if (info->is_in_uidl) {
meillo@10 607 if (!pop3_in_dele(popb, info->number))
meillo@10 608 return FALSE;
meillo@10 609 /* TODO: it probably makes sense to also delete this uid from the listing */
meillo@10 610 }
meillo@10 611 }
meillo@10 612 return TRUE;
meillo@0 613 }
meillo@0 614
meillo@10 615 gboolean
meillo@15 616 pop3_get(pop3_base * popb, gchar * user, gchar * pass, address * rcpt, address * return_path,
meillo@15 617 gint max_count, gint max_size, gboolean max_size_delete)
meillo@0 618 {
meillo@10 619 gboolean ok = FALSE;
meillo@10 620 gint num_children = 0;
meillo@0 621
meillo@10 622 DEBUG(5) debugf("rcpt = %s@%s\n", rcpt->local_part, rcpt->domain);
meillo@0 623
meillo@10 624 signal(SIGCHLD, SIG_DFL);
meillo@0 625
meillo@10 626 if (pop3_in_init(popb)) {
meillo@10 627 if (pop3_in_login(popb, user, pass)) {
meillo@10 628 if (pop3_in_stat(popb)) {
meillo@10 629 if (popb->msg_cnt > 0) {
meillo@0 630
meillo@15 631 logwrite(LOG_NOTICE | LOG_VERBOSE, "%d message(s) for user %s at %s\n",
meillo@15 632 popb->msg_cnt, user, popb->remote_host);
meillo@0 633
meillo@10 634 if (pop3_in_list(popb)) {
meillo@10 635 gboolean do_get = !(popb->flags & POP3_FLAG_UIDL);
meillo@10 636 if (!do_get)
meillo@10 637 do_get = pop3_in_uidl(popb);
meillo@10 638 if (do_get) {
meillo@10 639 gint count = 0;
meillo@10 640 GList *drop_node;
meillo@0 641
meillo@10 642 if (popb->flags & POP3_FLAG_UIDL) {
meillo@10 643 read_uidl(popb, user);
meillo@10 644 logwrite(LOG_VERBOSE | LOG_NOTICE, "%d message(s) already in uidl.\n", popb->uidl_known_cnt);
meillo@10 645 }
meillo@10 646 if ((popb->flags & POP3_FLAG_UIDL) && (popb->flags & POP3_FLAG_UIDL_DELE))
meillo@10 647 pop3_in_uidl_dele(popb);
meillo@0 648
meillo@10 649 foreach(popb->drop_list, drop_node) {
meillo@0 650
meillo@10 651 msg_info *info = (msg_info *) (drop_node->data);
meillo@10 652 gboolean do_get_this = !(popb->flags & POP3_FLAG_UIDL);
meillo@10 653 /* if(!do_get_this) do_get_this = !find_uid(popb, info->uid); */
meillo@10 654 if (!do_get_this)
meillo@10 655 do_get_this = !(info->is_in_uidl);
meillo@10 656 if (do_get_this) {
meillo@0 657
meillo@10 658 if ((info->size < max_size) || (max_size == 0)) {
meillo@10 659 message *msg;
meillo@0 660
meillo@10 661 logwrite(LOG_VERBOSE | LOG_NOTICE, "receiving message %d\n", info->number);
meillo@10 662 msg = pop3_in_retr(popb, info->number, rcpt);
meillo@10 663
meillo@10 664 if (msg) {
meillo@10 665 if (return_path)
meillo@10 666 msg->return_path = copy_address(return_path);
meillo@10 667 if (spool_write(msg, TRUE)) {
meillo@10 668 pid_t pid;
meillo@10 669 logwrite(LOG_NOTICE, "%s <= %s host=%s with %s\n", msg->uid,
meillo@10 670 addr_string(msg->return_path), popb->remote_host,
meillo@10 671 (popb->flags & POP3_FLAG_APOP) ? prot_names [PROT_APOP] : prot_names [PROT_POP3]);
meillo@10 672 info->is_fetched = TRUE;
meillo@10 673 count++;
meillo@0 674 #if DO_WRITE_UIDL_EARLY
meillo@10 675 if (popb->flags & POP3_FLAG_UIDL)
meillo@10 676 write_uidl(popb, user);
meillo@0 677 #endif
meillo@10 678 if (!conf.do_queue) {
meillo@0 679
meillo@10 680 /* wait for child processes. If there are too many, we wait blocking, before we fork another one */
meillo@10 681 while (num_children > 0) {
meillo@10 682 int status, options = WNOHANG;
meillo@10 683 pid_t pid;
meillo@0 684
meillo@10 685 if (num_children >= POP3_MAX_CHILDREN) {
meillo@10 686 logwrite(LOG_NOTICE, "too many children - waiting\n");
meillo@10 687 options = 0;
meillo@10 688 }
meillo@10 689 if ((pid = waitpid(0, &status, options)) > 0) {
meillo@10 690 num_children--;
meillo@10 691 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 692 logwrite(LOG_WARNING, "delivery process with pid %d returned %d\n", pid, WEXITSTATUS (status));
meillo@10 693 if (WIFSIGNALED(status))
meillo@10 694 logwrite(LOG_WARNING, "delivery process with pid %d got signal: %d\n", pid, WTERMSIG (status));
meillo@10 695 } else if (pid < 0) {
meillo@10 696 logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
meillo@10 697 }
meillo@10 698 }
meillo@0 699
meillo@10 700 if ((pid = fork()) == 0) {
meillo@10 701 deliver(msg);
meillo@10 702 _exit(EXIT_SUCCESS);
meillo@10 703 } else if (pid < 0) {
meillo@10 704 logwrite(LOG_ALERT | LOG_VERBOSE, "could not fork for delivery, id = %s: %s\n", msg->uid, strerror(errno));
meillo@10 705 } else
meillo@10 706 num_children++;
meillo@10 707 } else {
meillo@10 708 DEBUG(1) debugf("queuing forced by configuration or option.\n");
meillo@10 709 }
meillo@10 710 if (popb->flags & POP3_FLAG_DELETE)
meillo@10 711 pop3_in_dele(popb, info->number);
meillo@0 712
meillo@10 713 destroy_message(msg);
meillo@10 714 } /* if(spool_write(msg, TRUE)) */
meillo@10 715 } else {
meillo@10 716 logwrite(LOG_ALERT, "retrieving of message %d failed: %d\n", info->number, popb->error);
meillo@10 717 }
meillo@10 718 } /* if((info->size > max_size) ... */
meillo@10 719 else {
meillo@10 720 logwrite(LOG_NOTICE | LOG_VERBOSE, "size of message #%d (%d) > max_size (%d)\n", info->number, info->size, max_size);
meillo@10 721 if (max_size_delete)
meillo@10 722 if (popb->flags & POP3_FLAG_DELETE)
meillo@10 723 pop3_in_dele(popb, info->number);
meillo@10 724 }
meillo@10 725 } /* if(do_get_this) ... */
meillo@10 726 else {
meillo@10 727 if (popb->flags & POP3_FLAG_UIDL) {
meillo@10 728 info->is_fetched = TRUE; /* obsolete? */
meillo@10 729 logwrite(LOG_VERBOSE, "message %d already known\n", info->number);
meillo@10 730 DEBUG(1) debugf("message %d (uid = %s) not fetched\n", info->number, info->uid);
meillo@0 731 #if 0
meillo@0 732 #if DO_WRITE_UIDL_EARLY
meillo@10 733 write_uidl(popb, user); /* obsolete? */
meillo@0 734 #endif
meillo@0 735 #endif
meillo@10 736 }
meillo@10 737 }
meillo@10 738 if ((max_count != 0) && (count >= max_count))
meillo@10 739 break;
meillo@10 740 } /* foreach() */
meillo@0 741 #if DO_WRITE_UIDL_EARLY
meillo@0 742 #else
meillo@10 743 if (popb->flags & POP3_FLAG_UIDL)
meillo@10 744 write_uidl(popb, user);
meillo@0 745 #endif
meillo@10 746 } /* if(pop3_in_uidl(popb) ... */
meillo@10 747 } /* if(pop3_in_list(popb)) */
meillo@10 748 } /* if(popb->msg_cnt > 0) */
meillo@10 749 else {
meillo@10 750 logwrite(LOG_NOTICE | LOG_VERBOSE, "no messages for user %s at %s\n", user, popb->remote_host);
meillo@10 751 }
meillo@10 752 ok = TRUE;
meillo@10 753 }
meillo@10 754 pop3_in_quit(popb);
meillo@10 755 } else {
meillo@10 756 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 login failed for user %s, host = %s\n", user, popb->remote_host);
meillo@10 757 }
meillo@0 758 }
meillo@10 759 if (!ok) {
meillo@10 760 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 failed, error = %d\n", popb->error);
meillo@10 761 }
meillo@0 762
meillo@10 763 while (num_children > 0) {
meillo@10 764 int status;
meillo@10 765 pid_t pid;
meillo@10 766 if ((pid = wait(&status)) > 0) {
meillo@10 767 num_children--;
meillo@10 768 if (WEXITSTATUS(status) != EXIT_SUCCESS)
meillo@10 769 logwrite(LOG_WARNING, "delivery process with pid %d returned %d\n", pid, WEXITSTATUS(status));
meillo@10 770 if (WIFSIGNALED(status))
meillo@10 771 logwrite(LOG_WARNING, "delivery process with pid %d got signal: %d\n", pid, WTERMSIG(status));
meillo@10 772 } else {
meillo@10 773 logwrite(LOG_WARNING, "wait got error: %s\n", strerror(errno));
meillo@10 774 }
meillo@10 775 }
meillo@0 776
meillo@10 777 return ok;
meillo@0 778 }
meillo@0 779
meillo@0 780 /* function just to log into a pop server,
meillo@0 781 for pop_before_smtp (or is it smtp_after_pop?)
meillo@0 782 */
meillo@0 783
meillo@10 784 gboolean
meillo@10 785 pop3_login(gchar * host, gint port, GList * resolve_list, gchar * user, gchar * pass, guint flags)
meillo@0 786 {
meillo@10 787 gboolean ok = FALSE;
meillo@10 788 pop3_base *popb;
meillo@0 789
meillo@10 790 signal(SIGCHLD, SIG_IGN);
meillo@0 791
meillo@10 792 if ((popb = pop3_in_open(host, port, resolve_list, flags))) {
meillo@10 793 if (pop3_in_init(popb)) {
meillo@10 794 if (pop3_in_login(popb, user, pass))
meillo@10 795 ok = TRUE;
meillo@10 796 else
meillo@10 797 logwrite(LOG_ALERT | LOG_VERBOSE, "pop3 login failed for user %s, host = %s\n", user, host);
meillo@10 798 }
meillo@10 799 pop3_in_close(popb);
meillo@10 800 }
meillo@10 801 return ok;
meillo@0 802 }
meillo@0 803
meillo@0 804 #endif