Blame view

mailbox/mbx_pop.c 56.2 KB
1
/* GNU mailutils - a suite of utilities for electronic mail
2
   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Library Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

18 19 20 21
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

22
#include <termios.h>
23
#include <errno.h>
24 25 26
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
27 28
#include <sys/time.h>
#include <sys/types.h>
29 30
#include <unistd.h>
#include <fcntl.h>
31
#include <stdarg.h>
32 33 34 35 36

#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#endif

37 38 39
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
40

41 42
#include <md5-rsa.h>

43 44
#include <mailutils/attribute.h>
#include <mailutils/auth.h>
45 46 47
#include <mailutils/body.h>
#include <mailutils/debug.h>
#include <mailutils/errno.h>
48
#include <mailutils/error.h>
49 50 51 52 53 54 55
#include <mailutils/header.h>
#include <mailutils/message.h>
#include <mailutils/observer.h>
#include <mailutils/property.h>
#include <mailutils/stream.h>
#include <mailutils/url.h>

56
#include <folder0.h>
57
#include <mailbox0.h>
58
#include <registrar0.h>
59
#include <url0.h>
60

61 62
#define PROP_RFC822 1

63 64 65 66 67 68
/* Advance declarations.  */
struct _pop_data;
struct _pop_message;

typedef struct _pop_data * pop_data_t;
typedef struct _pop_message * pop_message_t;
69

70
/* The different possible states of a Pop client, Note that POP3 is not
71 72 73 74
   reentrant i.e. it is only one channel, so it is not possible to start
   Another operation while one is running.  The only resort is to close the
   connection and reopen it again.  This is what we do, the downside is that
   the client as to get the authentication again user/pass.  */
75
enum pop_state
76
{
77
  POP_NO_STATE, POP_STATE_DONE,
78 79
  POP_OPEN_CONNECTION,
  POP_GREETINGS,
80 81 82 83 84 85 86 87 88 89 90 91 92
  POP_APOP, POP_APOP_ACK,
  POP_DELE, POP_DELE_ACK,
  POP_LIST, POP_LIST_ACK, POP_LIST_RX,
  POP_QUIT, POP_QUIT_ACK,
  POP_NOOP, POP_NOOP_ACK,
  POP_RETR, POP_RETR_ACK, POP_RETR_RX_HDR, POP_RETR_RX_BODY,
  POP_RSET, POP_RSET_ACK,
  POP_STAT, POP_STAT_ACK,
  POP_TOP,  POP_TOP_ACK,  POP_TOP_RX,
  POP_UIDL, POP_UIDL_ACK,
  POP_AUTH, POP_AUTH_DONE,
  POP_AUTH_USER, POP_AUTH_USER_ACK,
  POP_AUTH_PASS, POP_AUTH_PASS_ACK
93 94
};

95
static void pop_destroy        __P ((mailbox_t));
96

97
/*  Functions/Methods that implements the mailbox_t API.  */
98 99 100 101 102 103 104 105 106
static int pop_open            __P ((mailbox_t, int));
static int pop_close           __P ((mailbox_t));
static int pop_get_message     __P ((mailbox_t, size_t, message_t *));
static int pop_messages_count  __P ((mailbox_t, size_t *));
static int pop_messages_recent __P ((mailbox_t, size_t *));
static int pop_message_unseen  __P ((mailbox_t, size_t *));
static int pop_expunge         __P ((mailbox_t));
static int pop_scan            __P ((mailbox_t, size_t, size_t *));
static int pop_is_updated      __P ((mailbox_t));
107 108

/* The implementation of message_t */
109
int _pop_user            __P ((authority_t));
110
int _pop_apop            __P ((authority_t));
111
static int pop_get_size        __P ((mailbox_t, off_t *));
112
/* We use pop_top for retreiving headers.  */
113
/* static int pop_header_read (header_t, char *, size_t, off_t, size_t *); */
114 115 116 117 118 119 120 121 122 123
static int pop_body_fd         __P ((stream_t, int *));
static int pop_body_size       __P ((body_t, size_t *));
static int pop_body_lines      __P ((body_t, size_t *));
static int pop_body_read       __P ((stream_t, char *, size_t, off_t, size_t *));
static int pop_message_read    __P ((stream_t, char *, size_t, off_t, size_t *));
static int pop_message_size    __P ((message_t, size_t *));
static int pop_message_fd      __P ((stream_t, int *));
static int pop_top             __P ((header_t, char *, size_t, off_t, size_t *));
static int pop_retr            __P ((pop_message_t, char *, size_t, off_t, size_t *));
static int pop_get_fd          __P ((pop_message_t, int *));
124 125 126
static int pop_get_attribute   __P ((attribute_t, int *));
static int pop_set_attribute   __P ((attribute_t, int));
static int pop_unset_attribute __P ((attribute_t, int));
127 128 129
static int pop_uidl            __P ((message_t, char *, size_t, size_t *));
static int pop_uid             __P ((message_t, size_t *));
static int fill_buffer         __P ((pop_data_t, char *, size_t));
130
static int pop_sleep           __P ((int));
131 132 133 134
static int pop_readline        __P ((pop_data_t));
static int pop_read_ack        __P ((pop_data_t));
static int pop_writeline       __P ((pop_data_t, const char *, ...));
static int pop_write           __P ((pop_data_t));
135 136 137 138
static int pop_get_user        __P ((authority_t));
static int pop_get_passwd      __P ((authority_t));
static char *pop_get_timestamp __P ((pop_data_t));
static int pop_get_md5         __P ((pop_data_t));
139

140
/* This structure holds the info for a message. The pop_message_t
141
   type, will serve as the owner of the message_t and contains the command to
142 143 144 145
   send to "RETR"eive the specify message.  The problem comes from the header.
   If the  POP server supports TOP, we can cleanly fetch the header.
   But otherwise we use the clumsy approach. .i.e for the header we read 'til
   ^\n then discard the rest, for the body we read after ^\n and discard the
Alain Magloire authored
146
   beginning.  This is a waste, Pop was not conceive for this obviously.  */
147
struct _pop_message
148
{
149
  int inbody;
150 151
  int skip_header;
  int skip_body;
152
  size_t body_size;
153
  size_t header_size;
154
  size_t body_lines;
155 156
  size_t header_lines;
  size_t message_size;
157
  size_t num;
158
  char *uidl; /* Cache the uidl string.  */
159
  int attr_flags;
160
  message_t message;
161
  pop_data_t mpd; /* Back pointer.  */
162 163
};

164
/* Structure to hold things general to the POP mailbox, like its state, how
165 166
   many messages we have so far etc ...  */
struct _pop_data
167
{
168
  void *func;  /*  Indicate a command is in operation, busy.  */
169 170
  size_t id;   /* A second level of distincion, we maybe in the same function
		  but working on a different message.  */
171 172
  enum pop_state state;
  pop_message_t *pmessages;
173 174 175
  size_t pmessages_count;
  size_t messages_count;
  size_t size;
176 177 178

  /* Working I/O buffers.  */
  char *buffer;
179 180 181
  size_t buflen; /* Len of buffer.  */
  char *ptr; /* Points to the end of the buffer i.e the non consume chars.  */
  char *nl;  /* Points to the '\n' char in te string.  */
182 183 184
  off_t offset; /* Dummy, this is use because of the stream buffering.
		   The stream_t maintains and offset and the offset we use must
		   be in sync.  */
185

186
  int is_updated;
187 188
  char *user;     /* Temporary holders for user and passwd.  */
  char *passwd;   /* Temporary holders for passwd memset (0) when finish.  */
189
  mailbox_t mbox; /* Back pointer.  */
190 191
} ;

Alain Magloire authored
192
/* Usefull little Macros, since these are very repetitive.  */
193 194

/* Check if we're busy ?  */
Alain Magloire authored
195 196
/* POP is a one channel download protocol, so if someone
   is trying to execute a command while another is running
197 198 199 200 201
   something is seriously incorrect,  So the best course
   of action is to close down the connection and start a new one.
   For example mime_t only reads part of the message.  If a client
   wants to read different part of the message via mime it should
   download it first.  POP does not have the features of IMAP for
202 203 204 205 206 207 208 209 210 211 212
   multipart messages.
   Let see a concrete example:
   {
     mailbox_t mbox; message_t msg; stream_t stream; char buffer[105];
     mailbox_create (&mbox, "pop://qnx.com");
     mailbox_get_message (mbox, 1, &msg);
     message_get_stream (msg, &stream);
     while (stream_readline (stream, buffer, sizeof(buffer), NULL) != 0) { ..}
   }
   if in the while of the readline, one try to get another email.  The pop
   server will get seriously confused, and the second message will still
Alain Magloire authored
213
   be the first one,  There is no way to tell POP servers yo! stop/abort.
214 215 216 217 218 219
   The approach is to close the stream and reopen again. So  every time
   we go in to a function our state is preserve by the triplets
   mpd->{func,state,id}.  The macro CHECK_BUSY checks if we are not
   in another operation if not you get access if yes the stream is close
   and pop_open() is recall again for a new connection.
 */
