masqmail-0.2

annotate src/pop3_in.c @ 0:08114f7dcc23

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