]> Pileus Git - ~andy/fetchmail/blob - driver.c
Comment fixes.
[~andy/fetchmail] / driver.c
1 /*
2  * driver.c -- generic driver for mail fetch method protocols
3  *
4  * Copyright 1997 by Eric S. Raymond
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include  "config.h"
9 #include  <stdio.h>
10 #include  <setjmp.h>
11 #include  <errno.h>
12 #include  <string.h>
13 #ifdef HAVE_MEMORY_H
14 #include  <memory.h>
15 #endif /* HAVE_MEMORY_H */
16 #if defined(STDC_HEADERS)
17 #include  <stdlib.h>
18 #endif
19 #if defined(HAVE_UNISTD_H)
20 #include <unistd.h>
21 #endif
22 #if defined(HAVE_SYS_ITIMER_H)
23 #include <sys/itimer.h>
24 #endif
25 #include  <sys/time.h>
26 #include  <signal.h>
27
28 #ifdef HAVE_NET_SOCKET_H
29 #include <net/socket.h>
30 #endif
31
32 #ifdef HAVE_RES_SEARCH
33 #include <netdb.h>
34 #include "mx.h"
35 #endif /* HAVE_RES_SEARCH */
36
37 #include "kerberos.h"
38 #ifdef KERBEROS_V4
39 #include <netinet/in.h>
40 #endif /* KERBEROS_V4 */
41
42 #include "i18n.h"
43 #include "socket.h"
44
45 #include "fetchmail.h"
46 #include "tunable.h"
47
48 /* throw types for runtime errors */
49 #define THROW_TIMEOUT   1               /* server timed out */
50 #define THROW_SIGPIPE   2               /* SIGPIPE on stream socket */
51
52 /* magic values for the message length array */
53 #define MSGLEN_UNKNOWN  0               /* length unknown (0 is impossible) */
54 #define MSGLEN_INVALID  -1              /* length passed back is invalid */
55 #define MSGLEN_TOOLARGE -2              /* message is too large */
56 #define MSGLEN_OLD      -3              /* message is old */
57
58 int pass;               /* how many times have we re-polled? */
59 int stage;              /* where are we? */
60 int phase;              /* where are we, for error-logging purposes? */
61 int batchcount;         /* count of messages sent in current batch */
62 flag peek_capable;      /* can we peek for better error recovery? */
63
64 static int timeoutcount;                /* count consecutive timeouts */
65
66 static jmp_buf  restart;
67
68 void set_timeout(int timeleft)
69 /* reset the nonresponse-timeout */
70 {
71 #if !defined(__EMX__) && !defined(__BEOS__) 
72     struct itimerval ntimeout;
73
74     if (timeleft == 0)
75         timeoutcount = 0;
76
77     ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
78     ntimeout.it_value.tv_sec  = timeleft;
79     ntimeout.it_value.tv_usec = 0;
80     setitimer(ITIMER_REAL, &ntimeout, (struct itimerval *)NULL);
81 #endif
82 }
83
84 static void timeout_handler (int signal)
85 /* handle SIGALRM signal indicating a server timeout */
86 {
87     timeoutcount++;
88     longjmp(restart, THROW_TIMEOUT);
89 }
90
91 static void sigpipe_handler (int signal)
92 /* handle SIGPIPE signal indicating a broken stream socket */
93 {
94     longjmp(restart, THROW_SIGPIPE);
95 }
96
97 #ifdef KERBEROS_V4
98 static int kerberos_auth(socket, canonical, principal) 
99 /* authenticate to the server host using Kerberos V4 */
100 int socket;             /* socket to server host */
101 char *canonical;        /* server name */
102 char *principal;
103 {
104     char * host_primary;
105     KTEXT ticket;
106     MSG_DAT msg_data;
107     CREDENTIALS cred;
108     Key_schedule schedule;
109     int rem;
110     char * prin_copy = (char *) NULL;
111     char * prin = (char *) NULL;
112     char * inst = (char *) NULL;
113     char * realm = (char *) NULL;
114
115     if (principal != (char *)NULL && *principal)
116     {
117         char *cp;
118         prin = prin_copy = xstrdup(principal);
119         for (cp = prin_copy; *cp && *cp != '.'; ++cp)
120             ;
121         if (*cp)
122         {
123             *cp++ = '\0';
124             inst = cp;
125             while (*cp && *cp != '@')
126                 ++cp;
127             if (*cp)
128             {
129                 *cp++ = '\0';
130                 realm = cp;
131             }
132         }
133     }
134   
135     xalloca(ticket, KTEXT, sizeof (KTEXT_ST));
136     rem = (krb_sendauth (0L, socket, ticket,
137                          prin ? prin : "pop",
138                          inst ? inst : canonical,
139                          realm ? realm : ((char *) (krb_realmofhost (canonical))),
140                          ((unsigned long) 0),
141                          (&msg_data),
142                          (&cred),
143                          (schedule),
144                          ((struct sockaddr_in *) 0),
145                          ((struct sockaddr_in *) 0),
146                          "KPOPV0.1"));
147     if (prin_copy)
148     {
149         free(prin_copy);
150     }
151     if (rem != KSUCCESS)
152     {
153         report(stderr, _("kerberos error %s\n"), (krb_get_err_text (rem)));
154         return (PS_AUTHFAIL);
155     }
156     return (0);
157 }
158 #endif /* KERBEROS_V4 */
159
160 #ifdef KERBEROS_V5
161 static int kerberos5_auth(socket, canonical)
162 /* authenticate to the server host using Kerberos V5 */
163 int socket;             /* socket to server host */
164 const char *canonical;  /* server name */
165 {
166     krb5_error_code retval;
167     krb5_context context;
168     krb5_ccache ccdef;
169     krb5_principal client = NULL, server = NULL;
170     krb5_error *err_ret = NULL;
171
172     krb5_auth_context auth_context = NULL;
173
174     krb5_init_context(&context);
175     krb5_init_ets(context);
176     krb5_auth_con_init(context, &auth_context);
177
178     if (retval = krb5_cc_default(context, &ccdef)) {
179         report(stderr, "krb5_cc_default: %s\n", error_message(retval));
180         return(PS_ERROR);
181     }
182
183     if (retval = krb5_cc_get_principal(context, ccdef, &client)) {
184         report(stderr, "krb5_cc_get_principal: %s\n", error_message(retval));
185         return(PS_ERROR);
186     }
187
188     if (retval = krb5_sname_to_principal(context, canonical, "pop",
189            KRB5_NT_UNKNOWN,
190            &server)) {
191         report(stderr, "krb5_sname_to_principal: %s\n", error_message(retval));
192         return(PS_ERROR);
193     }
194
195     retval = krb5_sendauth(context, &auth_context, (krb5_pointer) &socket,
196          "KPOPV1.0", client, server,
197          AP_OPTS_MUTUAL_REQUIRED,
198          NULL,  /* no data to checksum */
199          0,   /* no creds, use ccache instead */
200          ccdef,
201          &err_ret, 0,
202
203          NULL); /* don't need reply */
204
205     krb5_free_principal(context, server);
206     krb5_free_principal(context, client);
207     krb5_auth_con_free(context, auth_context);
208
209     if (retval) {
210 #ifdef HEIMDAL
211       if (err_ret && err_ret->e_text) {
212           report(stderr, _("krb5_sendauth: %s [server says '%*s'] \n"),
213                  error_message(retval),
214                  err_ret->e_text);
215 #else
216       if (err_ret && err_ret->text.length) {
217           report(stderr, _("krb5_sendauth: %s [server says '%*s'] \n"),
218                  error_message(retval),
219                  err_ret->text.length,
220                  err_ret->text.data);
221 #endif
222           krb5_free_error(context, err_ret);
223       } else
224           report(stderr, "krb5_sendauth: %s\n", error_message(retval));
225       return(PS_ERROR);
226     }
227
228     return 0;
229 }
230 #endif /* KERBEROS_V5 */
231
232 static void clean_skipped_list(struct idlist **skipped_list)
233 /* struct "idlist" contains no "prev" ptr; we must remove unused items first */
234 {
235     struct idlist *current=NULL, *prev=NULL, *tmp=NULL, *head=NULL;
236     prev = current = head = *skipped_list;
237
238     if (!head)
239         return;
240     do
241     {
242         /* if item has no reference, remove it */
243         if (current && current->val.status.mark == 0)
244         {
245             if (current == head) /* remove first item (head) */
246             {
247                 head = current->next;
248                 if (current->id) free(current->id);
249                 free(current);
250                 prev = current = head;
251             }
252             else /* remove middle/last item */
253             {
254                 tmp = current->next;
255                 prev->next = tmp;
256                 if (current->id) free(current->id);
257                 free(current);
258                 current = tmp;
259             }
260         }
261         else /* skip this item */
262         {
263             prev = current;
264             current = current->next;
265         }
266     } while(current);
267
268     *skipped_list = head;
269 }
270
271 static void send_size_warnings(struct query *ctl)
272 /* send warning mail with skipped msg; reset msg count when user notified */
273 {
274     int size, nbr;
275     int msg_to_send = FALSE;
276     struct idlist *head=NULL, *current=NULL;
277     int max_warning_poll_count;
278
279     head = ctl->skipped;
280     if (!head)
281         return;
282
283     /* don't start a notification message unless we need to */
284     for (current = head; current; current = current->next)
285         if (current->val.status.num == 0 && current->val.status.mark)
286             msg_to_send = TRUE;
287     if (!msg_to_send)
288         return;
289
290     /*
291      * There's no good way to recover if we can't send notification mail, 
292      * but it's not a disaster, either, since the skipped mail will not
293      * be deleted.
294      */
295     if (open_warning_by_mail(ctl, (struct msgblk *)NULL))
296         return;
297     stuff_warning(ctl,
298            _("Subject: Fetchmail oversized-messages warning.\r\n"
299              "\r\n"
300              "The following oversized messages remain on the mail server %s:"),
301                   ctl->server.pollname);
302  
303     if (run.poll_interval == 0)
304         max_warning_poll_count = 0;
305     else
306         max_warning_poll_count = ctl->warnings/run.poll_interval;
307
308     /* parse list of skipped msg, adding items to the mail */
309     for (current = head; current; current = current->next)
310     {
311         if (current->val.status.num == 0 && current->val.status.mark)
312         {
313             nbr = current->val.status.mark;
314             size = atoi(current->id);
315             stuff_warning(ctl, 
316                     _("\t%d msg %d octets long skipped by fetchmail.\r\n"),
317                     nbr, size);
318         }
319         current->val.status.num++;
320         current->val.status.mark = 0;
321
322         if (current->val.status.num >= max_warning_poll_count)
323             current->val.status.num = 0;
324     }
325
326     close_warning_by_mail(ctl, (struct msgblk *)NULL);
327 }
328
329 static void mark_oversized(int num, struct query *ctl, int *msgsizes)
330 /* mark a message oversized */
331 {
332     struct idlist *current=NULL, *tmp=NULL;
333     char size[32];
334     int cnt;
335
336     /* convert sz to string */
337 #ifdef HAVE_SNPRINTF
338     snprintf(size, sizeof(size),
339 #else
340     sprintf(size,
341 #endif /* HAVE_SNPRINTF */
342       "%d", msgsizes[num-1]);
343
344     /* build a list of skipped messages
345      * val.id = size of msg (string cnvt)
346      * val.status.num = warning_poll_count
347      * val.status.mask = nbr of msg this size
348      */
349
350     current = ctl->skipped;
351
352     /* initialise warning_poll_count to the
353      * current value so that all new msg will
354      * be included in the next mail
355      */
356     cnt = current ? current->val.status.num : 0;
357
358     /* if entry exists, increment the count */
359     if (current && str_in_list(&current, size, FALSE))
360     {
361         for ( ; current; current = current->next)
362         {
363             if (strcmp(current->id, size) == 0)
364             {
365                 current->val.status.mark++;
366                 break;
367             }
368         }
369     }
370     /* otherwise, create a new entry */
371     /* initialise with current poll count */
372     else
373     {
374         tmp = save_str(&ctl->skipped, size, 1);
375         tmp->val.status.num = cnt;
376     }
377 }
378
379 static int fetch_messages(int mailserver_socket, struct query *ctl, 
380                           int count, int *msgsizes, int maxfetch,
381                           int *fetches, int *dispatches, int *deletions)
382 /* fetch messages in lockstep mode */
383 {
384     int num, err, len;
385
386     for (num = 1; num <= count; num++)
387     {
388         flag suppress_delete = FALSE;
389         flag suppress_forward = FALSE;
390         flag suppress_readbody = FALSE;
391         flag retained = FALSE;
392
393         if (msgsizes[num-1] < 0)
394         {
395             if ((msgsizes[num-1] == MSGLEN_TOOLARGE) && !check_only)
396                 mark_oversized(num, ctl, msgsizes);
397             if (outlevel > O_SILENT)
398             {
399                 report_build(stdout, 
400                              _("skipping message %d (%d octets)"),
401                              num, msgsizes[num-1]);
402                 switch (msgsizes[num-1])
403                 {
404                 case MSGLEN_INVALID:
405                     /*
406                      * Invalid lengths are produced by Post Office/NT's
407                      * annoying habit of randomly prepending bogus
408                      * LIST items of length -1.  Patrick Audley
409                      * <paudley@pobox.com> tells us: LIST shows a
410                      * size of -1, RETR and TOP return "-ERR
411                      * System error - couldn't open message", and
412                      * DELE succeeds but doesn't actually delete
413                      * the message.
414                      */
415                     report_build(stdout, _(" (length -1)"));
416                     break;
417                 case MSGLEN_TOOLARGE:
418                     report_build(stdout, 
419                                  _(" (oversized, %d octets)"),
420                                  msgsizes[num-1]);
421                     break;
422                 }
423             }
424         }
425         else
426         {
427             flag wholesize = !ctl->server.base_protocol->fetch_body;
428
429             /* request a message */
430             err = (ctl->server.base_protocol->fetch_headers)(mailserver_socket,ctl,num, &len);
431             if (err != 0)
432                 return(err);
433
434             /* -1 means we didn't see a size in the response */
435             if (len == -1 && msgsizes)
436             {
437                 len = msgsizes[num - 1];
438                 wholesize = TRUE;
439             }
440
441             if (outlevel > O_SILENT)
442             {
443                 report_build(stdout, _("reading message %d of %d"),
444                              num,count);
445
446                 if (len > 0)
447                     report_build(stdout, _(" (%d %soctets)"),
448                                  len, wholesize ? "" : _("header "));
449                 if (outlevel >= O_VERBOSE)
450                     report_complete(stdout, "\n");
451                 else
452                     report_complete(stdout, " ");
453             }
454
455             /* 
456              * Read the message headers and ship them to the
457              * output sink.  
458              */
459             err = readheaders(mailserver_socket, len, msgsizes[num-1],
460                              ctl, num);
461             if (err == PS_RETAINED)
462                 suppress_forward = retained = TRUE;
463             else if (err == PS_TRANSIENT)
464                 suppress_delete = suppress_forward = TRUE;
465             else if (err == PS_REFUSED)
466                 suppress_forward = TRUE;
467             else if (err == PS_TRUNCATED)
468                 suppress_readbody = TRUE;
469             else if (err)
470                 return(err);
471
472             /* 
473              * If we're using IMAP4 or something else that
474              * can fetch headers separately from bodies,
475              * it's time to request the body now.  This
476              * fetch may be skipped if we got an anti-spam
477              * or other PS_REFUSED error response during
478              * readheaders.
479              */
480             if (ctl->server.base_protocol->fetch_body && !suppress_readbody) 
481             {
482                 if (outlevel >= O_VERBOSE && !isafile(1))
483                 {
484                     fputc('\n', stdout);
485                     fflush(stdout);
486                 }
487
488                 if ((err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, num)))
489                     return(err);
490                 len = 0;
491                 if (!suppress_forward)
492                 {
493                     if ((err=(ctl->server.base_protocol->fetch_body)(mailserver_socket,ctl,num,&len)))
494                         return(err);
495                     /*
496                      * Work around a bug in Novell's
497                      * broken GroupWise IMAP server;
498                      * its body FETCH response is missing
499                      * the required length for the data
500                      * string.  This violates RFC2060.
501                      */
502                     if (len == -1)
503                         len = msgsizes[num-1] - msgblk.msglen;
504                     if (outlevel > O_SILENT && !wholesize)
505                         report_complete(stdout,
506                                         _(" (%d body octets) "), len);
507                 }
508             }
509
510             /* process the body now */
511             if (len > 0)
512             {
513                 if (suppress_readbody)
514                 {
515                     /* When readheaders returns PS_TRUNCATED,
516                      * the body (which has no content)
517                      * has already been read by readheaders,
518                      * so we say readbody returned PS_SUCCESS
519                      */
520                     err = PS_SUCCESS;
521                 }
522                 else
523                 {
524                     err = readbody(mailserver_socket,
525                                   ctl,
526                                   !suppress_forward,
527                                   len);
528                 }
529                 if (err == PS_TRANSIENT)
530                     suppress_delete = suppress_forward = TRUE;
531                 else if (err)
532                     return(err);
533
534                 /* tell server we got it OK and resynchronize */
535                 if (ctl->server.base_protocol->trail)
536                 {
537                     if (outlevel >= O_VERBOSE && !isafile(1))
538                     {
539                         fputc('\n', stdout);
540                         fflush(stdout);
541                     }
542
543                     err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, num);
544                     if (err != 0)
545                         return(err);
546                 }
547             }
548
549             /* count # messages forwarded on this pass */
550             if (!suppress_forward)
551                 (*dispatches)++;
552
553             /*
554              * Check to see if the numbers matched?
555              *
556              * Yes, some servers foo this up horribly.
557              * All IMAP servers seem to get it right, and
558              * so does Eudora QPOP at least in 2.xx
559              * versions.
560              *
561              * Microsoft Exchange gets it completely
562              * wrong, reporting compressed rather than
563              * actual sizes (so the actual length of
564              * message is longer than the reported size).
565              * Another fine example of Microsoft brain death!
566              *
567              * Some older POP servers, like the old UCB
568              * POP server and the pre-QPOP QUALCOMM
569              * versions, report a longer size in the LIST
570              * response than actually gets shipped up.
571              * It's unclear what is going on here, as the
572              * QUALCOMM server (at least) seems to be
573              * reporting the on-disk size correctly.
574              */
575             if (msgsizes && msgblk.msglen != msgsizes[num-1])
576             {
577                 if (outlevel >= O_DEBUG)
578                     report(stdout,
579                            _("message %d was not the expected length (%d actual != %d expected)\n"),
580                            num, msgblk.msglen, msgsizes[num-1]);
581             }
582
583             /* end-of-message processing starts here */
584             if (!close_sink(ctl, &msgblk, !suppress_forward))
585             {
586                 ctl->errcount++;
587                 suppress_delete = TRUE;
588             }
589             (*fetches)++;
590         }
591
592         /*
593          * At this point in flow of control, either
594          * we've bombed on a protocol error or had
595          * delivery refused by the SMTP server
596          * (unlikely -- I've never seen it) or we've
597          * seen `accepted for delivery' and the
598          * message is shipped.  It's safe to mark the
599          * message seen and delete it on the server
600          * now.
601          */
602
603         /* tell the UID code we've seen this */
604         if (ctl->newsaved)
605         {
606             struct idlist       *sdp;
607
608             for (sdp = ctl->newsaved; sdp; sdp = sdp->next)
609                 if ((sdp->val.status.num == num) && (msgsizes[num-1] > 0)) 
610                 {
611                     sdp->val.status.mark = UID_SEEN;
612                     save_str(&ctl->oldsaved, sdp->id,UID_SEEN);
613                 }
614         }
615
616         /* maybe we delete this message now? */
617         if (retained)
618         {
619             if (outlevel > O_SILENT) 
620                 report(stdout, _(" retained\n"));
621         }
622         else if (ctl->server.base_protocol->delete
623                  && !suppress_delete
624                  && ((msgsizes[num-1] > 0) ? !ctl->keep : ctl->flush))
625         {
626             (*deletions)++;
627             if (outlevel > O_SILENT) 
628                 report_complete(stdout, _(" flushed\n"));
629             err = (ctl->server.base_protocol->delete)(mailserver_socket, ctl, num);
630             if (err != 0)
631                 return(err);
632 #ifdef POP3_ENABLE
633             delete_str(&ctl->newsaved, num);
634 #endif /* POP3_ENABLE */
635         }
636         else if (outlevel > O_SILENT) 
637             report_complete(stdout, _(" not flushed\n"));
638
639         /* perhaps this as many as we're ready to handle */
640         if (maxfetch && maxfetch <= *fetches && *fetches < count)
641         {
642             report(stdout, _("fetchlimit %d reached; %d messages left on server\n"),
643                    maxfetch, count - *fetches);
644             return(PS_MAXFETCH);
645         }
646     }
647
648     return(PS_SUCCESS);
649 }
650
651 static int do_session(ctl, proto, maxfetch)
652 /* retrieve messages from server using given protocol method table */
653 struct query *ctl;              /* parsed options with merged-in defaults */
654 const struct method *proto;     /* protocol method table */
655 const int maxfetch;             /* maximum number of messages to fetch */
656 {
657     int js;
658 #ifdef HAVE_VOLATILE
659     volatile int err, mailserver_socket = -1;   /* pacifies -Wall */
660 #else
661     int err, mailserver_socket = -1;
662 #endif /* HAVE_VOLATILE */
663     const char *msg;
664     void (*pipesave)(int);
665     void (*alrmsave)(int);
666
667     ctl->server.base_protocol = proto;
668
669     pass = 0;
670     err = 0;
671     init_transact(proto);
672
673     /* set up the server-nonresponse timeout */
674     alrmsave = signal(SIGALRM, timeout_handler);
675     mytimeout = ctl->server.timeout;
676
677     /* set up the broken-pipe timeout */
678     pipesave = signal(SIGPIPE, sigpipe_handler);
679
680     if ((js = setjmp(restart)))
681     {
682 #ifdef HAVE_SIGPROCMASK
683         /*
684          * Don't rely on setjmp() to restore the blocked-signal mask.
685          * It does this under BSD but is required not to under POSIX.
686          *
687          * If your Unix doesn't have sigprocmask, better hope it has
688          * BSD-like behavior.  Otherwise you may see fetchmail get
689          * permanently wedged after a second timeout on a bad read,
690          * because alarm signals were blocked after the first.
691          */
692         sigset_t        allsigs;
693
694         sigfillset(&allsigs);
695         sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
696 #endif /* HAVE_SIGPROCMASK */
697
698         if (js == THROW_SIGPIPE)
699         {
700             signal(SIGPIPE, SIG_IGN);
701             report(stdout,
702                    _("SIGPIPE thrown from an MDA or a stream socket error\n"));
703             err = PS_SOCKET;
704             goto cleanUp;
705         }
706         else if (js == THROW_TIMEOUT)
707         {
708             if (phase == OPEN_WAIT)
709                 report(stdout,
710                        _("timeout after %d seconds waiting to connect to server %s.\n"),
711                        ctl->server.timeout, ctl->server.pollname);
712             else if (phase == SERVER_WAIT)
713                 report(stdout,
714                        _("timeout after %d seconds waiting for server %s.\n"),
715                        ctl->server.timeout, ctl->server.pollname);
716             else if (phase == FORWARDING_WAIT)
717                 report(stdout,
718                        _("timeout after %d seconds waiting for %s.\n"),
719                        ctl->server.timeout,
720                        ctl->mda ? "MDA" : "SMTP");
721             else if (phase == LISTENER_WAIT)
722                 report(stdout,
723                        _("timeout after %d seconds waiting for listener to respond.\n"), ctl->server.timeout);
724             else
725                 report(stdout, 
726                        _("timeout after %d seconds.\n"), ctl->server.timeout);
727
728             /*
729              * If we've exceeded our threshold for consecutive timeouts, 
730              * try to notify the user, then mark the connection wedged.
731              * Don't do this if the connection can idle, though; idle
732              * timeouts just mean the frequency of mail is low.
733              */
734             if (timeoutcount > MAX_TIMEOUTS 
735                 && !open_warning_by_mail(ctl, (struct msgblk *)NULL))
736             {
737                 stuff_warning(ctl,
738                               _("Subject: fetchmail sees repeated timeouts\r\n"));
739                 stuff_warning(ctl,
740                               _("Fetchmail saw more than %d timeouts while attempting to get mail from %s@%s.\r\n"), 
741                               MAX_TIMEOUTS,
742                               ctl->remotename,
743                               ctl->server.truename);
744                 stuff_warning(ctl, 
745     _("This could mean that your mailserver is stuck, or that your SMTP\r\n" \
746     "server is wedged, or that your mailbox file on the server has been\r\n" \
747     "corrupted by a server error.  You can run `fetchmail -v -v' to\r\n" \
748     "diagnose the problem.\r\n\r\n" \
749     "Fetchmail won't poll this mailbox again until you restart it.\r\n"));
750                 close_warning_by_mail(ctl, (struct msgblk *)NULL);
751                 ctl->wedged = TRUE;
752             }
753
754             err = PS_ERROR;
755         }
756
757         /* try to clean up all streams */
758         release_sink(ctl);
759         if (ctl->smtp_socket != -1)
760             SockClose(ctl->smtp_socket);
761         if (mailserver_socket != -1)
762             SockClose(mailserver_socket);
763     }
764     else
765     {
766         char buf[MSGBUFSIZE+1], *realhost;
767         int count, new, bytes, deletions = 0, *msgsizes = NULL;
768 #if INET6_ENABLE
769         int fetches, dispatches, oldphase;
770 #else /* INET6_ENABLE */
771         int port, fetches, dispatches, oldphase;
772 #endif /* INET6_ENABLE */
773         struct idlist *idp;
774
775         /* execute pre-initialization command, if any */
776         if (ctl->preconnect && (err = system(ctl->preconnect)))
777         {
778             report(stderr, 
779                    _("pre-connection command failed with status %d\n"), err);
780             err = PS_SYNTAX;
781             goto closeUp;
782         }
783
784         /* open a socket to the mail server */
785         oldphase = phase;
786         phase = OPEN_WAIT;
787         set_timeout(mytimeout);
788 #if !INET6_ENABLE
789 #ifdef SSL_ENABLE
790         port = ctl->server.port ? ctl->server.port : ( ctl->use_ssl ? ctl->server.base_protocol->sslport : ctl->server.base_protocol->port );
791 #else
792         port = ctl->server.port ? ctl->server.port : ctl->server.base_protocol->port;
793 #endif
794 #endif /* !INET6_ENABLE */
795         realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
796
797         /* allow time for the port to be set up if we have a plugin */
798         if (ctl->server.plugin)
799             (void)sleep(1);
800 #if INET6_ENABLE
801         if ((mailserver_socket = SockOpen(realhost, 
802                              ctl->server.service ? ctl->server.service : ( ctl->use_ssl ? ctl->server.base_protocol->sslservice : ctl->server.base_protocol->service ),
803                              ctl->server.netsec, ctl->server.plugin)) == -1)
804 #else /* INET6_ENABLE */
805         if ((mailserver_socket = SockOpen(realhost, port, NULL, ctl->server.plugin)) == -1)
806 #endif /* INET6_ENABLE */
807         {
808             char        errbuf[BUFSIZ];
809 #if !INET6_ENABLE
810             int err_no = errno;
811 #ifdef HAVE_RES_SEARCH
812             if (err_no != 0 && h_errno != 0)
813                 report(stderr, _("internal inconsistency\n"));
814 #endif
815             /*
816              * Avoid generating a bogus error every poll cycle when we're
817              * in daemon mode but the connection to the outside world
818              * is down.
819              */
820             if (!((err_no == EHOSTUNREACH || err_no == ENETUNREACH) 
821                   && run.poll_interval))
822             {
823                 report_build(stderr, _("%s connection to %s failed"), 
824                              ctl->server.base_protocol->name, ctl->server.pollname);
825 #ifdef HAVE_RES_SEARCH
826                 if (h_errno != 0)
827                 {
828                     if (h_errno == HOST_NOT_FOUND)
829                         strcpy(errbuf, _("host is unknown."));
830 #ifndef __BEOS__
831                     else if (h_errno == NO_ADDRESS)
832                         strcpy(errbuf, _("name is valid but has no IP address."));
833 #endif
834                     else if (h_errno == NO_RECOVERY)
835                         strcpy(errbuf, _("unrecoverable name server error."));
836                     else if (h_errno == TRY_AGAIN)
837                         strcpy(errbuf, _("temporary name server error."));
838                     else
839 #ifdef HAVE_SNPRINTF
840                         snprintf(errbuf, sizeof(errbuf),
841 #else
842                         sprintf(errbuf,
843 #endif /* HAVE_SNPRINTF */
844                           _("unknown DNS error %d."), h_errno);
845                 }
846                 else
847 #endif /* HAVE_RES_SEARCH */
848                     strcpy(errbuf, strerror(err_no));
849                 report_complete(stderr, ": %s\n", errbuf);
850
851 #ifdef __UNUSED
852                 /* 
853                  * Don't use this.  It was an attempt to address Debian bug
854                  * #47143 (Notify user by mail when pop server nonexistent).
855                  * Trouble is, that doesn't work; you trip over the case 
856                  * where your SLIP or PPP link is down...
857                  */
858                 /* warn the system administrator */
859                 if (open_warning_by_mail(ctl, (struct msgblk *)NULL) == 0)
860                 {
861                     stuff_warning(ctl,
862                          _("Subject: Fetchmail unreachable-server warning.\r\n"
863                            "\r\n"
864                            "Fetchmail could not reach the mail server %s:")
865                                   ctl->server.pollname);
866                     stuff_warning(ctl, errbuf, ctl->server.pollname);
867                     close_warning_by_mail(ctl, (struct msgblk *)NULL);
868                 }
869 #endif
870             }
871 #endif /* INET6_ENABLE */
872             err = PS_SOCKET;
873             set_timeout(0);
874             phase = oldphase;
875             goto closeUp;
876         }
877         set_timeout(0);
878         phase = oldphase;
879
880 #ifdef SSL_ENABLE
881         /* perform initial SSL handshake on open connection */
882         /* Note:  We pass the realhost name over for certificate
883                 verification.  We may want to make this configurable */
884         if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslkey,ctl->sslcert,ctl->sslproto,ctl->sslcertck,
885             ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1) 
886         {
887             report(stderr, _("SSL connection failed.\n"));
888             goto closeUp;
889         }
890 #endif
891
892 #ifdef KERBEROS_V4
893         if (ctl->server.authenticate == A_KERBEROS_V4)
894         {
895             set_timeout(mytimeout);
896             err = kerberos_auth(mailserver_socket, ctl->server.truename,
897                                ctl->server.principal);
898             set_timeout(0);
899             if (err != 0)
900                 goto cleanUp;
901         }
902 #endif /* KERBEROS_V4 */
903
904 #ifdef KERBEROS_V5
905         if (ctl->server.authenticate == A_KERBEROS_V5)
906         {
907             set_timeout(mytimeout);
908             err = kerberos5_auth(mailserver_socket, ctl->server.truename);
909             set_timeout(0);
910             if (err != 0)
911                 goto cleanUp;
912         }
913 #endif /* KERBEROS_V5 */
914
915         /* accept greeting message from mail server */
916         err = (ctl->server.base_protocol->parse_response)(mailserver_socket, buf);
917         if (err != 0)
918             goto cleanUp;
919
920         /* try to get authorized to fetch mail */
921         stage = STAGE_GETAUTH;
922         if (ctl->server.base_protocol->getauth)
923         {
924             err = (ctl->server.base_protocol->getauth)(mailserver_socket, ctl, buf);
925
926             if (err != 0)
927             {
928                 if (err == PS_LOCKBUSY)
929                     report(stderr, _("Lock-busy error on %s@%s\n"),
930                           ctl->remotename,
931                           ctl->server.truename);
932                 else if (err == PS_SERVBUSY)
933                     report(stderr, _("Server busy error on %s@%s\n"),
934                           ctl->remotename,
935                           ctl->server.truename);
936                 else if (err == PS_AUTHFAIL)
937                 {
938                     report(stderr, _("Authorization failure on %s@%s%s\n"), 
939                            ctl->remotename,
940                            ctl->server.truename,
941                            (ctl->wehaveauthed ? _(" (previously authorized)") : "")
942                         );
943
944                     /*
945                      * If we're running in background, try to mail the
946                      * calling user a heads-up about the authentication 
947                      * failure once it looks like this isn't a fluke 
948                      * due to the server being temporarily inaccessible.
949                      * When we get third succesive failure, we notify the user
950                      * but only if we haven't already managed to get
951                      * authorization.  After that, once we get authorization
952                      * we let the user know service is restored.
953                      */
954                     if (run.poll_interval
955                         && ctl->wehavesentauthnote
956                         && ((ctl->wehaveauthed && ++ctl->authfailcount == 10)
957                             || ++ctl->authfailcount == 3)
958                         && !open_warning_by_mail(ctl, (struct msgblk *)NULL))
959                     {
960                         ctl->wehavesentauthnote = 1;
961                         stuff_warning(ctl,
962                                       _("Subject: fetchmail authentication failed on %s@%s\r\n"),
963                             ctl->remotename, ctl->server.truename);
964                         stuff_warning(ctl,
965                                       _("Fetchmail could not get mail from %s@%s.\r\n"), 
966                                       ctl->remotename,
967                                       ctl->server.truename);
968                         if (ctl->wehaveauthed)
969                             stuff_warning(ctl, _("\
970 The attempt to get authorization failed.\r\n\
971 Since we have already succeeded in getting authorization for this\r\n\
972 connection, this is probably another failure mode (such as busy server)\r\n\
973 that fetchmail cannot distinguish because the server didn't send a useful\r\n\
974 error message.\r\n\
975 \r\n\
976 However, if you HAVE changed you account details since starting the\r\n\
977 fetchmail daemon, you need to stop the daemon, change your configuration\r\n\
978 of fetchmail, and then restart the daemon.\r\n\
979 \r\n\
980 The fetchmail daemon will continue running and attempt to connect\r\n\
981 at each cycle.  No future notifications will be sent until service\r\n\
982 is restored."));
983                         else
984                             stuff_warning(ctl, _("\
985 The attempt to get authorization failed.\r\n\
986 This probably means your password is invalid, but some servers have\r\n\
987 other failure modes that fetchmail cannot distinguish from this\r\n\
988 because they don't send useful error messages on login failure.\r\n\
989 \r\n\
990 The fetchmail daemon will continue running and attempt to connect\r\n\
991 at each cycle.  No future notifications will be sent until service\r\n\
992 is restored."));
993                         close_warning_by_mail(ctl, (struct msgblk *)NULL);
994                     }
995                 }
996                 else
997                     report(stderr, _("Unknown login or authentication error on %s@%s\n"),
998                            ctl->remotename,
999                            ctl->server.truename);
1000                     
1001                 goto cleanUp;
1002             }
1003             else
1004             {
1005                 /*
1006                  * This connection has given us authorization at least once.
1007                  *
1008                  * There are dodgy server (clubinternet.fr for example) that
1009                  * give spurious authorization failures on patently good
1010                  * account/password details, then 5 minutes later let you in!
1011                  *
1012                  * This is meant to build in some tolerance of such nasty bits
1013                  * of work.
1014                  */
1015                 ctl->wehaveauthed = 1;
1016                 /*if (ctl->authfailcount >= 3)*/
1017                 if (ctl->wehavesentauthnote)
1018                 {
1019                     ctl->wehavesentauthnote = 0;
1020                     report(stderr,
1021                            _("Authorization OK on %s@%s\n"),
1022                            ctl->remotename,
1023                            ctl->server.truename);
1024                     if (!open_warning_by_mail(ctl, (struct msgblk *)NULL))
1025                     {
1026                         stuff_warning(ctl,
1027                               _("Subject: fetchmail authentication OK on %s@%s\r\n"),
1028                                       ctl->remotename, ctl->server.truename);
1029                         stuff_warning(ctl,
1030                               _("Fetchmail was able to log into %s@%s.\r\n"), 
1031                                       ctl->remotename,
1032                                       ctl->server.truename);
1033                         stuff_warning(ctl, 
1034                                       _("Service has been restored.\r\n"));
1035                         close_warning_by_mail(ctl, (struct msgblk *)NULL);
1036                     
1037                     }
1038                 }
1039                 /*
1040                  * Reporting only after the first three
1041                  * consecutive failures, or ten consecutive
1042                  * failures after we have managed to get
1043                  * authorization.
1044                  */
1045                 ctl->authfailcount = 0;
1046             }
1047         }
1048
1049         ctl->errcount = fetches = 0;
1050
1051         /* now iterate over each folder selected */
1052         for (idp = ctl->mailboxes; idp; idp = idp->next)
1053         {
1054             pass = 0;
1055             do {
1056                 dispatches = 0;
1057                 ++pass;
1058
1059                 /* reset timeout, in case we did an IDLE */
1060                 mytimeout = ctl->server.timeout;
1061
1062                 if (outlevel >= O_DEBUG)
1063                 {
1064                     if (idp->id)
1065                         report(stdout, _("selecting or re-polling folder %s\n"), idp->id);
1066                     else
1067                         report(stdout, _("selecting or re-polling default folder\n"));
1068                 }
1069
1070                 /* compute # of messages and number of new messages waiting */
1071                 stage = STAGE_GETRANGE;
1072                 err = (ctl->server.base_protocol->getrange)(mailserver_socket, ctl, idp->id, &count, &new, &bytes);
1073                 if (err != 0)
1074                     goto cleanUp;
1075
1076                 /* show user how many messages we downloaded */
1077                 if (idp->id)
1078 #ifdef HAVE_SNPRINTF
1079                     (void) snprintf(buf, sizeof(buf),
1080 #else
1081                     (void) sprintf(buf,
1082 #endif /* HAVE_SNPRINTF */
1083                                    _("%s at %s (folder %s)"),
1084                                    ctl->remotename, ctl->server.truename, idp->id);
1085                 else
1086 #ifdef HAVE_SNPRINTF
1087                     (void) snprintf(buf, sizeof(buf),
1088 #else
1089                     (void) sprintf(buf,
1090 #endif /* HAVE_SNPRINTF */
1091                                _("%s at %s"),
1092                                    ctl->remotename, ctl->server.truename);
1093                 if (outlevel > O_SILENT)
1094                 {
1095                     if (count == -1)            /* only used for ETRN */
1096                         report(stdout, _("Polling %s\n"), ctl->server.truename);
1097                     else if (count != 0)
1098                     {
1099                         if (new != -1 && (count - new) > 0)
1100                             report_build(stdout, _("%d %s (%d seen) for %s"),
1101                                   count, count > 1 ? _("messages") :
1102                                                      _("message"),
1103                                   count-new, buf);
1104                         else
1105                             report_build(stdout, _("%d %s for %s"), 
1106                                   count, count > 1 ? _("messages") :
1107                                                      _("message"), buf);
1108                         if (bytes == -1)
1109                             report_complete(stdout, ".\n");
1110                         else
1111                             report_complete(stdout, _(" (%d octets).\n"), bytes);
1112                     }
1113                     else
1114                     {
1115                         /* these are pointless in normal daemon mode */
1116                         if (pass == 1 && (run.poll_interval == 0 || outlevel >= O_VERBOSE))
1117                             report(stdout, _("No mail for %s\n"), buf); 
1118                     }
1119                 }
1120
1121                 /* very important, this is where we leave the do loop */ 
1122                 if (count == 0)
1123                     break;
1124
1125                 if (check_only)
1126                 {
1127                     if (new == -1 || ctl->fetchall)
1128                         new = count;
1129                     fetches = new;      /* set error status correctly */
1130                     /*
1131                      * There used to be a `goto noerror' here, but this
1132                      * prevneted checking of multiple folders.  This
1133                      * comment is a reminder in case I introduced some
1134                      * subtle bug by removing it...
1135                      */
1136                 }
1137                 else if (count > 0)
1138                 {    
1139                     flag        force_retrieval;
1140                     int         i, num;
1141
1142                     /*
1143                      * What forces this code is that in POP2 and
1144                      * IMAP2bis you can't fetch a message without
1145                      * having it marked `seen'.  In POP3 and IMAP4, on the
1146                      * other hand, you can (peek_capable is set by 
1147                      * each driver module to convey this; it's not a
1148                      * method constant because of the difference between
1149                      * IMAP2bis and IMAP4, and because POP3 doesn't  peek
1150                      * if fetchall is on).
1151                      *
1152                      * The result of being unable to peek is that if there's
1153                      * any kind of transient error (DNS lookup failure, or
1154                      * sendmail refusing delivery due to process-table limits)
1155                      * the message will be marked "seen" on the server without
1156                      * having been delivered.  This is not a big problem if
1157                      * fetchmail is running in foreground, because the user
1158                      * will see a "skipped" message when it next runs and get
1159                      * clued in.
1160                      *
1161                      * But in daemon mode this leads to the message
1162                      * being silently ignored forever.  This is not
1163                      * acceptable.
1164                      *
1165                      * We compensate for this by checking the error
1166                      * count from the previous pass and forcing all
1167                      * messages to be considered new if it's nonzero.
1168                      */
1169                     force_retrieval = !peek_capable && (ctl->errcount > 0);
1170
1171                     /* OK, we're going to gather size info next */
1172                     xalloca(msgsizes, int *, sizeof(int) * count);
1173                     for (i = 0; i < count; i++)
1174                         msgsizes[i] = MSGLEN_UNKNOWN;
1175
1176                     /* 
1177                      * We need the size of each message before it's
1178                      * loaded in order to pass it to the ESMTP SIZE
1179                      * option.  If the protocol has a getsizes method,
1180                      * we presume this means it doesn't get reliable
1181                      * sizes from message fetch responses.
1182                      */
1183                     if (proto->getsizes)
1184                     {
1185                         stage = STAGE_GETSIZES;
1186                         err = (proto->getsizes)(mailserver_socket, count, msgsizes);
1187                         if (err != 0)
1188                             goto cleanUp;
1189
1190                         if (bytes == -1)
1191                         {
1192                             bytes = 0;
1193                             for (i = 0; i < count; i++)
1194                                 bytes += msgsizes[i];
1195                         }
1196                     }
1197
1198                     /* mark some messages not to be retrieved */
1199                     for (num = 1; num <= count; num++)
1200                     {
1201                         if (NUM_NONZERO(ctl->limit) && (msgsizes[num-1] > ctl->limit))
1202                             msgsizes[num-1] = MSGLEN_TOOLARGE;
1203                         else if (ctl->fetchall || force_retrieval)
1204                             continue;
1205                         else if (ctl->server.base_protocol->is_old && (ctl->server.base_protocol->is_old)(mailserver_socket,ctl,num))
1206                             msgsizes[num-1] = MSGLEN_OLD;
1207                     }
1208
1209                     /* read, forward, and delete messages */
1210                     stage = STAGE_FETCH;
1211
1212                     /* fetch in lockstep mode */
1213                     err = fetch_messages(mailserver_socket, ctl, 
1214                                         count, msgsizes, 
1215                                         maxfetch,
1216                                         &fetches, &dispatches, &deletions);
1217                     if (err)
1218                         goto cleanUp;
1219
1220                     if (!check_only && ctl->skipped
1221                         && run.poll_interval > 0 && !nodetach)
1222                     {
1223                         clean_skipped_list(&ctl->skipped);
1224                         send_size_warnings(ctl);
1225                     }
1226                 }
1227             } while
1228                   /*
1229                    * Only re-poll if we either had some actual forwards and 
1230                    * either allowed deletions and had no errors.
1231                    * Otherwise it is far too easy to get into infinite loops.
1232                    */
1233                   (dispatches && ctl->server.base_protocol->retry && !ctl->keep && !ctl->errcount);
1234         }
1235
1236     /* no_error: */
1237         /* ordinary termination with no errors -- officially log out */
1238         err = (ctl->server.base_protocol->logout_cmd)(mailserver_socket, ctl);
1239         /*
1240          * Hmmmm...arguably this would be incorrect if we had fetches but
1241          * no dispatches (due to oversized messages, etc.)
1242          */
1243         if (err == 0)
1244             err = (fetches > 0) ? PS_SUCCESS : PS_NOMAIL;
1245         SockClose(mailserver_socket);
1246         goto closeUp;
1247
1248     cleanUp:
1249         /* we only get here on error */
1250         if (err != 0 && err != PS_SOCKET)
1251         {
1252             stage = STAGE_LOGOUT;
1253             (ctl->server.base_protocol->logout_cmd)(mailserver_socket, ctl);
1254         }
1255         SockClose(mailserver_socket);
1256     }
1257
1258     msg = (const char *)NULL;   /* sacrifice to -Wall */
1259     switch (err)
1260     {
1261     case PS_SOCKET:
1262         msg = _("socket");
1263         break;
1264     case PS_SYNTAX:
1265         msg = _("missing or bad RFC822 header");
1266         break;
1267     case PS_IOERR:
1268         msg = _("MDA");
1269         break;
1270     case PS_ERROR:
1271         msg = _("client/server synchronization");
1272         break;
1273     case PS_PROTOCOL:
1274         msg = _("client/server protocol");
1275         break;
1276     case PS_LOCKBUSY:
1277         msg = _("lock busy on server");
1278         break;
1279     case PS_SMTP:
1280         msg = _("SMTP transaction");
1281         break;
1282     case PS_DNS:
1283         msg = _("DNS lookup");
1284         break;
1285     case PS_UNDEFINED:
1286         report(stderr, _("undefined error\n"));
1287         break;
1288     }
1289     /* no report on PS_MAXFETCH or PS_UNDEFINED or PS_AUTHFAIL */
1290     if (err==PS_SOCKET || err==PS_SYNTAX
1291                 || err==PS_IOERR || err==PS_ERROR || err==PS_PROTOCOL 
1292                 || err==PS_LOCKBUSY || err==PS_SMTP || err==PS_DNS)
1293     {
1294         char    *stem;
1295
1296         if (phase == FORWARDING_WAIT || phase == LISTENER_WAIT)
1297             stem = _("%s error while delivering to SMTP host %s\n");
1298         else
1299             stem = _("%s error while fetching from %s\n");
1300         report(stderr, stem, msg, ctl->server.pollname);
1301     }
1302
1303 closeUp:
1304     /* execute wrapup command, if any */
1305     if (ctl->postconnect && (err = system(ctl->postconnect)))
1306     {
1307         report(stderr, _("post-connection command failed with status %d\n"), err);
1308         if (err == PS_SUCCESS)
1309             err = PS_SYNTAX;
1310     }
1311
1312     signal(SIGALRM, alrmsave);
1313     signal(SIGPIPE, pipesave);
1314     return(err);
1315 }
1316
1317 int do_protocol(ctl, proto)
1318 /* retrieve messages from server using given protocol method table */
1319 struct query *ctl;              /* parsed options with merged-in defaults */
1320 const struct method *proto;     /* protocol method table */
1321 {
1322     int err;
1323
1324 #ifndef KERBEROS_V4
1325     if (ctl->server.authenticate == A_KERBEROS_V4)
1326     {
1327         report(stderr, _("Kerberos V4 support not linked.\n"));
1328         return(PS_ERROR);
1329     }
1330 #endif /* KERBEROS_V4 */
1331
1332 #ifndef KERBEROS_V5
1333     if (ctl->server.authenticate == A_KERBEROS_V5)
1334     {
1335         report(stderr, _("Kerberos V5 support not linked.\n"));
1336         return(PS_ERROR);
1337     }
1338 #endif /* KERBEROS_V5 */
1339
1340     /* lacking methods, there are some options that may fail */
1341     if (!proto->is_old)
1342     {
1343         /* check for unsupported options */
1344         if (ctl->flush) {
1345             report(stderr,
1346                     _("Option --flush is not supported with %s\n"),
1347                     proto->name);
1348             return(PS_SYNTAX);
1349         }
1350         else if (ctl->fetchall) {
1351             report(stderr,
1352                     _("Option --all is not supported with %s\n"),
1353                     proto->name);
1354             return(PS_SYNTAX);
1355         }
1356     }
1357     if (!proto->getsizes && NUM_SPECIFIED(ctl->limit))
1358     {
1359         report(stderr,
1360                 _("Option --limit is not supported with %s\n"),
1361                 proto->name);
1362         return(PS_SYNTAX);
1363     }
1364
1365     /*
1366      * If no expunge limit or we do expunges within the driver,
1367      * then just do one session, passing in any fetchlimit.
1368      */
1369     if (proto->retry || !NUM_SPECIFIED(ctl->expunge))
1370         return(do_session(ctl, proto, NUM_VALUE_OUT(ctl->fetchlimit)));
1371     /*
1372      * There's an expunge limit, and it isn't handled in the driver itself.
1373      * OK; do multiple sessions, each fetching a limited # of messages.
1374      * Stop if the total count of retrieved messages exceeds ctl->fetchlimit
1375      * (if it was nonzero).
1376      */
1377     else
1378     {
1379         int totalcount = 0; 
1380         int lockouts   = 0;
1381         int expunge    = NUM_VALUE_OUT(ctl->expunge);
1382         int fetchlimit = NUM_VALUE_OUT(ctl->fetchlimit);
1383
1384         do {
1385             if (fetchlimit > 0 && (expunge == 0 || expunge > fetchlimit - totalcount))
1386                 expunge = fetchlimit - totalcount;
1387             err = do_session(ctl, proto, expunge);
1388             totalcount += expunge;
1389             if (NUM_SPECIFIED(ctl->fetchlimit) && totalcount >= fetchlimit)
1390                 break;
1391             if (err != PS_LOCKBUSY)
1392                 lockouts = 0;
1393             else if (lockouts >= MAX_LOCKOUTS)
1394                 break;
1395             else /* err == PS_LOCKBUSY */
1396             {
1397                 /*
1398                  * Allow time for the server lock to release.  if we
1399                  * don't do this, we'll often hit a locked-mailbox
1400                  * condition and fail.
1401                  */
1402                 lockouts++;
1403                 sleep(3);
1404             }
1405         } while
1406             (err == PS_MAXFETCH || err == PS_LOCKBUSY);
1407
1408         return(err);
1409     }
1410 }
1411
1412
1413 /* driver.c ends here */