220 221 222
#define CHECK_BUSY(mbox, mpd, function, identity) \
do \
  { \
223
    int err = monitor_wrlock (mbox->monitor); \
224 225 226 227 228 229
    if (err != 0) \
      return err; \
    if ((mpd->func && mpd->func != function) \
        || (mpd->id && mpd->id != (size_t)identity)) \
      { \
        mpd->id = 0; \
230
        mpd->func = (void *)pop_open; \
231
        mpd->state = POP_NO_STATE; \
232
        monitor_unlock (mbox->monitor); \
233 234 235 236 237 238 239 240 241 242
        err = pop_open (mbox, mbox->flags); \
        if (err != 0) \
          { \
            return err; \
          } \
      } \
    else \
      { \
        mpd->id = (size_t)identity; \
        mpd->func = func; \
243
        monitor_unlock (mbox->monitor); \
244 245 246 247
      } \
  } \
while (0)

248
/* Clear the state.  */
249
#define CLEAR_STATE(mpd) \
250
 mpd->id = 0, mpd->func = NULL, mpd->state = POP_NO_STATE
251

252 253 254 255 256 257 258 259
/* Clear the state and close the stream.  */
#define CHECK_ERROR_CLOSE(mbox, mpd, status) \
do \
  { \
     if (status != 0) \
       { \
          stream_close (mbox->stream); \
          CLEAR_STATE (mpd); \
260
          mpd->func = (void *)-1; \
261
          MAILBOX_DEBUG1(mbox, MU_DEBUG_PROT, "CHECK_ERROR_CLOSE: %s\n", mu_errstring (status));\
262 263 264 265 266
          return status; \
       } \
  } \
while (0)

267
/* If error, clear the state and return.  */
268 269 270 271 272 273
#define CHECK_ERROR(mpd, status) \
do \
  { \
     if (status != 0) \
       { \
          CLEAR_STATE (mpd); \
274
          mpd->func = (void*)-1; \
275
          MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_ERROR: %s\n", mu_errstring (status));\
276 277 278 279 280
          return status; \
       } \
  } \
while (0)

281
/* Clear the state for non recoverable error.  */
282
#define CHECK_EAGAIN(mpd, status) \
283 284 285 286 287 288 289
do \
  { \
    if (status != 0) \
      { \
         if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \
           { \
             CLEAR_STATE (mpd); \
290
             mpd->func = (void *)-1; \
291
             MAILBOX_DEBUG1(mpd->mbox, MU_DEBUG_PROT, "CHECK_EAGAIN: %s\n", mu_errstring (status));\
292 293 294 295 296 297
           } \
         return status; \
      } \
   }  \
while (0)

298

299
/* Allocate mailbox_t, allocate pop internal structures.  */
300 301
int
_mailbox_pop_init (mailbox_t mbox)
302
{
303
  pop_data_t mpd;
304
  int status = 0;
305

306
  /* Allocate specifics for pop data.  */
307 308
  mpd = mbox->data = calloc (1, sizeof (*mpd));
  if (mbox->data == NULL)
309
    return ENOMEM;
310

311
  mpd->mbox = mbox;		/* Back pointer.  */
312

313
  mpd->state = POP_NO_STATE;	/* Init with no state.  */
314

Alain Magloire authored
315
  /* Initialize the structure.  */
316
  mbox->_destroy = pop_destroy;
317

318 319
  mbox->_open = pop_open;
  mbox->_close = pop_close;
320

Alain Magloire authored
321
  /* Messages.  */
322 323
  mbox->_get_message = pop_get_message;
  mbox->_messages_count = pop_messages_count;
324 325
  mbox->_messages_recent = pop_messages_recent;
  mbox->_message_unseen = pop_message_unseen;
326
  mbox->_expunge = pop_expunge;
327

328 329
  mbox->_scan = pop_scan;
  mbox->_is_updated = pop_is_updated;
330

331
  mbox->_get_size = pop_get_size;
332

333 334 335 336 337 338 339 340
  /* Set our properties.  */
  {
    property_t property = NULL;
    mailbox_get_property (mbox, &property);
    property_set_value (property, "TYPE", "POP3", 1);
  }

  /* Hack! POP does not really have a folder.  */
341 342
  mbox->folder->data = mbox;

343
  return status;
344 345
}

346
/*  Cleaning up all the ressources associate with a pop mailbox.  */
347
static void
348
pop_destroy (mailbox_t mbox)
349
{
350
  if (mbox->data)
351
    {
352 353
      pop_data_t mpd = mbox->data;
      size_t i;
354
      monitor_wrlock (mbox->monitor);
355 356
      /* Destroy the pop messages and ressources associated to them.  */
      for (i = 0; i < mpd->pmessages_count; i++)
357
	{
358
	  if (mpd->pmessages[i])
359
	    {
360 361
	      message_destroy (&(mpd->pmessages[i]->message),
			       mpd->pmessages[i]);
362 363
	      if (mpd->pmessages[i]->uidl)
		free (mpd->pmessages[i]->uidl);
364 365
	      free (mpd->pmessages[i]);
	      mpd->pmessages[i] = NULL;
366
	    }
367
	}
368 369 370 371
      if (mpd->buffer)
	free (mpd->buffer);
      if (mpd->pmessages)
	free (mpd->pmessages);
372 373
      free (mpd);
      mbox->data = NULL;
374
      monitor_unlock (mbox->monitor);
375
    }
376 377
}

378 379
/* Simple User/pass authentication for pop. We ask for the info
   from the standard input.  */
380 381
int
_pop_user (authority_t auth)
382
{
383 384
  folder_t folder = authority_get_owner (auth);
  mailbox_t mbox = folder->data;
385
  pop_data_t mpd = mbox->data;
386 387
  int status;

388
  switch (mpd->state)
389
    {
390
    case POP_AUTH:
391 392 393 394 395 396 397 398 399 400 401 402
      /*  Fetch the user from them.  */
      status = pop_get_user (auth);
      if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
	{
	  CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
	}
      status = pop_writeline (mpd, "USER %s\r\n", mpd->user);
      CHECK_ERROR_CLOSE(mbox, mpd, status);
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      free (mpd->user);
      mpd->user = NULL;
      mpd->state = POP_AUTH_USER;
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418

    case POP_AUTH_USER:
      /* Send username.  */
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
      mpd->state = POP_AUTH_USER_ACK;

    case POP_AUTH_USER_ACK:
      /* Get the user ack.  */
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
	{
	  observable_t observable = NULL;
	  mailbox_get_observable (mbox, &observable);
419
	  CLEAR_STATE (mpd);
420 421 422
	  observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
	  CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
	}
423 424
      status = pop_get_passwd (auth);
      if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
425 426 427
	{
	  CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
	}
428
      status = pop_writeline (mpd, "PASS %s\r\n", mpd->passwd);
429
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
430
      /* Leave not trail of the passwd.  */
431
      memset (mpd->passwd, '\0', strlen (mpd->passwd));
432 433
      free (mpd->passwd);
      mpd->passwd = NULL;
434
      CHECK_ERROR_CLOSE (mbox, mpd, status);
435 436 437 438 439 440
      mpd->state = POP_AUTH_PASS;

    case POP_AUTH_PASS:
      /* Send passwd.  */
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
441
      /* Clear the buffer it contains the passwd.  */
442
      memset (mpd->buffer, '\0', mpd->buflen);
443 444 445 446 447 448 449 450 451 452 453
      mpd->state = POP_AUTH_PASS_ACK;

    case POP_AUTH_PASS_ACK:
      /* Get the ack from passwd.  */
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
	{
	  observable_t observable = NULL;
	  mailbox_get_observable (mbox, &observable);
454
	  CLEAR_STATE (mpd);
455 456 457 458 459 460 461 462
	  observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
	  CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
	}
      mpd->state = POP_AUTH_DONE;
      break;  /* We're outta here.  */

    default:
      break;
463
    }
464
  CLEAR_STATE (mpd);
465 466 467
  return 0;
}

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
int
_pop_apop (authority_t auth)
{
  folder_t folder = authority_get_owner (auth);
  mailbox_t mbox = folder->data;
  pop_data_t mpd = mbox->data;
  int status;

  switch (mpd->state)
    {
    case POP_AUTH:
      /* Fetch the user from them.  */
      status = pop_get_user (auth);
      if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0')
	{
	  CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
	}

      /* Fetch the secret from them.  */
      status = pop_get_passwd (auth);
      if (status != 0 || mpd->passwd == NULL || mpd->passwd[0] == '\0')
	{
	  CHECK_ERROR_CLOSE (mbox, mpd, EINVAL);
	}

      /* Make the MD5 digest string.  */
      status = pop_get_md5 (mpd);
      if (status != 0)
	{
	  CHECK_ERROR_CLOSE (mbox, mpd, status);
	}
      status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->passwd);
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      /* We have to obscure the md5 string.  */
      memset (mpd->passwd, '\0', strlen (mpd->passwd));
      free (mpd->user);
      free (mpd->passwd);
      mpd->user = NULL;
      mpd->passwd = NULL;
      CHECK_ERROR_CLOSE (mbox, mpd, status);
      mpd->state = POP_APOP;

    case POP_APOP:
      /* Send apop.  */
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
      /* Clear the buffer it contains the md5.  */
      memset (mpd->buffer, '\0', mpd->buflen);
      mpd->state = POP_APOP_ACK;

    case POP_APOP_ACK:
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
        {
          observable_t observable = NULL;
          mailbox_get_observable (mbox, &observable);
          CLEAR_STATE (mpd);
          observable_notify (observable, MU_EVT_AUTHORITY_FAILED);
          CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
        }
      mpd->state = POP_AUTH_DONE;
      break;  /* We're outta here.  */

    default:
      break;
    }
  CLEAR_STATE (mpd);
  return 0;
}


