masqmail-0.2

annotate src/pop3_in.c @ 171:349518b940db

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