masqmail

annotate src/pop3_in.c @ 156:ee2afbf92428

require host_name to be set in config file exit otherwise there is no portable way to determine the hostname (actually the hostname that masqmail should use) thus it must be set by the administrator
author meillo@marmaro.de
date Thu, 08 Jul 2010 09:49:05 +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