541
/* Open the connection to the sever, and send the authentication.
542
   FIXME: Should also send the CAPA command to detect for example the suport
543
   for TOP, APOP, ... and DTRT(Do The Right Thing).  */
544
static int
545
pop_open (mailbox_t mbox, int flags)
546
{
547
  pop_data_t mpd = mbox->data;
548
  int status;
549 550 551
  char *host;
  size_t hostlen = 0;
  long port = 110;
552

Alain Magloire authored
553
  /* Sanity checks.  */
554
  if (mpd == NULL)
555 556
    return EINVAL;

557
  /* Fetch the pop server name and the port in the url_t.  */
558 559
  status = url_get_host (mbox->url, NULL, 0, &hostlen);
  if (status != 0)
560
    return status;
561 562 563 564
  host = alloca (hostlen + 1);
  url_get_host (mbox->url, host, hostlen + 1, NULL);
  url_get_port (mbox->url, &port);

565
  mbox->flags = flags;
566

567 568
  /* Do not check for reconnect here.  */
  /* CHECK_BUSY (mbox, mpd, func, 0); */
569

570
  /* Enter the pop state machine, and boogy: AUTHORISATION State.  */
571 572
  switch (mpd->state)
    {
573
    case POP_NO_STATE:
574
      /* Allocate a working io buffer.  */
575 576
      if (mpd->buffer == NULL)
	{
577
	  /* 255 is the limit lenght of a POP3 command according to RFCs.  */
578
	  mpd->buflen = 255;
579
	  mpd->buffer = calloc (mpd->buflen + 1, sizeof (char));
580 581 582 583 584
	  if (mpd->buffer == NULL)
	    {
	      CHECK_ERROR (mpd, ENOMEM);
	    }
	}
585 586
      else
	{
587
	  /* Clear any residual from a previous connection.  */
588 589
	  memset (mpd->buffer, '\0', mpd->buflen);
	}
590
      mpd->ptr = mpd->buffer;
591

592 593 594
      /* Create the networking stack.  */
      if (mbox->stream == NULL)
	{
595
	  status = tcp_stream_create (&mbox->stream, host, port, mbox->flags);
596
	  CHECK_ERROR(mpd, status);
597 598
	  /* Using the awkward stream_t buffering.  */
	  stream_setbufsiz (mbox->stream, BUFSIZ);
599
	}
600
      else
601 602 603 604 605 606 607 608 609 610 611 612
	{
	  /* This is sudden death: for many pop servers, it is important to
	     let them time to remove locks or move the .user.pop files.  This
	     happen when we do BUSY_CHECK().  For example, the user does not
	     want to read the entire file, and wants start to read a new
	     message, closing the connection and immediately contact the
	     server again, and we'll end up having "-ERR Mail Lock busy" or
	     something similar. To prevent this race condition we sleep 2
	     seconds. */
	  stream_close (mbox->stream);
	  pop_sleep (2);
	}
613
      mpd->state = POP_OPEN_CONNECTION;
614

615 616
    case POP_OPEN_CONNECTION:
      /* Establish the connection.  */
617
      MAILBOX_DEBUG2 (mbox, MU_DEBUG_PROT, "open (%s:%d)\n", host, port);
618
      status = stream_open (mbox->stream);
619
      CHECK_EAGAIN (mpd, status);
620
      /* Can't recover bailout.  */
621
      CHECK_ERROR_CLOSE (mbox, mpd, status);
622 623 624
      mpd->state = POP_GREETINGS;

    case POP_GREETINGS:
625 626 627 628 629 630 631 632 633 634 635
      {
	/* Swallow the greetings.  */
	status = pop_read_ack (mpd);
	CHECK_EAGAIN (mpd, status);
	MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
	if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
	  {
	    CHECK_ERROR_CLOSE (mbox, mpd, EACCES);
	  }
	mpd->state = POP_AUTH;
      }
636

637
    case POP_AUTH:
638 639 640 641
    case POP_AUTH_USER:
    case POP_AUTH_USER_ACK:
    case POP_AUTH_PASS:
    case POP_AUTH_PASS_ACK:
642 643
    case POP_APOP:
    case POP_APOP_ACK:
644
      /* Authenticate.  */
645
      status = authority_authenticate (mbox->folder->authority);
646
      CHECK_EAGAIN (mpd, status);
647

648 649
    case POP_AUTH_DONE:
      break;
650

651 652
    default:
      /*
653
	mu_error ("pop_open unknown state\n");
654
      */
655
      break;
656
    }/* End AUTHORISATION state. */
657

Alain Magloire authored
658
  /* Clear any state.  */
659
  CLEAR_STATE (mpd);
660 661 662
  return 0;
}

663
/* Send the QUIT and close the socket.  */
664
static int
665
pop_close (mailbox_t mbox)
666
{
667
  pop_data_t mpd = mbox->data;
668
  void *func = (void *)pop_close;
669
  int status;
670
  size_t i;
671

672
  if (mpd == NULL)
673 674
    return EINVAL;

675
  /* Should not check for Busy, we're shuting down anyway.  */
676
  /* CHECK_BUSY (mbox, mpd, func, 0); */
677
  monitor_wrlock (mbox->monitor);
678
  if (mpd->func && mpd->func != func)
679 680
    mpd->state = POP_NO_STATE;
  mpd->id = 0;
681
  mpd->func = func;
682
  monitor_unlock (mbox->monitor);
683

684 685 686 687
  /*  Ok boys, it's a wrap: UPDATE State.  */
  switch (mpd->state)
    {
    case POP_NO_STATE:
688
      /* Initiate the close.  */
689
      status = pop_writeline (mpd, "QUIT\r\n");
690
      CHECK_ERROR (mpd, status);
691 692
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      mpd->state = POP_QUIT;
693

694
    case POP_QUIT:
695
      /* Send the quit.  */
696 697
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
698 699 700 701
      mpd->state = POP_QUIT_ACK;

    case POP_QUIT_ACK:
      /* Glob the acknowledge.  */
702 703
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
704
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
705
      /*  Now what ! and how can we tell them about errors ?  So far now
706 707
	  lets just be verbose about the error but close the connection
	  anyway.  */
708
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
709
	mu_error ("pop_close: %s\n", mpd->buffer);
710
      stream_close (mbox->stream);
711
      break;
712

713 714
    default:
      /*
715
	mu_error ("pop_close unknow state");
716
      */
717
      break;
718 719
    } /* UPDATE state.  */

720
  /* Free the messages.  */
721
  for (i = 0; i < mpd->pmessages_count; i++)
722
    {
723
      if (mpd->pmessages[i])
724
	{
725 726
	  message_destroy (&(mpd->pmessages[i]->message),
			   mpd->pmessages[i]);
727 728
	  if (mpd->pmessages[i]->uidl)
	    free (mpd->pmessages[i]->uidl);
729
	  free (mpd->pmessages[i]);
730
	  mpd->pmessages[i] = NULL;
731 732
	}
    }
733
  /* And clear any residue.  */
734 735
  if (mpd->pmessages)
    free (mpd->pmessages);
736 737 738
  mpd->pmessages = NULL;
  mpd->pmessages_count = 0;
  mpd->is_updated = 0;
739 740
  if (mpd->buffer)
    free (mpd->buffer);
741
  mpd->buffer = NULL;
742

743
  CLEAR_STATE (mpd);
744 745 746
  return 0;
}

747 748
/*  Only build/setup the message_t structure for a mesgno. pop_message_t,
    will act as the owner of messages.  */
