2 * driver.c -- generic driver for mail fetch method protocols
4 * Copyright 1996 by Eric S. Raymond
6 * For license terms, see the file COPYING in this directory.
14 #if defined(STDC_HEADERS)
17 #if defined(HAVE_UNISTD_H)
20 #if defined(HAVE_STDARG_H)
28 #ifdef HAVE_GETHOSTBYNAME
31 #endif /* HAVE_GETHOSTBYNAME */
36 #include <netinet/in.h> /* must be included before "socket.h".*/
38 #endif /* KERBEROS_V4 */
40 #include "fetchmail.h"
43 #define SMTP_PORT 25 /* standard SMTP service port */
45 int batchlimit; /* how often to tear down the delivery connection */
46 int batchcount; /* count of messages sent in current batch */
48 static const struct method *protocol;
49 static jmp_buf restart;
53 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
55 static char *shroud; /* string to shroud in debug output, if non-NULL */
56 static int mytimeout; /* value of nonreponse timeout */
58 static int strcrlf(dst, src, count)
59 /* replace LFs with CR-LF; return length of string with replacements */
60 char *dst; /* new string with CR-LFs */
61 char *src; /* original string with LFs */
62 int count; /* length of src */
79 static void vtalarm(int timeleft)
80 /* reset the nonresponse-timeout */
82 struct itimerval ntimeout;
84 ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
85 ntimeout.it_value.tv_sec = timeleft;
86 ntimeout.it_value.tv_usec = 0;
87 setitimer(ITIMER_VIRTUAL, &ntimeout, (struct itimerval *)NULL);
90 static void vtalarm_handler (int signal)
91 /* handle server-timeout SIGVTALARM signal */
96 #ifdef HAVE_RES_SEARCH
99 static int is_host_alias(const char *name, struct query *ctl)
100 /* determine whether name is a DNS alias of the hostname */
103 struct mxentry *mxp, *mxrecords;
106 * The first two checks are optimizations that will catch a good
107 * many cases. First, check against the hostname the user specified.
108 * Odds are good this will either be the mailserver's FQDN or a
109 * suffix of it with the mailserver's domain's default host name
110 * omitted. Next, check against the mailserver's FQDN, in case
111 * it's not the same as the declared hostname.
113 * Either of these on a mail address is definitive. Only if the
114 * name doesn't match either is it time to call the bind library.
115 * If this happens odds are good we're looking at an MX name.
117 if (strcmp(name, ctl->servername) == 0)
119 else if (strcmp(name, ctl->canonical_name) == 0)
123 * Is it in the `also known as' cache accumulated by previous DNS checks?
124 * This cache may someday be primed by an aka option.
126 else if (uid_in_list(&ctl->lead_server->aka, name))
130 * We know DNS service was up at the beginning of this poll cycle.
131 * If it's down, our nameserver has crashed. We don't want to try
132 * delivering the current message or anything else from this
133 * mailbox until it's back up.
135 else if ((he = gethostbyname(name)) != (struct hostent *)NULL)
137 if (strcmp(ctl->canonical_name, he->h_name) == 0)
145 case HOST_NOT_FOUND: /* specified host is unknown */
146 case NO_ADDRESS: /* valid, but does not have an IP address */
149 case NO_RECOVERY: /* non-recoverable name server error */
150 case TRY_AGAIN: /* temporary error on authoritative server */
152 if (outlevel != O_SILENT)
153 putchar('\n'); /* terminate the progress message */
155 "fetchmail: nameserver failure while looking for `%s' during poll of %s.\n",
156 name, ctl->servername);
158 longjmp(restart, 2); /* try again next poll cycle */
163 * We're only here if DNS was OK but the gethostbyname() failed
164 * with a HOST_NOT_FOUND or NO_ADDRESS error.
165 * Search for a name match on MX records pointing to the server.
168 if ((mxrecords = getmxrecords(name)) == (struct mxentry *)NULL)
171 case HOST_NOT_FOUND: /* specified host is unknown */
174 case NO_ADDRESS: /* valid, but does not have an IP address */
175 for (mxp = mxrecords; mxp->name; mxp++)
176 if (strcmp(name, mxp->name) == 0)
180 case NO_RECOVERY: /* non-recoverable name server error */
181 case TRY_AGAIN: /* temporary error on authoritative server */
184 "fetchmail: nameserver failure while looking for `%s' during poll of %s.\n",
185 name, ctl->servername);
187 longjmp(restart, 2); /* try again next poll cycle */
194 /* add this name to relevant server's `also known as' list */
195 save_uid(&ctl->lead_server->aka, -1, name);
199 void find_server_names(hdr, ctl, xmit_names)
200 /* parse names out of a RFC822 header into an ID list */
201 const char *hdr; /* RFC822 header in question */
202 struct query *ctl; /* list of permissible aliases */
203 struct idlist **xmit_names; /* list of recipient names parsed out */
205 if (hdr == (char *)NULL)
211 if ((cp = nxtaddr(hdr)) != (char *)NULL)
215 if ((atsign = strchr(cp, '@')))
218 * Address has an @. Check to see if the right-hand part
219 * is an alias or MX equivalent of the mailserver. If it's
220 * not, skip this name. If it is, we'll keep going and try
221 * to find a mapping to a client name.
223 if (!is_host_alias(atsign+1, ctl))
228 lname = idpair_find(&ctl->localnames, cp);
229 if (lname != (char *)NULL)
231 if (outlevel == O_VERBOSE)
233 "fetchmail: mapped %s to local %s\n",
235 save_uid(xmit_names, -1, lname);
238 ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
241 #endif /* HAVE_RES_SEARCH */
243 static FILE *smtp_open(struct query *ctl)
244 /* try to open a socket to the appropriate SMTP server for this query */
246 ctl = ctl->lead_smtp; /* go to the SMTP leader for this query */
248 /* maybe it's time to close the socket in order to force delivery */
249 if (batchlimit && ctl->smtp_sockfp && batchcount++ == batchlimit)
251 fclose(ctl->smtp_sockfp);
252 ctl->smtp_sockfp = (FILE *)NULL;
256 /* if no socket to this host is already set up, try to open one */
257 if (ctl->smtp_sockfp == (FILE *)NULL)
259 if ((ctl->smtp_sockfp = Socket(ctl->smtphost, SMTP_PORT)) == (FILE *)NULL)
260 return((FILE *)NULL);
261 else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
262 || SMTP_helo(ctl->smtp_sockfp, ctl->servername) != SM_OK)
264 fclose(ctl->smtp_sockfp);
265 ctl->smtp_sockfp = (FILE *)NULL;
269 return(ctl->smtp_sockfp);
272 static int gen_readmsg (sockfp, len, delimited, ctl)
273 /* read message content and ship to SMTP or MDA */
274 FILE *sockfp; /* to which the server is connected */
275 long len; /* length of message */
276 int delimited; /* does the protocol use a message delimiter? */
277 struct query *ctl; /* query control record */
279 char buf [MSGBUFSIZE+1];
280 char *bufp, *headers, *fromhdr, *tohdr, *cchdr, *bcchdr, *received_for;
281 int n, oldlen, mboxfd;
282 int inheaders,lines,sizeticker;
284 #ifdef HAVE_GETHOSTBYNAME
285 char rbuf[HOSTLEN + USERNAMELEN + 4];
286 #endif /* HAVE_GETHOSTBYNAME */
288 /* read the message content from the server */
290 headers = fromhdr = tohdr = cchdr = bcchdr = received_for = NULL;
294 while (delimited || len > 0)
296 if ((n = SockGets(buf,sizeof(buf),sockfp)) < 0)
298 vtalarm(ctl->timeout);
300 /* write the message size dots */
304 while (sizeticker >= SIZETICKER)
306 if (outlevel > O_SILENT)
308 sizeticker -= SIZETICKER;
313 if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
315 if (delimited && *bufp == '.') {
318 break; /* end of message */
325 reply_hack(bufp, ctl->servername);
329 oldlen = strlen(bufp);
330 headers = xmalloc(oldlen + 1);
331 (void) strcpy(headers, bufp);
339 * We deal with RFC822 continuation lines here.
340 * Replace previous '\n' with '\r' so nxtaddr
341 * and reply_hack will be able to see past it.
342 * (We know this is safe because SocketGets stripped
343 * out all carriage returns in the read loop above
344 * and we haven't reintroduced any since then.)
345 * We'll undo this before writing the header.
347 if (isspace(bufp[0]))
348 headers[oldlen-1] = '\r';
350 newlen = oldlen + strlen(bufp);
351 headers = realloc(headers, newlen + 1);
354 strcpy(headers + oldlen, bufp);
355 bufp = headers + oldlen;
359 if (!strncasecmp("From:", bufp, 5))
361 else if (!fromhdr && !strncasecmp("Resent-From:", bufp, 12))
363 else if (!fromhdr && !strncasecmp("Apparently-From:", bufp, 16))
365 else if (!strncasecmp("To:", bufp, 3))
367 else if (!strncasecmp("Apparently-To:", bufp, 14))
369 else if (!strncasecmp("Cc:", bufp, 3))
371 else if (!strncasecmp("Bcc:", bufp, 4))
373 #ifdef HAVE_GETHOSTBYNAME
374 else if (MULTIDROP(ctl) && !strncasecmp("Received:", bufp, 9))
379 * Try to extract the real envelope addressee. We look here
380 * specifically for the mailserver's Received line.
381 * Note: this will only work for sendmail, or an MTA that
382 * shares sendmail's convention of embedding the envelope
383 * address in the Received line.
386 strcat(rbuf, ctl->canonical_name);
387 if ((ok = strstr(bufp, rbuf)))
388 ok = strstr(ok, "for <");
396 for (sp = ok + 5; *sp && *sp != '>' && *sp != '@'; sp++)
399 if (*sp != ';' && *sp != '@')
405 received_for = alloca(strlen(rbuf)+1);
406 strcpy(received_for, rbuf);
407 if (outlevel == O_VERBOSE)
409 "fetchmail: found Received address `%s'\n",
413 #endif /* HAVE_GETHOSTBYNAME */
417 else if (headers) /* OK, we're at end of headers now */
420 struct idlist *idp, *xmit_names;
421 int good_addresses, bad_addresses;
422 #ifdef HAVE_RES_SEARCH
423 int no_local_matches = FALSE;
424 #endif /* HAVE_RES_SEARCH */
426 /* cons up a list of local recipients */
427 xmit_names = (struct idlist *)NULL;
428 bad_addresses = good_addresses = 0;
429 #ifdef HAVE_RES_SEARCH
430 /* is this a multidrop box? */
435 * We have the actual envelope addressee.
436 * It has to be a mailserver address, or we
437 * wouldn't have got here.
439 find_server_names(received_for, ctl, &xmit_names);
443 * We haven't extracted the envelope address.
444 * So check all the header addresses.
446 find_server_names(tohdr, ctl, &xmit_names);
447 find_server_names(cchdr, ctl, &xmit_names);
448 find_server_names(bcchdr, ctl, &xmit_names);
452 no_local_matches = TRUE;
453 save_uid(&xmit_names, -1, user);
454 if (outlevel == O_VERBOSE)
456 "fetchmail: no local matches, forwarding to %s\n",
460 else /* it's a single-drop box, use first localname */
461 #endif /* HAVE_RES_SEARCH */
462 save_uid(&xmit_names, -1, ctl->localnames->id);
464 /* time to address the message */
465 if (ctl->mda[0]) /* we have a declared MDA */
471 * We go through this in order to be able to handle very
472 * long lists of users and (re)implement %s.
474 for (idp = xmit_names; idp; idp = idp->next)
476 sp = sargv = (char **)alloca(sizeof(char **) * ctl->mda_argcount+nlocals+2);
477 for (i = 0; i < ctl->mda_argcount; i++)
478 if (strcmp("%s", ctl->mda_argv[i]))
479 *sp++ = ctl->mda_argv[i];
481 for (idp = xmit_names; idp; idp = idp->next)
487 * Arrange to run with user's permissions if we're root.
488 * This will initialize the ownership of any files the
489 * MDA creates properly. (The seteuid call is available
490 * under all BSDs and Linux)
493 #endif /* HAVE_SETEUID */
495 mboxfd = openmailpipe(sargv);
498 /* this will fail quietly if we didn't start as root */
500 #endif /* HAVE_SETEUID */
504 fprintf(stderr, "fetchmail: MDA open failed\n");
512 /* build a connection to the SMTP listener */
513 if (ctl->mda[0] == '\0' && ((sinkfp = smtp_open(ctl)) == NULL))
515 free_uid_list(&xmit_names);
516 fprintf(stderr, "fetchmail: SMTP connect failed\n");
521 * Try to get the SMTP listener to take the header
522 * From address as MAIL FROM (this makes the logging
523 * nicer). If it won't, fall back on the calling-user
524 * ID. This won't affect replies, which use the header
525 * From address anyway.
527 if (!fromhdr || !(ap = nxtaddr(fromhdr))
528 || SMTP_from(sinkfp, ap) != SM_OK)
529 if (SMTP_from(sinkfp, user) != SM_OK)
530 return(PS_SMTP); /* should never happen */
532 /* now list the recipient addressees */
533 for (idp = xmit_names; idp; idp = idp->next)
534 if (SMTP_rcpt(sinkfp, idp->id) == SM_OK)
541 "fetchmail: SMTP listener doesn't like recipient address `%s'\n", idp->id);
543 if (!good_addresses && SMTP_rcpt(sinkfp, user) != SM_OK)
546 "fetchmail: can't even send to calling user!\n");
550 /* tell it we're ready to send data */
552 if (outlevel == O_VERBOSE)
553 fputs("SMTP> ", stderr);
556 /* change continuation markers back to regular newlines */
557 for (cp = headers; cp < headers + oldlen; cp++)
561 /* replace all LFs with CR-LF before sending to the SMTP server */
564 char *newheaders = xmalloc(1 + oldlen * 2);
566 oldlen = strcrlf(newheaders, headers, oldlen);
568 headers = newheaders;
571 /* write all the headers */
573 n = write(mboxfd,headers,oldlen);
575 n = SockWrite(headers, oldlen, sinkfp);
581 perror("fetchmail: writing RFC822 headers");
584 else if (outlevel == O_VERBOSE)
589 /* write error notifications */
590 #ifdef HAVE_RES_SEARCH
591 if (no_local_matches || bad_addresses)
594 #endif /* HAVE_RES_SEARCH */
597 char errhd[USERNAMELEN + POPBUFSIZE], *errmsg;
600 (void) strcpy(errhd, "X-Fetchmail-Warning: ");
601 #ifdef HAVE_RES_SEARCH
602 if (no_local_matches)
604 strcat(errhd, "no recipient addresses matched declared local names");
608 #endif /* HAVE_RES_SEARCH */
612 strcat(errhd, "SMTP listener rejected local recipient addresses: ");
613 errlen = strlen(errhd);
614 for (idp = xmit_names; idp; idp = idp->next)
616 errlen += strlen(idp->id) + 2;
618 errmsg = alloca(errlen+3);
619 (void) strcpy(errmsg, errhd);
620 for (idp = xmit_names; idp; idp = idp->next)
623 strcat(errmsg, idp->id);
625 strcat(errmsg, ", ");
629 strcat(errmsg, "\n");
632 write(mboxfd, errmsg, strlen(errmsg));
634 SockWrite(errmsg, strlen(errmsg), sinkfp);
637 free_uid_list(&xmit_names);
640 /* SMTP byte-stuffing */
641 if (*bufp == '.' && ctl->mda[0] == 0)
642 SockWrite(".", 1, sinkfp);
644 /* replace all LFs with CR-LF in the line */
647 char *newbufp = xmalloc(1 + strlen(bufp) * 2);
649 strcrlf(newbufp, bufp, strlen(bufp));
653 /* ship out the text line */
655 n = write(mboxfd,bufp,strlen(bufp));
657 n = SockWrite(bufp, strlen(bufp), sinkfp);
663 perror("fetchmail: writing message text");
666 else if (outlevel == O_VERBOSE)
675 /* close the delivery pipe, we'll reopen before next message */
676 if (closemailpipe(mboxfd))
681 /* write message terminator */
682 if (SMTP_eom(sinkfp) != SM_OK)
684 fputs("fetchmail: SMTP listener refused delivery\n", stderr);
694 kerberos_auth (socket, canonical)
695 /* authenticate to the server host using Kerberos V4 */
696 int socket; /* socket to server host */
697 const char *canonical; /* server name */
703 Key_schedule schedule;
706 ticket = ((KTEXT) (malloc (sizeof (KTEXT_ST))));
707 rem = (krb_sendauth (0L, socket, ticket, "pop",
709 ((char *) (krb_realmofhost (canonical))),
714 ((struct sockaddr_in *) 0),
715 ((struct sockaddr_in *) 0),
720 fprintf (stderr, "fetchmail: kerberos error %s\n", (krb_get_err_text (rem)));
725 #endif /* KERBEROS_V4 */
727 int do_protocol(ctl, proto)
728 /* retrieve messages from server using given protocol method table */
729 struct query *ctl; /* parsed options with merged-in defaults */
730 const struct method *proto; /* protocol method table */
736 if (ctl->authenticate == A_KERBEROS)
738 fputs("fetchmail: Kerberos support not linked.\n", stderr);
741 #endif /* KERBEROS_V4 */
743 /* lacking methods, there are some options that may fail */
746 /* check for unsupported options */
749 "Option --flush is not supported with %s\n",
753 else if (ctl->fetchall) {
755 "Option --all is not supported with %s\n",
760 if (!proto->getsizes && ctl->limit)
763 "Option --limit is not supported with %s\n",
770 tag[0] = '\0'; /* nuke any tag hanging out from previous query */
773 /* set up the server-nonresponse timeout */
774 sigsave = signal(SIGVTALRM, vtalarm_handler);
775 vtalarm(mytimeout = ctl->timeout);
777 if ((js = setjmp(restart)) == 1)
780 "fetchmail: timeout after %d seconds waiting for %s.\n",
781 ctl->timeout, ctl->servername);
786 /* error message printed at point of longjmp */
791 char buf [POPBUFSIZE+1];
792 int *msgsizes, len, num, count, new, deletions = 0;
795 /* open a socket to the mail server */
796 if ((sockfp = Socket(ctl->servername,
797 ctl->port ? ctl->port : protocol->port)) == NULL)
799 perror("fetchmail, connecting to host");
805 if (ctl->authenticate == A_KERBEROS)
807 ok = (kerberos_auth (fileno(sockfp), ctl->canonical_name));
808 vtalarm(ctl->timeout);
812 #endif /* KERBEROS_V4 */
814 /* accept greeting message from mail server */
815 ok = (protocol->parse_response)(sockfp, buf);
816 vtalarm(ctl->timeout);
820 /* try to get authorized to fetch mail */
821 shroud = ctl->password;
822 ok = (protocol->getauth)(sockfp, ctl, buf);
823 vtalarm(ctl->timeout);
824 shroud = (char *)NULL;
830 /* compute number of messages and number of new messages waiting */
831 if ((protocol->getrange)(sockfp, ctl, &count, &new) != 0)
833 vtalarm(ctl->timeout);
835 /* show user how many messages we downloaded */
836 if (outlevel > O_SILENT)
838 fprintf(stderr, "No mail from %s@%s\n",
843 fprintf(stderr, "%d message%s", count, count > 1 ? "s" : "");
844 if (new != -1 && (count - new) > 0)
845 fprintf(stderr, " (%d seen)", count-new);
852 /* we may need to get sizes in order to check message limits */
853 msgsizes = (int *)NULL;
854 if (!ctl->fetchall && proto->getsizes && ctl->limit)
856 msgsizes = (int *)alloca(sizeof(int) * count);
858 if ((ok = (proto->getsizes)(sockfp, count, msgsizes)) != 0)
865 if (new == -1 || ctl->fetchall)
867 ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
873 * What forces this code is that in POP3 you can't fetch a
874 * message without having it marked `seen'.
876 * The result is that if there's any kind of transient error
877 * (DNS lookup failure, or sendmail refusing delivery due to
878 * process-table limits) the message will be marked "seen" on
879 * the server without having been delivered. This is not a
880 * big problem if fetchmail is running in foreground, because
881 * the user will see a "skipped" message when it next runs and
884 * But in daemon mode this leads to the message being silently
885 * ignored forever. This is not acceptable.
887 * We compensate for this by checking the error count from the
888 * previous pass and forcing all messages to be considered new
891 int force_retrieval = (ctl->errcount > 0);
895 /* read, forward, and delete messages */
896 for (num = 1; num <= count; num++)
898 int toolarge = msgsizes && (msgsizes[num-1] > ctl->limit);
899 int fetch_it = ctl->fetchall ||
900 (!toolarge && (force_retrieval || !(protocol->is_old && (protocol->is_old)(sockfp,ctl,num))));
902 /* we may want to reject this message if it's old */
905 if (outlevel > O_SILENT)
907 fprintf(stderr, "skipping message %d", num);
909 fprintf(stderr, " (oversized, %d bytes)", msgsizes[num-1]);
914 /* request a message */
915 (protocol->fetch)(sockfp, num, &len);
916 vtalarm(ctl->timeout);
918 if (outlevel > O_SILENT)
920 fprintf(stderr, "reading message %d", num);
922 fprintf(stderr, " (%d bytes)", len);
923 if (outlevel == O_VERBOSE)
929 /* read the message and ship it to the output sink */
930 ok = gen_readmsg(sockfp,
934 vtalarm(ctl->timeout);
938 /* tell the server we got it OK and resynchronize */
940 (protocol->trail)(sockfp, ctl, num);
944 * At this point in flow of control, either we've bombed
945 * on a protocol error or had delivery refused by the SMTP
946 * server (unlikely -- I've never seen it) or we've seen
947 * `accepted for delivery' and the message is shipped.
948 * It's safe to mark the message seen and delete it on the
952 /* maybe we delete this message now? */
954 && (fetch_it ? !ctl->keep : ctl->flush))
957 if (outlevel > O_SILENT)
958 fprintf(stderr, " flushed\n");
959 ok = (protocol->delete)(sockfp, ctl, num);
960 vtalarm(ctl->timeout);
963 delete_uid(&ctl->newsaved, num);
965 else if (outlevel > O_SILENT)
966 fprintf(stderr, " not flushed\n");
969 /* remove all messages flagged for deletion */
970 if (protocol->expunge_cmd && deletions > 0)
972 ok = gen_transact(sockfp, protocol->expunge_cmd);
977 ok = gen_transact(sockfp, protocol->exit_cmd);
984 ok = gen_transact(sockfp, protocol->exit_cmd);
992 if (ok != 0 && ok != PS_SOCKET)
994 gen_transact(sockfp, protocol->exit_cmd);
1002 fputs("fetchmail: socket", stderr);
1005 fputs("fetchmail: authorization", stderr);
1008 fputs("fetchmail: missing or bad RFC822 header", stderr);
1011 fputs("fetchmail: MDA", stderr);
1014 fputs("fetchmail: client/server synchronization", stderr);
1017 fputs("fetchmail: client/server protocol", stderr);
1020 fputs("fetchmail: SMTP transaction", stderr);
1023 fputs("fetchmail: undefined", stderr);
1026 if (ok==PS_SOCKET || ok==PS_AUTHFAIL || ok==PS_SYNTAX || ok==PS_IOERR
1027 || ok==PS_ERROR || ok==PS_PROTOCOL || ok==PS_SMTP)
1028 fprintf(stderr, " error while talking to %s\n", ctl->servername);
1031 signal(SIGVTALRM, sigsave);
1035 #if defined(HAVE_STDARG_H)
1036 void gen_send(FILE *sockfp, char *fmt, ... )
1037 /* assemble command in printf(3) style and send to the server */
1040 void gen_send(sockfp, fmt, va_alist)
1041 /* assemble command in printf(3) style and send to the server */
1042 FILE *sockfp; /* socket to which server is connected */
1043 const char *fmt; /* printf-style format */
1047 char buf [POPBUFSIZE+1];
1050 if (protocol->tagged)
1051 (void) sprintf(buf, "%s ", GENSYM);
1055 #if defined(HAVE_STDARG_H)
1060 vsprintf(buf + strlen(buf), fmt, ap);
1063 strcat(buf, "\r\n");
1064 SockWrite(buf, strlen(buf), sockfp);
1066 if (outlevel == O_VERBOSE)
1070 if (shroud && (cp = strstr(buf, shroud)))
1071 memset(cp, '*', strlen(shroud));
1072 fprintf(stderr,"> %s", buf);
1076 #if defined(HAVE_STDARG_H)
1077 int gen_transact(FILE *sockfp, char *fmt, ... )
1078 /* assemble command in printf(3) style, send to server, accept a response */
1081 int gen_transact(sockfp, fmt, va_alist)
1082 /* assemble command in printf(3) style, send to server, accept a response */
1083 FILE *sockfp; /* socket to which server is connected */
1084 const char *fmt; /* printf-style format */
1089 char buf [POPBUFSIZE+1];
1092 if (protocol->tagged)
1093 (void) sprintf(buf, "%s ", GENSYM);
1097 #if defined(HAVE_STDARG_H)
1102 vsprintf(buf + strlen(buf), fmt, ap);
1105 strcat(buf, "\r\n");
1106 SockWrite(buf, strlen(buf), sockfp);
1107 if (outlevel == O_VERBOSE)
1111 if (shroud && (cp = strstr(buf, shroud)))
1112 memset(cp, '*', strlen(shroud));
1113 fprintf(stderr,"> %s", buf);
1116 /* we presume this does its own response echoing */
1117 ok = (protocol->parse_response)(sockfp, buf);
1123 /* driver.c ends here */