749
static int
750
pop_get_message (mailbox_t mbox, size_t msgno, message_t *pmsg)
751
{
752
  pop_data_t mpd = mbox->data;
753
  message_t msg = NULL;
754
  pop_message_t mpm;
755
  int status;
756 757
  size_t i;

Alain Magloire authored
758
  /* Sanity.  */
759
  if (pmsg == NULL || mpd == NULL)
760 761
    return EINVAL;

762
  monitor_rdlock (mbox->monitor);
763
  /* See if we have already this message.  */
764 765 766 767 768 769 770
  for (i = 0; i < mpd->pmessages_count; i++)
    {
      if (mpd->pmessages[i])
	{
	  if (mpd->pmessages[i]->num == msgno)
	    {
	      *pmsg = mpd->pmessages[i]->message;
771
	      monitor_unlock (mbox->monitor);
772 773 774 775
	      return 0;
	    }
	}
    }
776
  monitor_unlock (mbox->monitor);
777

778 779 780
  mpm = calloc (1, sizeof (*mpm));
  if (mpm == NULL)
    return ENOMEM;
781 782

  /* Back pointer.  */
783 784
  mpm->mpd = mpd;
  mpm->num = msgno;
785

786 787
  /* Create the message.  */
  {
788
    stream_t stream = NULL;
789
    if ((status = message_create (&msg, mpm)) != 0
790
	|| (status = stream_create (&stream, mbox->flags, msg)) != 0)
791
      {
792
	stream_destroy (&stream, msg);
Alain Magloire authored
793
	message_destroy (&msg, mpm);
794 795
	free (mpm);
	return status;
796
      }
797 798
    /* Help for the readline()s  */
    stream_setbufsiz (stream, 128);
799 800
    stream_set_read (stream, pop_message_read, msg);
    stream_set_fd (stream, pop_message_fd, msg);
801
    message_set_stream (msg, stream, mpm);
802
    message_set_size (msg, pop_message_size, mpm);
803
  }
804

Alain Magloire authored
805
  /* Create the header.  */
806
  {
Alain Magloire authored
807
    header_t header = NULL;
808
    if ((status = header_create (&header, NULL, 0,  msg)) != 0)
809
      {
810
	message_destroy (&msg, mpm);
811
	free (mpm);
812 813
	return status;
      }
814
    header_set_fill (header, pop_top, msg);
815
    message_set_header (msg, header, mpm);
816 817
  }

Alain Magloire authored
818
  /* Create the attribute.  */
819 820
  {
    attribute_t attribute;
821
    status = attribute_create (&attribute, msg);
822 823
    if (status != 0)
      {
824
	message_destroy (&msg, mpm);
825
	free (mpm);
826 827
	return status;
      }
828 829 830
    attribute_set_get_flags (attribute, pop_get_attribute, msg);
    attribute_set_set_flags (attribute, pop_set_attribute, msg);
    attribute_set_unset_flags (attribute, pop_unset_attribute, msg);
831
    message_set_attribute (msg, attribute, mpm);
832 833
  }

834
  /* Create the body and its stream.  */
835
  {
Alain Magloire authored
836 837
    body_t body = NULL;
    stream_t stream = NULL;
838 839
    if ((status = body_create (&body, msg)) != 0
	|| (status = stream_create (&stream, mbox->flags, body)) != 0)
840
      {
841 842
	body_destroy (&body, msg);
	stream_destroy (&stream, body);
843
	message_destroy (&msg, mpm);
844
	free (mpm);
845 846
	return status;
      }
847 848
    /* Helps for the readline()s  */
    stream_setbufsiz (stream, 128);
849 850 851 852 853
    stream_set_read (stream, pop_body_read, body);
    stream_set_fd (stream, pop_body_fd, body);
    body_set_size (body, pop_body_size, msg);
    body_set_lines (body, pop_body_lines, msg);
    body_set_stream (body, stream, msg);
854
    message_set_body (msg, body, mpm);
855 856
  }

857
  /* Set the UIDL call on the message. */
858 859 860
  message_set_uidl (msg, pop_uidl, mpm);

  /* Set the UID on the message. */
861
  message_set_uid (msg, pop_uid, mpm);
862

Alain Magloire authored
863
  /* Add it to the list.  */
864
  monitor_wrlock (mbox->monitor);
865
  {
866
    pop_message_t *m ;
867 868 869
    m = realloc (mpd->pmessages, (mpd->pmessages_count + 1)*sizeof (*m));
    if (m == NULL)
      {
870
	message_destroy (&msg, mpm);
871
	free (mpm);
872
	monitor_unlock (mbox->monitor);
873 874 875
	return ENOMEM;
      }
    mpd->pmessages = m;
876
    mpd->pmessages[mpd->pmessages_count] = mpm;
877 878
    mpd->pmessages_count++;
  }
879
  monitor_unlock (mbox->monitor);
880

881
  /* Save The message pointer.  */
882
  message_set_mailbox (msg, mbox, mpm);
883
  *pmsg = mpm->message = msg;
884 885 886 887

  return 0;
}

888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
/* There is no such thing in pop all messages should be consider recent.
   FIXME: We could cheat and peek at the status if it was not strip
   by the server ...  */
static int
pop_messages_recent (mailbox_t mbox, size_t *precent)
{
  return pop_messages_count (mbox, precent);
}

/* There is no such thing in pop all messages should be consider unseen.
   FIXME: We could cheat and peek at the status if it was not strip
   by the server ...  */
static int
pop_message_unseen (mailbox_t mbox, size_t *punseen)
{
  size_t count = 0;
  int status = pop_messages_count (mbox, &count);
  if (status != 0)
    return status;
  if (punseen)
    *punseen = (count > 0) ? 1 : 0;
  return 0;
}

912
/*  How many messages we have.  Done with STAT.  */
913
static int
914
pop_messages_count (mailbox_t mbox, size_t *pcount)
915
{
916
  pop_data_t mpd = mbox->data;
917
  int status;
918
  void *func = (void *)pop_messages_count;
919

920
  if (mpd == NULL)
921 922
    return EINVAL;

923
  /* Do not send a STAT if we know the answer.  */
924
  if (pop_is_updated (mbox))
925 926 927 928 929 930
    {
      if (pcount)
	*pcount = mpd->messages_count;
      return 0;
    }

931
  /* Flag busy.  */
932
  CHECK_BUSY (mbox, mpd, func, 0);
933 934

  /* TRANSACTION state.  */
935 936
  switch (mpd->state)
    {
937
    case POP_NO_STATE:
938 939
      status = pop_writeline (mpd, "STAT\r\n");
      CHECK_ERROR (mpd, status);
940 941
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
      mpd->state = POP_STAT;
942

943
    case POP_STAT:
Alain Magloire authored
944
      /* Send the STAT.  */
945 946
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
947 948 949
      mpd->state = POP_STAT_ACK;

    case POP_STAT_ACK:
Alain Magloire authored
950
      /* Get the ACK.  */
951 952
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
953
      MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
954
      break;
955

956
    default:
957
      /*
958
	mu_error ("pop_messages_count: unknow state\n");
959
      */
960
      break;
961 962
    }

963

964
  /* Parse the answer.  */
965
  status = sscanf (mpd->buffer, "+OK %d %d", &(mpd->messages_count),
966
		   &(mpd->size));
967

968
  /*  Clear the state _after_ the scanf, since another thread could
969
      start writing over mpd->buffer.  */
970 971
  CLEAR_STATE (mpd);

972 973
  if (status == EOF || status != 2)
    return EIO;
974 975 976 977

  if (pcount)
    *pcount = mpd->messages_count;
  mpd->is_updated = 1;
978 979 980
  return 0;
}

Alain Magloire authored
981
/* Update and scanning.  */
982
static int
983
pop_is_updated (mailbox_t mbox)
984
{
985 986
  pop_data_t mpd = mbox->data;
  if (mpd == NULL)
987 988 989 990
    return 0;
  return mpd->is_updated;
}

991
/* We just simulate by sending a notification for the total msgno.  */
992
/* FIXME is message is set deleted should we sent a notif ?  */
993
static int
994
pop_scan (mailbox_t mbox, size_t msgno, size_t *pcount)
995 996 997
{
  int status;
  size_t i;
998
  size_t count = 0;
999

1000 1001 1002
  status = pop_messages_count (mbox, &count);
  if (pcount)
    *pcount = count;
1003 1004
  if (status != 0)
    return status;
1005 1006
  if (mbox->observable == NULL)
    return 0;
1007
  for (i = msgno; i <= count; i++)
1008 1009 1010 1011 1012 1013 1014 1015
    {
      if (observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD) != 0)
	break;
      if (((i +1) % 10) == 0)
	{
	  observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS);
	}
    }
1016 1017 1018
  return 0;
}

1019 1020 1021 1022 1023
/* This is where we actually send the DELE command. Meaning that when
   the attribute on the message is set deleted the comand DELE is not
   sent right away and if we did there is no way to mark a message undeleted
   beside closing down the connection without going to the update state via
   QUIT.  So DELE is send only when in expunge.  */
1024
static int
1025
pop_expunge (mailbox_t mbox)
1026
{
1027
  pop_data_t mpd = mbox->data;
1028 1029
  size_t i;
  attribute_t attr;
1030
  int status;
1031
  void *func = (void *)pop_expunge;
1032

1033
  if (mpd == NULL)
1034
    return EINVAL;
1035

Alain Magloire authored
1036
  /* Busy ?  */
1037
  CHECK_BUSY (mbox, mpd, func, 0);
1038

1039
  for (i = (int)mpd->id; i < mpd->pmessages_count; mpd->id = ++i)
1040
    {
1041
      if (message_get_attribute (mpd->pmessages[i]->message, &attr) == 0)
1042 1043 1044
	{
	  if (attribute_is_deleted (attr))
	    {
1045 1046
	      switch (mpd->state)
		{
1047
		case POP_NO_STATE:
1048 1049 1050
		  status = pop_writeline (mpd, "DELE %d\r\n",
					  mpd->pmessages[i]->num);
		  CHECK_ERROR (mpd, status);
1051 1052
		  MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
		  mpd->state = POP_DELE;
1053

1054
		case POP_DELE:
1055
		  /* Send DELETE.  */
1056 1057
		  status = pop_write (mpd);
		  CHECK_EAGAIN (mpd, status);
1058 1059 1060
		  mpd->state = POP_DELE_ACK;

		case POP_DELE_ACK:
1061
		  /* Ack Delete.  */
1062 1063
		  status = pop_read_ack (mpd);
		  CHECK_EAGAIN (mpd, status);
1064
		  MAILBOX_DEBUG0 (mbox, MU_DEBUG_PROT, mpd->buffer);
1065
		  if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
1066
		    {
1067
		      CHECK_ERROR (mpd, ERANGE);
1068
		    }
1069 1070
		  mpd->state = POP_NO_STATE;
		  break;
1071

1072
		default:
1073
		  /* mu_error ("pop_expunge: unknow state\n"); */
1074
		  break;
1075 1076 1077 1078
		} /* switch (state) */
	    } /* if attribute_is_deleted() */
	} /* message_get_attribute() */
    } /* for */
1079
  CLEAR_STATE (mpd);
1080
  /* Invalidate.  But Really they should shutdown the channel POP protocol
1081
     is not meant for this like IMAP.  */
Alain Magloire authored
1082
  mpd->is_updated = 0;
1083 1084 1085
  return 0;
}

1086
/* Mailbox size ? It is part of the STAT command */
1087
static int
1088
pop_get_size (mailbox_t mbox, off_t *psize)
1089
{
1090
  pop_data_t mpd = mbox->data;
1091
  int status = 0;
1092

1093
  if (mpd == NULL)
1094 1095
    return EINVAL;

1096 1097
  if (! pop_is_updated (mbox))
    status = pop_messages_count (mbox, &mpd->size);
1098 1099 1100 1101 1102
  if (psize)
    *psize = mpd->size;
  return status;
}

1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
/* Form the RFC:
   "It is important to note that the octet count for a message on the
   server host may differ from the octet count assigned to that message
   due to local conventions for designating end-of-line.  Usually,
   during the AUTHORIZATION state of the POP3 session, the POP3 server
   can calculate the size of each message in octets when it opens the
   maildrop.  For example, if the POP3 server host internally represents
   end-of-line as a single character, then the POP3 server simply counts
   each occurrence of this character in a message as two octets."

   This is not perfect if we do not know the number of lines in the message
   then the octets returned will not be correct so we do our best.
 */
1116
static int
1117
pop_message_size (message_t msg, size_t *psize)
1118
{
1119 1120 1121 1122 1123 1124
  pop_message_t mpm = message_get_owner (msg);
  pop_data_t mpd;
  int status = 0;
  void *func = (void *)pop_message_size;
  size_t num;

1125
  if (mpm == NULL)
1126
    return EINVAL;
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162

  /* Did we have it already ?  */
  if (mpm->message_size != 0)
    {
      *psize = mpm->message_size;
      return 0;
    }

  mpd = mpm->mpd;
  /* Busy ? */
  CHECK_BUSY (mpd->mbox, mpd, func, msg);

  /* Get the size.  */
  switch (mpd->state)
    {
    case POP_NO_STATE:
      status = pop_writeline (mpd, "LIST %d\r\n", mpm->num);
      CHECK_ERROR (mpd, status);
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
      mpd->state = POP_LIST;

    case POP_LIST:
      /* Send the LIST.  */
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
      mpd->state = POP_LIST_ACK;

    case POP_LIST_ACK:
      /* Resp from LIST. */
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
      break;

    default:
      /*
1163
	mu_error ("pop_message_size state\n");
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173
      */
      break;
    }

  status = sscanf (mpd->buffer, "+OK %d %d\n", &num, &mpm->message_size);
  CLEAR_STATE (mpd);

  if (status != 2)
    status = EINVAL;

1174 1175
  /* The size of the message is with the extra '\r' octet for everyline.
     Substract to get, hopefully, a good count.  */
1176
  if (psize)
1177
    *psize = mpm->message_size - (mpm->header_lines + mpm->body_lines);
1178 1179 1180
  return 0;
}

1181 1182 1183 1184
/* Another source of trouble, POP only gives the size of the message
   not the size of subparts like headers, body etc .. Again we're doing
   our best with what we know but the only way to get a precise number
   is by dowloading the whole message.  */
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200
static int
pop_body_size (body_t body, size_t *psize)
{
  message_t msg = body_get_owner (body);
  pop_message_t mpm = message_get_owner (msg);

  if (mpm == NULL)
    return EINVAL;

  /* Did we have it already ?  */
  if (mpm->body_size != 0)
    {
      *psize = mpm->body_size;
    }
  else if (mpm->message_size != 0)
    {
1201
      /* Take a guest.  */
1202 1203 1204 1205 1206
      *psize = mpm->message_size - mpm->header_size - mpm->body_lines;
    }
  else
    *psize = 0;

1207 1208 1209
  return 0;
}

1210
/* Not know until the whole message get downloaded.  */
1211
static int
1212
pop_body_lines (body_t body, size_t *plines)
1213
{
1214 1215
  message_t msg = body_get_owner (body);
  pop_message_t mpm = message_get_owner (msg);
1216
  if (mpm == NULL)
1217 1218 1219 1220 1221 1222
    return EINVAL;
  if (plines)
    *plines = mpm->body_lines;
  return 0;
}

1223 1224
/* Pop does not have any command for this, We fake by reading the "Status: "
   header.  But this is hackish some POP server(Qpopper) skip it.  Also
1225 1226 1227 1228 1229 1230 1231 1232 1233
   because we call header_get_value the function may return EAGAIN... uncool.
   To put it another way, many servers simply remove the "Status:" header
   field, when you dowload a message, so a message will always look like
   new even if you already read it.  There is also no way to set an attribute
   on remote mailbox via the POP server and many server once you do a RETR
   and in some cases a TOP will mark the message as read; "Status: RO"
   or maybe worst some ISP configure there servers to delete after
   the RETR some go as much as deleting after the TOP, since technicaly
   you can download a message via TOP without RET'reiving it.  */
1234
static int
1235
pop_get_attribute (attribute_t attr, int *pflags)
1236
{
1237 1238
  message_t msg = attribute_get_owner (attr);
  pop_message_t mpm = message_get_owner (msg);
1239 1240 1241
  char hdr_status[64];
  header_t header = NULL;

1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
  if (mpm == NULL || pflags == NULL)
    return EINVAL;
  if (mpm->attr_flags == 0)
    {
      hdr_status[0] = '\0';
      message_get_header (mpm->message, &header);
      header_get_value (header, "Status", hdr_status, sizeof hdr_status, NULL);
      string_to_flags (hdr_status, &(mpm->attr_flags));
    }
  *pflags = mpm->attr_flags;
  return 0;
}

static int
pop_set_attribute (attribute_t attr, int flags)
{
  message_t msg = attribute_get_owner (attr);
  pop_message_t mpm = message_get_owner (msg);

  if (mpm == NULL)
    return EINVAL;
  mpm->attr_flags |= flags;
  return 0;
}

static int
pop_unset_attribute (attribute_t attr, int flags)
{
  message_t msg = attribute_get_owner (attr);
  pop_message_t mpm = message_get_owner (msg);

1273
  if (mpm == NULL)
1274
    return EINVAL;
1275
  mpm->attr_flags &= ~flags;
1276
  return 0;
1277 1278
}

1279
/* Stub to call the fd from body object.  */
1280 1281 1282 1283 1284 1285 1286 1287 1288
static int
pop_body_fd (stream_t stream, int *pfd)
{
  body_t body = stream_get_owner (stream);
  message_t msg = body_get_owner (body);
  pop_message_t mpm = message_get_owner (msg);
  return pop_get_fd (mpm, pfd);
}

1289
/* Stub to call the fd from message object.  */
1290 1291 1292 1293 1294 1295 1296 1297
static int
pop_message_fd (stream_t stream, int *pfd)
{
  message_t msg = stream_get_owner (stream);
  pop_message_t mpm = message_get_owner (msg);
  return pop_get_fd (mpm, pfd);
}

1298
/* Finally return the fd.  */
1299 1300
static int
pop_get_fd (pop_message_t mpm, int *pfd)
1301
{
1302 1303
  if (mpm && mpm->mpd && mpm->mpd->mbox)
	return stream_get_fd (mpm->mpd->mbox->stream, pfd);
1304 1305 1306
  return EINVAL;
}

1307 1308 1309 1310 1311 1312 1313 1314 1315
static int
pop_uid (message_t msg,  size_t *puid)
{
  pop_message_t mpm = message_get_owner (msg);
  if (puid)
    *puid = mpm->num;
  return 0;
}

1316
/* Get the UIDL.  Client should be prepare since it may fail.  UIDL is
1317 1318
   optional on many POP servers.
   FIXME:  We should check this with CAPA and fall back to a md5 scheme ?
1319
   Or maybe check for "X-UIDL" a la Qpopper ?  */
1320
static int
1321
pop_uidl (message_t msg, char *buffer, size_t buflen, size_t *pnwriten)
1322
{
1323
  pop_message_t mpm = message_get_owner (msg);
1324 1325
  pop_data_t mpd;
  int status = 0;
1326
  void *func = (void *)pop_uidl;
1327
  size_t num;
1328
  /* According to the RFC uidl's are no longer then 70 chars.  Still playit
1329
     safe  */
1330 1331
  char uniq[128];

1332
  if (mpm == NULL)
1333 1334
    return EINVAL;

1335
  /* Is it cache ?  */
1336
  if (mpm->uidl)
1337
    {
1338
      size_t len = strlen (mpm->uidl);
1339 1340 1341 1342
      if (buffer)
	{
	  buflen--; /* Leave space for the null.  */
	  buflen = (len > buflen) ? buflen : len;
1343
	  memcpy (buffer, mpm->uidl, buflen);
1344 1345 1346 1347 1348 1349 1350 1351 1352
	  buffer[buflen] = '\0';
	}
      else
	buflen = len;
      if (pnwriten)
	*pnwriten = buflen;
      return 0;
    }

1353 1354 1355
  mpd = mpm->mpd;

  /* Busy ? */
1356
  CHECK_BUSY (mpd->mbox, mpd, func, 0);
1357 1358 1359 1360 1361

  /* Get the UIDL.  */
  switch (mpd->state)
    {
    case POP_NO_STATE:
1362 1363
      status = pop_writeline (mpd, "UIDL %d\r\n", mpm->num);
      CHECK_ERROR (mpd, status);
1364 1365
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
      mpd->state = POP_UIDL;
1366

1367
    case POP_UIDL:
1368
      /* Send the UIDL.  */
1369 1370
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
1371 1372 1373
      mpd->state = POP_UIDL_ACK;

    case POP_UIDL_ACK:
1374 1375 1376
      /* Resp from UIDL. */
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
1377
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
1378
      break;
1379

1380 1381
    default:
      /*
1382
	mu_error ("pop_uidl state\n");
1383
      */
1384
      break;
1385 1386
    }

1387
  /* FIXME:  I should cache the result.  */
1388
  *uniq = '\0';
1389
  status = sscanf (mpd->buffer, "+OK %d %127s\n", &num, uniq);
1390 1391 1392 1393 1394 1395 1396 1397 1398
  if (status != 2)
    {
      status = EINVAL;
      buflen = 0;
    }
  else
    {
      num = strlen (uniq);
      uniq[num - 1] = '\0'; /* Nuke newline.  */
1399 1400 1401 1402 1403 1404 1405 1406 1407
      if (buffer)
	{
	  buflen--; /* Leave space for the null.  */
	  buflen = (buflen < num) ? buflen : num;
	  memcpy (buffer, uniq, buflen);
	  buffer [buflen] = '\0';
	}
      else
	buflen = num - 1; /* Do not count newline.  */
1408
      mpm->uidl = strdup (uniq);
1409 1410
      status = 0;
    }
1411 1412 1413

  CLEAR_STATE (mpd);

1414 1415 1416 1417 1418
  if (pnwriten)
    *pnwriten = buflen;
  return status;
}

1419 1420
/* How we retrieve the headers.  If it fails we jump to the pop_retr()
   code .i.e send a RETR and skip the body, ugly.
1421 1422 1423
   NOTE: if the offset is different, flag an error, offset is meaningless
   on a socket but we better warn them, some stuff like mime_t may try to
   read ahead, for example for the headers.  */
1424
static int
1425
pop_top (header_t header, char *buffer, size_t buflen,
1426
	 off_t offset, size_t *pnread)
1427
{
1428 1429
  message_t msg = header_get_owner (header);
  pop_message_t mpm = message_get_owner (msg);
1430 1431 1432
  pop_data_t mpd;
  size_t nread = 0;
  int status = 0;
1433
  void *func = (void *)pop_top;
1434

1435
  if (mpm == NULL)
1436 1437 1438 1439
    return EINVAL;

  mpd = mpm->mpd;

1440 1441 1442
  /* Busy ? */
  CHECK_BUSY (mpd->mbox, mpd, func, msg);

1443 1444 1445 1446 1447 1448 1449 1450
  /* We start fresh then reset the sizes.  */
  if (mpd->state == POP_NO_STATE)
    mpm->header_size = 0;

  /* Throw an error if trying to seek back.  */
  if ((size_t)offset < mpm->header_size)
    return ESPIPE;

1451 1452 1453 1454 1455 1456 1457
  /* Get the header.  */
  switch (mpd->state)
    {
    case POP_NO_STATE:
      /* TOP is an optionnal command, if we want to be compliant we can not
	 count on it to exists. So we should be prepare when it fails and
	 fall to a second scheme.  */
1458 1459
      status = pop_writeline (mpd, "TOP %d 0\r\n", mpm->num);
      CHECK_ERROR (mpd, status);
1460 1461
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
      mpd->state = POP_TOP;
1462

1463
    case POP_TOP:
1464
      /* Send the TOP.  */
1465 1466
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
1467 1468 1469 1470
      mpd->state = POP_TOP_ACK;

    case POP_TOP_ACK:
      /* Ack from TOP. */
1471 1472
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
1473
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
1474
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
1475
	{
1476
	  /* mu_error ("TOP not implemented\n"); */
1477 1478 1479 1480 1481
	  /* Fall back to RETR call.  */
	  mpd->state = POP_NO_STATE;
	  mpm->skip_header = 0;
	  mpm->skip_body = 1;
	  return pop_retr (mpm, buffer, buflen, offset, pnread);
1482 1483 1484 1485 1486
	}
      mpd->state = POP_TOP_RX;

    case POP_TOP_RX:
      /* Get the header.  */
1487
      do
1488
	{
1489 1490 1491 1492 1493 1494 1495 1496 1497
	  /* Seek in position.  */
	  ssize_t pos = offset - mpm->header_size;
	  /* Do we need to fill up.  */
	  if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
	    {
	      status = pop_readline (mpd);
	      CHECK_EAGAIN (mpd, status);
	      mpm->header_lines++;
	    }
1498
	  /* If we have to skip some data to get to the offset.  */
1499 1500 1501 1502 1503
	  if (pos > 0)
	    nread = fill_buffer (mpd, NULL, pos);
	  else
	    nread = fill_buffer (mpd, buffer, buflen);
	  mpm->header_size += nread;
1504
	}
1505
      while (nread > 0 && (size_t)offset > mpm->header_size);
1506
      break;
1507

1508
    default:
1509
      /* Probaly TOP was not supported so we have fall back to RETR.  */
1510 1511 1512
      mpm->skip_header = 0;
      mpm->skip_body = 1;
      return pop_retr (mpm, buffer, buflen, offset, pnread);
1513 1514 1515 1516 1517 1518 1519 1520 1521
    } /* switch (state) */

  if (nread == 0)
    {
      CLEAR_STATE (mpd);
    }
  if (pnread)
    *pnread = nread;
  return 0;
1522 1523
}

1524 1525 1526 1527
/* This is no longer use, see pop_top to retreive headers, we still
   keep it around for debugging purposes.  */
#if 0
/* Stub to call pop_retr ().   Call form the stream object of the header.  */
1528
static int
1529
pop_header_read (header_t header, char *buffer, size_t buflen, off_t offset,
1530 1531
		 size_t *pnread)
{
1532
  message_t msg = header_get_owner (header);
1533
  pop_message_t mpm = message_get_owner (msg);
1534
  pop_data_t mpd;
1535
  void *func = (void *)pop_header_read;
1536 1537 1538

  if (mpm == NULL)
    return EINVAL;
1539

1540 1541
  mpd = mpm->mpd;

1542 1543 1544
  /* Busy ? */
  CHECK_BUSY (mpd->mbox, mpd, func, msg);

1545 1546
  /* We start fresh then reset the sizes.  */
  if (mpd->state == POP_NO_STATE)
1547
    mpm->header_size = mpm->inbody = 0;
1548 1549 1550 1551 1552

  /* Throw an error if trying to seek back.  */
  if ((size_t)offset < mpm->header_size)
    return ESPIPE;

1553 1554 1555 1556
  mpm->skip_header = 0;
  mpm->skip_body = 1;
  return pop_retr (mpm, buffer, buflen, offset, pnread);
}
1557
#endif
1558

1559
/* Stub to call pop_retr (). Call from the stream object of the body.  */
1560
static int
1561
pop_body_read (stream_t is, char *buffer, size_t buflen, off_t offset,
1562 1563
	       size_t *pnread)
{
1564 1565 1566
  body_t body = stream_get_owner (is);
  message_t msg = body_get_owner (body);
  pop_message_t mpm = message_get_owner (msg);
1567
  pop_data_t mpd;
1568
  void *func = (void *)pop_body_read;
1569 1570 1571

  if (mpm == NULL)
    return EINVAL;
1572

1573 1574
  mpd = mpm->mpd;

1575 1576 1577
  /* Busy ? */
  CHECK_BUSY (mpd->mbox, mpd, func, msg);

1578 1579
  /* We start fresh then reset the sizes.  */
  if (mpd->state == POP_NO_STATE)
1580
    mpm->body_size = mpm->inbody = 0;
1581 1582 1583 1584 1585

  /* Can not seek back this a stream socket.  */
  if ((size_t)offset < mpm->body_size)
    return ESPIPE;

1586 1587 1588 1589 1590
  mpm->skip_header = 1;
  mpm->skip_body = 0;
  return pop_retr (mpm, buffer, buflen, offset, pnread);
}

1591
/* Stub to call pop_retr (), calling from the stream object of a message.  */
1592
static int
1593 1594
pop_message_read (stream_t is, char *buffer, size_t buflen, off_t offset,
		  size_t *pnread)
1595
{
1596 1597
  message_t msg = stream_get_owner (is);
  pop_message_t mpm = message_get_owner (msg);
1598
  pop_data_t mpd;
1599
  void *func = (void *)pop_message_read;
1600 1601 1602

  if (mpm == NULL)
    return EINVAL;
1603

1604
  mpd = mpm->mpd;
1605

1606 1607 1608
  /* Busy ? */
  CHECK_BUSY (mpd->mbox, mpd, func, msg);

1609 1610
  /* We start fresh then reset the sizes.  */
  if (mpd->state == POP_NO_STATE)
1611
    mpm->header_size = mpm->body_size = mpm->inbody = 0;
1612 1613 1614 1615 1616

  /* Can not seek back this is a stream socket.  */
  if ((size_t)offset < (mpm->body_size + mpm->header_size))
    return ESPIPE;

1617 1618 1619 1620
  mpm->skip_header = mpm->skip_body = 0;
  return pop_retr (mpm, buffer, buflen, offset, pnread);
}

1621
/* Little helper to fill the buffer without overflow.  */
1622
static int
1623
fill_buffer (pop_data_t mpd, char *buffer, size_t buflen)
1624 1625
{
  int nleft, n, nread = 0;
1626 1627 1628

  /* How much we can copy ?  */
  n = mpd->ptr - mpd->buffer;
1629
  nleft = buflen - n;
1630 1631 1632

 /* We got more then requested.  */
  if (nleft < 0)
1633
    {
1634
      size_t sentinel;
1635
      nread = buflen;
1636
      sentinel = mpd->ptr - (mpd->buffer + nread);
1637 1638
      if (buffer)
	memcpy (buffer, mpd->buffer, nread);
1639 1640
      memmove (mpd->buffer, mpd->buffer + nread, sentinel);
      mpd->ptr = mpd->buffer + sentinel;
1641 1642 1643
    }
  else
    {
1644
      /* Drain our buffer.  */;
1645
      nread = n;
1646 1647
      if (buffer)
	memcpy (buffer, mpd->buffer, nread);
1648
      mpd->ptr = mpd->buffer;
1649
    }
1650

1651 1652 1653
  return nread;
}

1654
/* The heart of most funtions.  Send the RETR and skip different parts.  */
1655 1656 1657
static int
pop_retr (pop_message_t mpm, char *buffer, size_t buflen, off_t offset,
	  size_t *pnread)
1658
{
1659
  pop_data_t mpd;
1660 1661
  size_t nread = 0;
  int status = 0;
1662
  size_t oldbuflen = buflen;
1663

1664
  /* Meaningless.  */
1665 1666
  (void)offset;

1667 1668
  mpd = mpm->mpd;

1669 1670
  if (pnread)
    *pnread = nread;
1671

1672
  /*  Take care of the obvious.  */
1673
  if (buffer == NULL || buflen == 0)
1674 1675 1676 1677
    {
      CLEAR_STATE (mpd);
      return 0;
    }
1678

1679
  /* pop_retr() is not call directly so we assume that the locks were set.  */
1680 1681 1682

  switch (mpd->state)
    {
1683
    case POP_NO_STATE:
1684
      mpm->body_lines = mpm->body_size = 0;
1685
      status = pop_writeline (mpd, "RETR %d\r\n", mpm->num);
1686
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);
1687
      CHECK_ERROR (mpd, status);
1688
      mpd->state = POP_RETR;
1689

1690
    case POP_RETR:
1691
      /* Send the RETR command.  */
1692 1693
      status = pop_write (mpd);
      CHECK_EAGAIN (mpd, status);
1694 1695 1696
      mpd->state = POP_RETR_ACK;

    case POP_RETR_ACK:
Alain Magloire authored
1697
      /* RETR ACK.  */
1698 1699
      status = pop_read_ack (mpd);
      CHECK_EAGAIN (mpd, status);
1700 1701
      MAILBOX_DEBUG0 (mpd->mbox, MU_DEBUG_PROT, mpd->buffer);

1702
      if (strncasecmp (mpd->buffer, "+OK", 3) != 0)
1703
	{
1704
	  CHECK_ERROR (mpd, EACCES);
1705
	}
1706 1707 1708
      mpd->state = POP_RETR_RX_HDR;

    case POP_RETR_RX_HDR:
1709
      /* Skip/Take the header.  */
1710
      while (!mpm->inbody)
1711
        {
1712
	  /* Do we need to fill up.  */
1713 1714 1715 1716 1717 1718 1719 1720 1721 1722
	  if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
	    {
	      status = pop_readline (mpd);
	      if (status != 0)
		{
		  /* Do we have something in the buffer flush it first.  */
		  if (buflen != oldbuflen)
		    return 0;
		  CHECK_EAGAIN (mpd, status);
		}
1723
	      mpm->header_lines++;
1724 1725 1726
	    }
	  /* Oops !! Hello houston we have a major problem here.  */
	  if (mpd->buffer[0] == '\0')
1727
	    {
1728
	      /* Still Do the right thing.  */
1729 1730 1731 1732 1733 1734
	      if (buflen != oldbuflen)
		{
		  CLEAR_STATE (mpd);
		}
	      else
		mpd->state = POP_STATE_DONE;
1735
	      return 0;
1736
	    }
1737 1738 1739 1740 1741 1742 1743 1744
	  /* The problem is that we are using RETR instead of TOP to retreive
	     headers, i.e the server contacted does not support it.  So we
	     have to make sure that the next line really means end of the
	     headers.  Still some cases we may loose.  But 99.9% of POPD
	     encounter support TOP.  In the 0.1% case get GNU pop3d, or the
	     hack below will suffice.  */
	  if (mpd->buffer[0] == '\n' && mpd->buffer[1] == '\0')
	    mpm->inbody = 1; /* break out of the while.  */
1745
	  if (!mpm->skip_header)
1746
	    {
1747
	      ssize_t pos = offset  - mpm->header_size;
1748
	      if (pos > 0)
1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764
		{
		  nread = fill_buffer (mpd, NULL, pos);
		  mpm->header_size += nread;
		}
	      else
		{
		  nread = fill_buffer (mpd, buffer, buflen);
		  mpm->header_size += nread;
		  if (pnread)
		    *pnread += nread;
		  buflen -= nread ;
		  if (buflen > 0)
		    buffer += nread;
		  else
		    return 0;
		}
1765 1766
	    }
	  else
1767
	    mpd->ptr = mpd->buffer;
1768
	}
1769 1770 1771
      mpd->state = POP_RETR_RX_BODY;

    case POP_RETR_RX_BODY:
1772
      /* Start/Take the body.  */
1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791
      while (mpm->inbody)
	{
	  /* Do we need to fill up.  */
	  if (mpd->nl == NULL || mpd->ptr == mpd->buffer)
	    {
	      status = pop_readline (mpd);
	      if (status != 0)
		{
		  /* Flush The Buffer ?  */
		  if (buflen != oldbuflen)
		    return 0;
		  CHECK_EAGAIN (mpd, status);
		}
	      mpm->body_lines++;
	    }

	    if (mpd->buffer[0] == '\0')
	      mpm->inbody = 0; /* Breakout of the while.  */

1792
	    if (!mpm->skip_body)
1793
	      {
1794 1795 1796 1797 1798
		/* If we did not skip the header, it means that we are
		   downloading the entire message and the header_size should be
		   part of the offset count.  */
		ssize_t pos = offset - (mpm->body_size + ((mpm->skip_header) ?
					0 : mpm->header_size));
1799 1800 1801 1802 1803
		if (pos > 0)
		  {
		    nread = fill_buffer (mpd, NULL, pos);
		    mpm->body_size += nread;
		  }
1804
		else
1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815
		  {
		    nread = fill_buffer (mpd, buffer, buflen);
		    mpm->body_size += nread;
		    if (pnread)
		      *pnread += nread;
		    buflen -= nread ;
		    if (buflen > 0)
		      buffer += nread;
		    else
		      return 0;
		  }
1816 1817 1818
	      }
	    else
	      {
1819 1820
		mpm->body_size += (mpd->ptr - mpd->buffer);
		mpd->ptr = mpd->buffer;
1821
	      }
1822
	  }
1823
      mpm->message_size = mpm->body_size + mpm->header_size;
1824
      mpd->state = POP_STATE_DONE;
1825
      /* Return here earlier, because we want to return nread = 0 to notify
1826
	 the callee that we've finish, since there is already data
1827 1828 1829 1830
	 we have to return them first and _then_ tell them its finish.  If we
	 don't we will start over again by sending another RETR.  */
      if (buflen != oldbuflen)
	return 0;
1831

1832
    case POP_STATE_DONE:
1833
      /* A convenient break, this is here we can return 0, we're done.  */
1834

1835
    default:
1836
      /* mu_error ("pop_retr unknow state\n"); */
1837
      break;
Alain Magloire authored
1838
    } /* Switch state.  */
1839

1840 1841
  CLEAR_STATE (mpd);
  mpm->skip_header = mpm->skip_body = 0;
1842 1843 1844
  return 0;
}

1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865
/* Extract the User from the URL or the ticket.  */
static int
pop_get_user (authority_t auth)
{
  folder_t folder = authority_get_owner (auth);
  mailbox_t mbox = folder->data;
  pop_data_t mpd = mbox->data;
  ticket_t ticket = NULL;
  int status;
  /*  Fetch the user from them.  */
  size_t n = 0;

  authority_get_ticket (auth, &ticket);
  if (mpd->user)
    {
      free (mpd->user);
      mpd->user = NULL;
    }
  /* Was it in the URL? */
  status = url_get_user (mbox->url, NULL, 0, &n);
  if (status != 0 || n == 0)
1866
    ticket_pop (ticket, mbox->url, "Pop User: ",  &mpd->user);
1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895
  else
    {
      mpd->user = calloc (1, n + 1);
      url_get_user (mbox->url, mpd->user, n + 1, NULL);
    }
  return 0;
}

/* Extract the User from the URL or the ticket.  */
static int
pop_get_passwd (authority_t auth)
{
  folder_t folder = authority_get_owner (auth);
  mailbox_t mbox = folder->data;
  pop_data_t mpd = mbox->data;
  ticket_t ticket = NULL;
  int status;
  /*  Fetch the user from them.  */
  size_t n = 0;

  authority_get_ticket (auth, &ticket);
  if (mpd->passwd)
    {
      free (mpd->passwd);
      mpd->passwd = NULL;
    }
  /* Was it in the URL? */
  status = url_get_passwd (mbox->url, NULL, 0, &n);
  if (status != 0 || n == 0)
1896
    ticket_pop (ticket, mbox->url, "Pop Passwd: ",  &mpd->passwd);
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
  else
    {
      mpd->passwd = calloc (1, n + 1);
      url_get_passwd (mbox->url, mpd->passwd, n + 1, NULL);
    }
  return 0;
}


static char *
pop_get_timestamp (pop_data_t mpd)
{
  char *right, *left;
  char *timestamp = NULL;
  size_t len;

  len = strlen (mpd->buffer);
  right = memchr (mpd->buffer, '<', len);
  if (right)
    {
      len = len - (right - mpd->buffer);
      left = memchr (right, '>', len);
      if (left)
	{
	  len = left - right + 1;
	  timestamp = calloc (len + 1, 1);
	  if (timestamp != NULL)
	    {
	      memcpy (timestamp, right, len);
	    }
	}
    }
  return timestamp;
}

/*  Make the MD5 string.  */
static int
pop_get_md5 (pop_data_t mpd)
{
  MD5_CTX md5context;
  unsigned char md5digest[16];
  char digest[64]; /* Really it just has to be 32 + 1(null).  */
  char *tmp;
  size_t n;
  char *timestamp;

  timestamp = pop_get_timestamp (mpd);
  if (timestamp == NULL)
    return EINVAL;

  MD5Init (&md5context);
  MD5Update (&md5context, (unsigned char *)timestamp, strlen (timestamp));
  MD5Update (&md5context, (unsigned char *)mpd->passwd, strlen (mpd->passwd));
  MD5Final (md5digest, &md5context);
  for (tmp = digest, n = 0; n < 16; n++, tmp += 2)
    sprintf (tmp, "%02x", md5digest[n]);
  *tmp = '\0';
  free (timestamp);
  free (mpd->passwd);
  mpd->passwd = strdup (digest);
  return 0;
}

1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970
/* GRRRRR!!  We can not use sleep in the library since this we'll
   muck up any alarm() done by the user.  */
static int
pop_sleep (int seconds)
{
  struct timeval tval;
  tval.tv_sec = seconds;
  tval.tv_usec = 0;
  return select (1, NULL, NULL, NULL, &tval);
}

1971 1972 1973 1974
/* C99 says that a conforming implementation of snprintf () should return the
   number of char that would have been call but many old GNU/Linux && BSD
   implementations return -1 on error.  Worse QnX/Neutrino actually does not
   put the terminal null char.  So let's try to cope.  */
1975
static int
1976
pop_writeline (pop_data_t mpd, const char *format, ...)
1977
{
1978 1979
  int len;
  va_list ap;
1980
  int done = 1;
1981

Alain Magloire authored
1982 1983
  if (mpd->buffer == NULL)
    return EINVAL;
1984 1985
  va_start(ap, format);
  do
1986
    {
1987
      len = vsnprintf (mpd->buffer, mpd->buflen - 1, format, ap);
1988 1989
      if (len < 0 || len >= (int)mpd->buflen
	  || !memchr (mpd->buffer, '\0', len + 1))
1990 1991 1992 1993 1994
	{
	  mpd->buflen *= 2;
	  mpd->buffer = realloc (mpd->buffer, mpd->buflen);
	  if (mpd->buffer == NULL)
	    return ENOMEM;
1995
	  done = 0;
1996
	}
1997 1998
      else
	done = 1;
1999
    }
2000
  while (!done);
2001 2002 2003
  va_end(ap);
  mpd->ptr = mpd->buffer + len;
  return 0;
2004 2005
}

2006 2007
/* A socket may write less then expected and we have to cope with nonblocking.
   if the write failed we keep track and restart where left.  */
2008
static int
2009
pop_write (pop_data_t mpd)
2010
{
2011 2012
  int status = 0;
  if (mpd->ptr > mpd->buffer)
2013
    {
2014
      size_t len;
2015
      size_t n = 0;
2016
      len = mpd->ptr - mpd->buffer;
2017
      status = stream_write (mpd->mbox->stream, mpd->buffer, len, 0, &n);
2018 2019
      if (status == 0)
	{
2020 2021
	  memmove (mpd->buffer, mpd->buffer + n, len - n);
	  mpd->ptr -= n;
2022
	}
2023
    }
2024
  else
2025
    mpd->ptr = mpd->buffer;
2026
  return status;
2027 2028
}

2029 2030
/* Call readline and reset the mpd->ptr to the buffer, signalling that we have
   done the read to completion. */
2031
static int
2032
pop_read_ack (pop_data_t mpd)
2033
{
2034 2035 2036 2037
  int status = pop_readline (mpd);
  if (status == 0)
    mpd->ptr = mpd->buffer;
  return status;
2038 2039
}

2040
/* Read a complete line form the pop server. Transform CRLF to LF, remove
2041 2042
   the stuff byte termination octet ".", put a null in the buffer
   when done.  */
2043
static int
2044
pop_readline (pop_data_t mpd)
2045
{
2046 2047
  size_t n = 0;
  size_t total = mpd->ptr - mpd->buffer;
2048 2049
  int status;

2050 2051
  /* Must get a full line before bailing out.  */
  do
2052
    {
2053
      status = stream_readline (mpd->mbox->stream, mpd->buffer + total,
2054
				mpd->buflen - total,  mpd->offset, &n);
2055 2056
      if (status != 0)
	return status;
2057

2058 2059 2060 2061 2062
      /* The server went away:  It maybe a timeout and some pop server
	 does not send the -ERR.  Consider this like an error.  */
      if (n == 0)
	return EIO;

2063
      total += n;
2064
      mpd->offset += n;
2065 2066
      mpd->nl = memchr (mpd->buffer, '\n', total);
      if (mpd->nl == NULL)  /* Do we have a full line.  */
2067
	{
2068 2069
	  /* Allocate a bigger buffer ?  */
	  if (total >= mpd->buflen -1)
2070
	    {
2071 2072 2073 2074
	      mpd->buflen *= 2;
	      mpd->buffer = realloc (mpd->buffer, mpd->buflen + 1);
	      if (mpd->buffer == NULL)
		return ENOMEM;
2075 2076
	    }
	}
2077
      mpd->ptr = mpd->buffer + total;
2078
    }
2079
  while (mpd->nl == NULL);
2080

2081 2082 2083 2084 2085
  /* When examining a multi-line response, the client checks to see if the
     line begins with the termination octet "."(DOT). If yes and if octets
     other than CRLF follow, the first octet of the line (the termination
     octet) is stripped away.  */
  if (total >= 3  && mpd->buffer[0] == '.')
2086
    {
2087
      if (mpd->buffer[1] != '\r' && mpd->buffer[2] != '\n')
2088
	{
2089 2090 2091
	  memmove (mpd->buffer, mpd->buffer + 1, total - 1);
	  mpd->ptr--;
	  mpd->nl--;
2092
	}
2093 2094 2095 2096
      /* And if CRLF immediately follows the termination character, then the
	 response from the POP server is ended and the line containing
	 ".CRLF" is not considered part of the multi-line response.  */
      else if (mpd->buffer[1] == '\r' && mpd->buffer[2] == '\n')
2097
	{
2098 2099 2100
	  mpd->buffer[0] = '\0';
	  mpd->ptr = mpd->buffer;
	  mpd->nl = NULL;
2101 2102
	}
    }
2103 2104 2105 2106 2107 2108 2109
  /* \r\n --> \n\0, conversion.  */
  if (mpd->nl > mpd->buffer)
    {
      *(mpd->nl - 1) = '\n';
      *(mpd->nl) = '\0';
      mpd->ptr = mpd->nl;
    }
2110
  return 0;
2111
}