| 1 |
/** |
| 2 |
* \file transact.c -- transaction primitives for the fetchmail driver loop |
| 3 |
* |
| 4 |
* Copyright 2001 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 <string.h> |
| 11 |
#include <ctype.h> |
| 12 |
#ifdef HAVE_MEMORY_H |
| 13 |
#include <memory.h> |
| 14 |
#endif /* HAVE_MEMORY_H */ |
| 15 |
#if defined(STDC_HEADERS) |
| 16 |
#include <stdlib.h> |
| 17 |
#endif |
| 18 |
#if defined(HAVE_UNISTD_H) |
| 19 |
#include <unistd.h> |
| 20 |
#endif |
| 21 |
#if defined(HAVE_STDARG_H) |
| 22 |
#include <stdarg.h> |
| 23 |
#else |
| 24 |
#include <varargs.h> |
| 25 |
#endif |
| 26 |
#include <limits.h> |
| 27 |
#include <assert.h> |
| 28 |
|
| 29 |
#ifdef HAVE_NET_SOCKET_H |
| 30 |
#include <net/socket.h> |
| 31 |
#endif |
| 32 |
#include <sys/socket.h> |
| 33 |
#include <netdb.h> |
| 34 |
#include "fm_md5.h" |
| 35 |
|
| 36 |
#include "i18n.h" |
| 37 |
#include "socket.h" |
| 38 |
#include "fetchmail.h" |
| 39 |
|
| 40 |
/** Macro to clamp the argument so it is >= INT_MIN. */ |
| 41 |
#define _FIX_INT_MIN(x) ((x) < INT_MIN ? INT_MIN : (x)) |
| 42 |
/** Macro to clamp the argument so it is <= INT_MAX. */ |
| 43 |
#define _FIX_INT_MAX(x) ((x) > INT_MAX ? INT_MAX : (x)) |
| 44 |
/** Macro to clamp the argument so it is representable as an int. */ |
| 45 |
#define CAST_TO_INT(x) ((int)(_FIX_INT_MIN(_FIX_INT_MAX(x)))) |
| 46 |
/** Macro to clamp the unsigned argument so it is representable as an int. */ |
| 47 |
#define UCAST_TO_INT(x) ((int)(_FIX_INT_MAX(x))) |
| 48 |
|
| 49 |
/* global variables: please reinitialize them explicitly for proper |
| 50 |
* working in daemon mode */ |
| 51 |
|
| 52 |
/* session variables initialized in init_transact() */ |
| 53 |
int suppress_tags = FALSE; /**< emit tags in the protocol? */ |
| 54 |
char tag[TAGLEN]; /**< buffer for the tag */ |
| 55 |
static int tagnum; /**< local counter for the tag */ |
| 56 |
/** Macro to generate the tag and store it in #tag. */ |
| 57 |
#define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag) |
| 58 |
static const struct method *protocol; /**< description of the protocol used for the current poll */ |
| 59 |
char shroud[PASSWORDLEN*2+3]; /**< string to shroud in debug output */ |
| 60 |
|
| 61 |
/* session variables initialized in do_session() */ |
| 62 |
int mytimeout; /**< value of nonreponse timeout */ |
| 63 |
|
| 64 |
/* mail variables initialized in readheaders() */ |
| 65 |
struct msgblk msgblk; /**< stores attributes of the currently processed message */ |
| 66 |
static int accept_count /** count of accepted recipients */, reject_count /** count of rejected recipients */; |
| 67 |
|
| 68 |
/** add given address to xmit_names if it exactly matches a full address |
| 69 |
* \returns nonzero if matched */ |
| 70 |
static int map_address(const char *addr,/**< address to match */ |
| 71 |
struct query *ctl, /**< contains list of aliases */ |
| 72 |
struct idlist **xmit_names /**< list of recipient names */) |
| 73 |
{ |
| 74 |
const char *lname; |
| 75 |
|
| 76 |
lname = idpair_find(&ctl->localnames, addr); |
| 77 |
if (lname) { |
| 78 |
if (outlevel >= O_DEBUG) |
| 79 |
report(stdout, GT_("mapped address %s to local %s\n"), addr, lname); |
| 80 |
save_str(xmit_names, lname, XMIT_ACCEPT); |
| 81 |
accept_count++; |
| 82 |
} |
| 83 |
return lname != NULL; |
| 84 |
} |
| 85 |
|
| 86 |
/** add given name to xmit_names if it matches declared localnames */ |
| 87 |
static void map_name(const char *name, struct query *ctl, struct idlist **xmit_names) |
| 88 |
/** \param name name to map */ |
| 89 |
/** \param ctl list of permissible aliases */ |
| 90 |
/** \param xmit_names list of recipient names parsed out */ |
| 91 |
{ |
| 92 |
const char *lname; |
| 93 |
|
| 94 |
lname = idpair_find(&ctl->localnames, name); |
| 95 |
if (!lname && ctl->wildcard) |
| 96 |
lname = name; |
| 97 |
|
| 98 |
if (lname != (char *)NULL) |
| 99 |
{ |
| 100 |
if (outlevel >= O_DEBUG) |
| 101 |
report(stdout, GT_("mapped %s to local %s\n"), name, lname); |
| 102 |
save_str(xmit_names, lname, XMIT_ACCEPT); |
| 103 |
accept_count++; |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
static void find_server_names(const char *hdr, |
| 108 |
struct query *ctl, |
| 109 |
struct idlist **xmit_names) |
| 110 |
/** parse names out of a RFC822 header into an ID list */ |
| 111 |
/** \param hdr RFC822 header in question */ |
| 112 |
/** \param ctl list of permissible aliases */ |
| 113 |
/** \param xmit_names list of recipient names parsed out */ |
| 114 |
{ |
| 115 |
if (hdr == (char *)NULL) |
| 116 |
return; |
| 117 |
else |
| 118 |
{ |
| 119 |
char *cp; |
| 120 |
|
| 121 |
for (cp = nxtaddr(hdr); cp != NULL; cp = nxtaddr(NULL)) |
| 122 |
{ |
| 123 |
char *atsign; |
| 124 |
|
| 125 |
/* |
| 126 |
* Handle empty address from a To: header containing only |
| 127 |
* a comment. |
| 128 |
*/ |
| 129 |
if (!*cp) |
| 130 |
continue; |
| 131 |
|
| 132 |
/* |
| 133 |
* If the name of the user begins with a qmail virtual |
| 134 |
* domain prefix, ignore the prefix. Doing this here |
| 135 |
* means qvirtual will work either with ordinary name |
| 136 |
* mapping or with a localdomains option. |
| 137 |
*/ |
| 138 |
if (ctl->server.qvirtual) |
| 139 |
{ |
| 140 |
int sl = strlen(ctl->server.qvirtual); |
| 141 |
|
| 142 |
if (!strncasecmp((char *)cp, ctl->server.qvirtual, sl)) |
| 143 |
cp += sl; |
| 144 |
} |
| 145 |
|
| 146 |
if ((atsign = strchr((char *)cp, '@'))) { |
| 147 |
struct idlist *idp; |
| 148 |
|
| 149 |
/* try to match full address first, this takes |
| 150 |
* precedence over localdomains and alias mappings */ |
| 151 |
if (map_address(cp, ctl, xmit_names)) |
| 152 |
goto nomap; |
| 153 |
|
| 154 |
/* |
| 155 |
* Does a trailing segment of the hostname match something |
| 156 |
* on the localdomains list? If so, save the whole name |
| 157 |
* and keep going. |
| 158 |
*/ |
| 159 |
for (idp = ctl->server.localdomains; idp; idp = idp->next) { |
| 160 |
char *rhs; |
| 161 |
|
| 162 |
rhs = atsign + (strlen(atsign) - strlen(idp->id)); |
| 163 |
if (rhs > atsign && |
| 164 |
(rhs[-1] == '.' || rhs[-1] == '@') && |
| 165 |
strcasecmp(rhs, idp->id) == 0) |
| 166 |
{ |
| 167 |
if (outlevel >= O_DEBUG) |
| 168 |
report(stdout, GT_("passed through %s matching %s\n"), |
| 169 |
cp, idp->id); |
| 170 |
save_str(xmit_names, (const char *)cp, XMIT_ACCEPT); |
| 171 |
accept_count++; |
| 172 |
goto nomap; |
| 173 |
} |
| 174 |
} |
| 175 |
|
| 176 |
/* if we matched a local domain, idp != NULL */ |
| 177 |
if (!idp) |
| 178 |
{ |
| 179 |
/* |
| 180 |
* Check to see if the right-hand part is an alias |
| 181 |
* or MX equivalent of the mailserver. If it's |
| 182 |
* not, skip this name. If it is, we'll keep |
| 183 |
* going and try to find a mapping to a client name. |
| 184 |
*/ |
| 185 |
if (!is_host_alias(atsign+1, ctl, &ai0)) |
| 186 |
{ |
| 187 |
save_str(xmit_names, cp, XMIT_REJECT); |
| 188 |
reject_count++; |
| 189 |
continue; |
| 190 |
} |
| 191 |
} |
| 192 |
atsign[0] = '\0'; |
| 193 |
map_name(cp, ctl, xmit_names); |
| 194 |
nomap:; |
| 195 |
} |
| 196 |
} |
| 197 |
} |
| 198 |
} |
| 199 |
|
| 200 |
/** |
| 201 |
* Return zero on a syntactically invalid address, nz on a valid one. |
| 202 |
* |
| 203 |
* This used to be strchr(a, '.'), but it turns out that lines like this |
| 204 |
* |
| 205 |
* Received: from punt-1.mail.demon.net by mailstore for markb@example.com |
| 206 |
* id 938765929:10:27223:2; Fri, 01 Oct 99 08:18:49 GMT |
| 207 |
* |
| 208 |
* are not uncommon. So now we just check that the following token is |
| 209 |
* not itself an email address. |
| 210 |
*/ |
| 211 |
#define VALID_ADDRESS(a) (!strchr((a), '@')) |
| 212 |
|
| 213 |
/** write \a value into \a rbuf, indexed by \a tp, if there is |
| 214 |
* sufficient room left. */ |
| 215 |
#define RBUF_WRITE(value) do { if (tp < rbuf+sizeof(rbuf)-1) *tp++=(value); } while(0) |
| 216 |
|
| 217 |
/** Try to extract real address from the Received line. |
| 218 |
* If a valid Received: line is found, we return the full address in |
| 219 |
* a buffer which can be parsed from nxtaddr(). This is to ensure that |
| 220 |
* the local domain part of the address can be passed along in |
| 221 |
* find_server_names() if it contains one. |
| 222 |
* Note: We should return a dummy header containing the address |
| 223 |
* which makes nxtaddr() behave correctly. |
| 224 |
*/ |
| 225 |
static char *parse_received(struct query *ctl, char *bufp) |
| 226 |
{ |
| 227 |
char *base, *ok = (char *)NULL; |
| 228 |
static char rbuf[HOSTLEN + USERNAMELEN + 4]; |
| 229 |
struct addrinfo *ai0; |
| 230 |
|
| 231 |
/* |
| 232 |
* Try to extract the real envelope addressee. We look here |
| 233 |
* specifically for the mailserver's Received line. |
| 234 |
* Note: this will only work for sendmail, or an MTA that |
| 235 |
* shares sendmail's convention for embedding the envelope |
| 236 |
* address in the Received line. Sendmail itself only |
| 237 |
* does this when the mail has a single recipient. |
| 238 |
*/ |
| 239 |
if (outlevel >= O_DEBUG) |
| 240 |
report(stdout, GT_("analyzing Received line:\n%s"), bufp); |
| 241 |
|
| 242 |
/* search for whitepace-surrounded "by" followed by valid address */ |
| 243 |
for (base = bufp; ; base = ok + 2) |
| 244 |
{ |
| 245 |
if (!(ok = strstr(base, "by"))) |
| 246 |
break; |
| 247 |
else if (!isspace((unsigned char)ok[-1]) || !isspace((unsigned char)ok[2])) |
| 248 |
continue; |
| 249 |
else |
| 250 |
{ |
| 251 |
char *sp, *tp; |
| 252 |
|
| 253 |
/* extract space-delimited token after "by" */ |
| 254 |
for (sp = ok + 2; isspace((unsigned char)*sp); sp++) |
| 255 |
continue; |
| 256 |
tp = rbuf; |
| 257 |
for (; *sp && !isspace((unsigned char)*sp); sp++) |
| 258 |
RBUF_WRITE(*sp); |
| 259 |
*tp = '\0'; |
| 260 |
|
| 261 |
/* look for valid address */ |
| 262 |
if (VALID_ADDRESS(rbuf)) |
| 263 |
break; |
| 264 |
else |
| 265 |
ok = sp - 1; /* arrange to skip this token */ |
| 266 |
} |
| 267 |
} |
| 268 |
if (ok) |
| 269 |
{ |
| 270 |
/* |
| 271 |
* If it's a DNS name of the mail server, look for the |
| 272 |
* recipient name after a following "for". Otherwise |
| 273 |
* punt. |
| 274 |
*/ |
| 275 |
if (is_host_alias(rbuf, ctl, &ai0)) |
| 276 |
{ |
| 277 |
if (outlevel >= O_DEBUG) |
| 278 |
report(stdout, |
| 279 |
GT_("line accepted, %s is an alias of the mailserver\n"), rbuf); |
| 280 |
} |
| 281 |
else |
| 282 |
{ |
| 283 |
if (outlevel >= O_DEBUG) |
| 284 |
report(stdout, |
| 285 |
GT_("line rejected, %s is not an alias of the mailserver\n"), |
| 286 |
rbuf); |
| 287 |
return(NULL); |
| 288 |
} |
| 289 |
|
| 290 |
/* search for whitepace-surrounded "for" followed by xxxx@yyyy */ |
| 291 |
for (base = ok + 4 + strlen(rbuf); ; base = ok + 2) |
| 292 |
{ |
| 293 |
if (!(ok = strstr(base, "for"))) |
| 294 |
break; |
| 295 |
else if (!isspace((unsigned char)ok[-1]) || !isspace((unsigned char)ok[3])) |
| 296 |
continue; |
| 297 |
else |
| 298 |
{ |
| 299 |
char *sp, *tp; |
| 300 |
|
| 301 |
/* extract space-delimited token after "for" */ |
| 302 |
for (sp = ok + 3; isspace((unsigned char)*sp); sp++) |
| 303 |
continue; |
| 304 |
tp = rbuf; |
| 305 |
for (; !isspace((unsigned char)*sp); sp++) |
| 306 |
RBUF_WRITE(*sp); |
| 307 |
*tp = '\0'; |
| 308 |
|
| 309 |
if (strchr(rbuf, '@')) |
| 310 |
break; |
| 311 |
else |
| 312 |
ok = sp - 1; /* arrange to skip this token */ |
| 313 |
} |
| 314 |
} |
| 315 |
if (ok) |
| 316 |
{ |
| 317 |
flag want_gt = FALSE; |
| 318 |
char *sp, *tp; |
| 319 |
|
| 320 |
/* char after "for" could be space or a continuation newline */ |
| 321 |
for (sp = ok + 4; isspace((unsigned char)*sp); sp++) |
| 322 |
continue; |
| 323 |
tp = rbuf; |
| 324 |
RBUF_WRITE(':'); /* Here is the hack. This is to be friends */ |
| 325 |
RBUF_WRITE(' '); /* with nxtaddr()... */ |
| 326 |
if (*sp == '<') |
| 327 |
{ |
| 328 |
want_gt = TRUE; |
| 329 |
sp++; |
| 330 |
} |
| 331 |
while (*sp == '@') /* skip routes */ |
| 332 |
while (*sp && *sp++ != ':') |
| 333 |
continue; |
| 334 |
while (*sp |
| 335 |
&& (want_gt ? (*sp != '>') : !isspace((unsigned char)*sp)) |
| 336 |
&& *sp != ';') |
| 337 |
if (!isspace((unsigned char)*sp)) |
| 338 |
{ |
| 339 |
RBUF_WRITE(*sp); |
| 340 |
sp++; |
| 341 |
} |
| 342 |
else |
| 343 |
{ |
| 344 |
/* uh oh -- whitespace here can't be right! */ |
| 345 |
ok = (char *)NULL; |
| 346 |
break; |
| 347 |
} |
| 348 |
RBUF_WRITE('\n'); |
| 349 |
*tp = '\0'; |
| 350 |
if (strlen(rbuf) <= 3) /* apparently nothing has been found */ |
| 351 |
ok = NULL; |
| 352 |
} else |
| 353 |
ok = (char *)NULL; |
| 354 |
} |
| 355 |
|
| 356 |
if (!ok) |
| 357 |
{ |
| 358 |
if (outlevel >= O_DEBUG) |
| 359 |
report(stdout, GT_("no Received address found\n")); |
| 360 |
return(NULL); |
| 361 |
} |
| 362 |
else |
| 363 |
{ |
| 364 |
if (outlevel >= O_DEBUG) { |
| 365 |
char *lf = rbuf + strlen(rbuf)-1; |
| 366 |
*lf = '\0'; |
| 367 |
if (outlevel >= O_DEBUG) |
| 368 |
report(stdout, GT_("found Received address `%s'\n"), rbuf+2); |
| 369 |
*lf = '\n'; |
| 370 |
} |
| 371 |
return(rbuf); |
| 372 |
} |
| 373 |
} |
| 374 |
|
| 375 |
/* shared by readheaders and readbody */ |
| 376 |
static int sizeticker; /**< internal state variable for print_ticker() */ |
| 377 |
|
| 378 |
/** Print ticker based on a amount of data transferred of \a bytes. |
| 379 |
* Increments \a *tickervar by \a bytes, and if it exceeds |
| 380 |
* \a SIZETICKER, print a dot and reduce *tickervar by \a SIZETICKER. */ |
| 381 |
static void print_ticker(int *tickervar, int bytes) |
| 382 |
{ |
| 383 |
*tickervar += bytes; |
| 384 |
while (*tickervar >= SIZETICKER) |
| 385 |
{ |
| 386 |
if (want_progress()) |
| 387 |
{ |
| 388 |
fputc('.', stdout); |
| 389 |
fflush(stdout); |
| 390 |
} |
| 391 |
*tickervar -= SIZETICKER; |
| 392 |
} |
| 393 |
} |
| 394 |
|
| 395 |
/** Check if \a s is equal to a LF or CR LF sequence, followed by a NUL |
| 396 |
* byte. \todo FIXME merge this with end_of_header? */ |
| 397 |
#define EMPTYLINE(s) (((s)[0] == '\r' && (s)[1] == '\n' && (s)[2] == '\0') \ |
| 398 |
|| ((s)[0] == '\n' && (s)[1] == '\0')) |
| 399 |
|
| 400 |
/** Check if \a s is an empty line. Accept "\r*\n" as EOH in order to be bulletproof against broken survers */ |
| 401 |
static int end_of_header (const char *s) |
| 402 |
{ |
| 403 |
while (s[0] == '\r') |
| 404 |
s++; |
| 405 |
return (s[0] == '\n' && s[1] == '\0'); |
| 406 |
} |
| 407 |
|
| 408 |
/** read message headers and ship to SMTP or MDA */ |
| 409 |
int readheaders(int sock, |
| 410 |
long fetchlen, |
| 411 |
long reallen, |
| 412 |
struct query *ctl, |
| 413 |
int num, |
| 414 |
flag *suppress_readbody) |
| 415 |
/** \param sock to which the server is connected */ |
| 416 |
/** \param fetchlen length of message according to fetch response */ |
| 417 |
/** \param reallen length of message according to getsizes */ |
| 418 |
/** \param ctl query control record */ |
| 419 |
/** \param num index of message */ |
| 420 |
/** \param suppress_readbody output: whether call to readbody() should be supressed */ |
| 421 |
{ |
| 422 |
struct addrblk |
| 423 |
{ |
| 424 |
int offset; |
| 425 |
struct addrblk *next; |
| 426 |
}; |
| 427 |
struct addrblk *to_addrchain = NULL; |
| 428 |
struct addrblk **to_chainptr = &to_addrchain; |
| 429 |
struct addrblk *resent_to_addrchain = NULL; |
| 430 |
struct addrblk **resent_to_chainptr = &resent_to_addrchain; |
| 431 |
|
| 432 |
char buf[MSGBUFSIZE+1]; |
| 433 |
int from_offs, reply_to_offs, resent_from_offs; |
| 434 |
int app_from_offs, sender_offs, resent_sender_offs; |
| 435 |
int env_offs; |
| 436 |
char *received_for, *rcv, *cp; |
| 437 |
static char *delivered_to = NULL; |
| 438 |
int n, oldlen, ch, remaining, skipcount; |
| 439 |
size_t linelen; |
| 440 |
int delivered_to_count; |
| 441 |
struct idlist *idp; |
| 442 |
flag no_local_matches = FALSE; |
| 443 |
flag has_nuls; |
| 444 |
int olderrs, good_addresses, bad_addresses; |
| 445 |
int retain_mail = 0, refuse_mail = 0; |
| 446 |
flag already_has_return_path = FALSE; |
| 447 |
|
| 448 |
sizeticker = 0; |
| 449 |
has_nuls = FALSE; |
| 450 |
msgblk.return_path[0] = '\0'; |
| 451 |
olderrs = ctl->errcount; |
| 452 |
|
| 453 |
/* read message headers */ |
| 454 |
msgblk.reallen = reallen; |
| 455 |
|
| 456 |
/* |
| 457 |
* We used to free the header block unconditionally at the end of |
| 458 |
* readheaders, but it turns out that if close_sink() hits an error |
| 459 |
* condition the code for sending bouncemail will actually look |
| 460 |
* at the freed storage and coredump... |
| 461 |
*/ |
| 462 |
xfree(msgblk.headers); |
| 463 |
free_str_list(&msgblk.recipients); |
| 464 |
xfree(delivered_to); |
| 465 |
|
| 466 |
/* initially, no message digest */ |
| 467 |
memset(ctl->digest, '\0', sizeof(ctl->digest)); |
| 468 |
|
| 469 |
received_for = NULL; |
| 470 |
from_offs = reply_to_offs = resent_from_offs = app_from_offs = |
| 471 |
sender_offs = resent_sender_offs = env_offs = -1; |
| 472 |
oldlen = 0; |
| 473 |
msgblk.msglen = 0; |
| 474 |
skipcount = 0; |
| 475 |
delivered_to_count = 0; |
| 476 |
ctl->mimemsg = 0; |
| 477 |
|
| 478 |
for (remaining = fetchlen; remaining > 0 || protocol->delimited; ) |
| 479 |
{ |
| 480 |
char *line, *rline; |
| 481 |
|
| 482 |
line = (char *)xmalloc(sizeof(buf)); |
| 483 |
linelen = 0; |
| 484 |
line[0] = '\0'; |
| 485 |
do { |
| 486 |
do { |
| 487 |
char *sp, *tp; |
| 488 |
|
| 489 |
set_timeout(mytimeout); |
| 490 |
if ((n = SockRead(sock, buf, sizeof(buf)-1)) == -1) { |
| 491 |
set_timeout(0); |
| 492 |
free(line); |
| 493 |
return(PS_SOCKET); |
| 494 |
} |
| 495 |
set_timeout(0); |
| 496 |
|
| 497 |
/* |
| 498 |
* Smash out any NULs, they could wreak havoc later on. |
| 499 |
* Some network stacks seem to generate these at random, |
| 500 |
* especially (according to reports) at the beginning of the |
| 501 |
* first read. NULs are illegal in RFC822 format. |
| 502 |
*/ |
| 503 |
for (sp = tp = buf; sp < buf + n; sp++) |
| 504 |
if (*sp) |
| 505 |
*tp++ = *sp; |
| 506 |
*tp = '\0'; |
| 507 |
n = tp - buf; |
| 508 |
} while |
| 509 |
(n == 0); |
| 510 |
|
| 511 |
remaining -= n; |
| 512 |
linelen += n; |
| 513 |
msgblk.msglen += n; |
| 514 |
|
| 515 |
/* |
| 516 |
* Try to gracefully handle the case where the length of a |
| 517 |
* line exceeds MSGBUFSIZE. |
| 518 |
*/ |
| 519 |
if (n && buf[n-1] != '\n') |
| 520 |
{ |
| 521 |
rline = (char *) realloc(line, linelen + 1); |
| 522 |
if (rline == NULL) |
| 523 |
{ |
| 524 |
free (line); |
| 525 |
return(PS_IOERR); |
| 526 |
} |
| 527 |
line = rline; |
| 528 |
memcpy(line + linelen - n, buf, n); |
| 529 |
line[linelen] = '\0'; |
| 530 |
ch = ' '; /* So the next iteration starts */ |
| 531 |
continue; |
| 532 |
} |
| 533 |
|
| 534 |
/* lines may not be properly CRLF terminated; fix this for qmail */ |
| 535 |
/* we don't want to overflow the buffer here */ |
| 536 |
if (ctl->forcecr && buf[n-1]=='\n' && (n==1 || buf[n-2]!='\r')) |
| 537 |
{ |
| 538 |
char * tcp; |
| 539 |
rline = (char *) realloc(line, linelen + 2); |
| 540 |
if (rline == NULL) |
| 541 |
{ |
| 542 |
free (line); |
| 543 |
return(PS_IOERR); |
| 544 |
} |
| 545 |
line = rline; |
| 546 |
memcpy(line + linelen - n, buf, n - 1); |
| 547 |
tcp = line + linelen - 1; |
| 548 |
*tcp++ = '\r'; |
| 549 |
*tcp++ = '\n'; |
| 550 |
*tcp = '\0'; |
| 551 |
/* n++; - not used later on */ |
| 552 |
linelen++; |
| 553 |
} |
| 554 |
else |
| 555 |
{ |
| 556 |
rline = (char *) realloc(line, linelen + 1); |
| 557 |
if (rline == NULL) |
| 558 |
{ |
| 559 |
free (line); |
| 560 |
return(PS_IOERR); |
| 561 |
} |
| 562 |
line = rline; |
| 563 |
memcpy(line + linelen - n, buf, n + 1); |
| 564 |
} |
| 565 |
|
| 566 |
/* check for end of headers */ |
| 567 |
if (end_of_header(line)) |
| 568 |
{ |
| 569 |
eoh: |
| 570 |
if (linelen != strlen (line)) |
| 571 |
has_nuls = TRUE; |
| 572 |
free(line); |
| 573 |
goto process_headers; |
| 574 |
} |
| 575 |
|
| 576 |
/* |
| 577 |
* Check for end of message immediately. If one of your folders |
| 578 |
* has been mangled, the delimiter may occur directly after the |
| 579 |
* header. |
| 580 |
*/ |
| 581 |
if (protocol->delimited && line[0] == '.' && EMPTYLINE(line+1)) |
| 582 |
{ |
| 583 |
if (suppress_readbody) |
| 584 |
*suppress_readbody = TRUE; |
| 585 |
goto eoh; /* above */ |
| 586 |
} |
| 587 |
|
| 588 |
/* |
| 589 |
* At least one brain-dead website (netmind.com) is known to |
| 590 |
* send out robotmail that's missing the RFC822 delimiter blank |
| 591 |
* line before the body! Without this check fetchmail segfaults. |
| 592 |
* With it, we treat such messages as spam and refuse them. |
| 593 |
* |
| 594 |
* Frederic Marchal reported in February 2006 that hotmail |
| 595 |
* or something improperly wrapped a very long TO header |
| 596 |
* (wrapped without inserting whitespace in the continuation |
| 597 |
* line) and found that this code thus refused a message |
| 598 |
* that should have been delivered. |
| 599 |
* |
| 600 |
* XXX FIXME: we should probably wrap the message up as |
| 601 |
* message/rfc822 attachment and forward to postmaster (Rob |
| 602 |
* MacGregor) |
| 603 |
*/ |
| 604 |
if (!refuse_mail |
| 605 |
&& !ctl->server.badheader == BHACCEPT |
| 606 |
&& !isspace((unsigned char)line[0]) |
| 607 |
&& !strchr(line, ':')) |
| 608 |
{ |
| 609 |
if (linelen != strlen (line)) |
| 610 |
has_nuls = TRUE; |
| 611 |
if (outlevel > O_SILENT) |
| 612 |
report(stdout, |
| 613 |
GT_("incorrect header line found - see manpage for bad-header option\n")); |
| 614 |
if (outlevel >= O_VERBOSE) |
| 615 |
report (stdout, GT_("line: %s"), line); |
| 616 |
refuse_mail = 1; |
| 617 |
} |
| 618 |
|
| 619 |
/* check for RFC822 continuations */ |
| 620 |
set_timeout(mytimeout); |
| 621 |
ch = SockPeek(sock); |
| 622 |
set_timeout(0); |
| 623 |
} while |
| 624 |
(ch == ' ' || ch == '\t'); /* continuation to next line? */ |
| 625 |
|
| 626 |
/* write the message size dots */ |
| 627 |
if ((outlevel > O_SILENT && outlevel < O_VERBOSE) && linelen > 0) |
| 628 |
{ |
| 629 |
print_ticker(&sizeticker, linelen); |
| 630 |
} |
| 631 |
|
| 632 |
/* |
| 633 |
* Decode MIME encoded headers. We MUST do this before |
| 634 |
* looking at the Content-Type / Content-Transfer-Encoding |
| 635 |
* headers (RFC 2046). |
| 636 |
*/ |
| 637 |
if ( ctl->mimedecode ) |
| 638 |
{ |
| 639 |
char *tcp; |
| 640 |
UnMimeHeader(line); |
| 641 |
/* the line is now shorter. So we retrace back till we find |
| 642 |
* our terminating combination \n\0, we move backwards to |
| 643 |
* make sure that we don't catch some \n\0 stored in the |
| 644 |
* decoded part of the message */ |
| 645 |
for (tcp = line + linelen - 1; tcp > line && (*tcp != 0 || tcp[-1] != '\n'); tcp--) { } |
| 646 |
if (tcp > line) linelen = tcp - line; |
| 647 |
} |
| 648 |
|
| 649 |
|
| 650 |
/* skip processing if we are going to retain or refuse this mail */ |
| 651 |
if (retain_mail || refuse_mail) |
| 652 |
{ |
| 653 |
free(line); |
| 654 |
continue; |
| 655 |
} |
| 656 |
|
| 657 |
/* we see an ordinary (non-header, non-message-delimiter) line */ |
| 658 |
if (linelen != strlen (line)) |
| 659 |
has_nuls = TRUE; |
| 660 |
|
| 661 |
/* |
| 662 |
* The University of Washington IMAP server (the reference |
| 663 |
* implementation of IMAP4 written by Mark Crispin) relies |
| 664 |
* on being able to keep base-UID information in a special |
| 665 |
* message at the head of the mailbox. This message should |
| 666 |
* neither be deleted nor forwarded. |
| 667 |
* |
| 668 |
* An example for such a message is (keep this in so people |
| 669 |
* find it when looking where the special code is to handle the |
| 670 |
* data): |
| 671 |
* |
| 672 |
* From MAILER-DAEMON Wed Nov 23 11:38:42 2005 |
| 673 |
* Date: 23 Nov 2005 11:38:42 +0100 |
| 674 |
* From: Mail System Internal Data <MAILER-DAEMON@mail.example.org> |
| 675 |
* Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA |
| 676 |
* Message-ID: <1132742322@mail.example.org> |
| 677 |
* X-IMAP: 1132742306 0000000001 |
| 678 |
* Status: RO |
| 679 |
* |
| 680 |
* This text is part of the internal format of your mail folder, and is not |
| 681 |
* a real message. It is created automatically by the mail system software. |
| 682 |
* If deleted, important folder data will be lost, and it will be re-created |
| 683 |
* with the data reset to initial values. |
| 684 |
* |
| 685 |
* This message is only visible if a POP3 server that is unaware |
| 686 |
* of these UWIMAP messages is used besides UWIMAP or PINE. |
| 687 |
* |
| 688 |
* We will just check if the first message in the mailbox has an |
| 689 |
* X-IMAP: header. |
| 690 |
*/ |
| 691 |
#ifdef POP2_ENABLE |
| 692 |
/* |
| 693 |
* We disable this check under POP2 because there's no way to |
| 694 |
* prevent deletion of the message. So at least we ought to |
| 695 |
* forward it to the user so he or she will have some clue |
| 696 |
* that things have gone awry. |
| 697 |
*/ |
| 698 |
if (servport("pop2") != servport(protocol->service)) |
| 699 |
#endif /* POP2_ENABLE */ |
| 700 |
if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) { |
| 701 |
free(line); |
| 702 |
retain_mail = 1; |
| 703 |
continue; |
| 704 |
} |
| 705 |
|
| 706 |
/* |
| 707 |
* This code prevents fetchmail from becoming an accessory after |
| 708 |
* the fact to upstream sendmails with the `E' option on. It also |
| 709 |
* copes with certain brain-dead POP servers (like NT's) that pass |
| 710 |
* through Unix from_ lines. |
| 711 |
* |
| 712 |
* Either of these bugs can result in a non-RFC822 line at the |
| 713 |
* beginning of the headers. If fetchmail just passes it |
| 714 |
* through, the client listener may think the message has *no* |
| 715 |
* headers (since the first) line it sees doesn't look |
| 716 |
* RFC822-conformant) and fake up a set. |
| 717 |
* |
| 718 |
* What the user would see in this case is bogus (synthesized) |
| 719 |
* headers, followed by a blank line, followed by the >From, |
| 720 |
* followed by the real headers, followed by a blank line, |
| 721 |
* followed by text. |
| 722 |
* |
| 723 |
* We forestall this lossage by tossing anything that looks |
| 724 |
* like an escaped or passed-through From_ line in headers. |
| 725 |
* These aren't RFC822 so our conscience is clear... |
| 726 |
*/ |
| 727 |
if (!strncasecmp(line, ">From ", 6) || !strncasecmp(line, "From ", 5)) |
| 728 |
{ |
| 729 |
free(line); |
| 730 |
continue; |
| 731 |
} |
| 732 |
|
| 733 |
/* |
| 734 |
* We remove all Delivered-To: headers if dropdelivered is set |
| 735 |
* - special care must be taken if Delivered-To: is also used |
| 736 |
* as envelope at the same time. |
| 737 |
* |
| 738 |
* This is to avoid false mail loops errors when delivering |
| 739 |
* local messages to and from a Postfix or qmail mailserver. |
| 740 |
*/ |
| 741 |
if (ctl->dropdelivered && !strncasecmp(line, "Delivered-To:", 13)) |
| 742 |
{ |
| 743 |
if (delivered_to || |
| 744 |
ctl->server.envelope == STRING_DISABLED || |
| 745 |
!ctl->server.envelope || |
| 746 |
strcasecmp(ctl->server.envelope, "Delivered-To") || |
| 747 |
delivered_to_count != ctl->server.envskip) |
| 748 |
free(line); |
| 749 |
else |
| 750 |
delivered_to = line; |
| 751 |
delivered_to_count++; |
| 752 |
continue; |
| 753 |
} |
| 754 |
|
| 755 |
/* |
| 756 |
* If we see a Status line, it may have been inserted by an MUA |
| 757 |
* on the mail host, or it may have been inserted by the server |
| 758 |
* program after the headers in the transaction stream. This |
| 759 |
* can actually hose some new-mail notifiers such as xbuffy, |
| 760 |
* which assumes any Status line came from a *local* MDA and |
| 761 |
* therefore indicates that the message has been seen. |
| 762 |
* |
| 763 |
* Some buggy POP servers (including at least the 3.3(20) |
| 764 |
* version of the one distributed with IMAP) insert empty |
| 765 |
* Status lines in the transaction stream; we'll chuck those |
| 766 |
* unconditionally. Nonempty ones get chucked if the user |
| 767 |
* turns on the dropstatus flag. |
| 768 |
*/ |
| 769 |
{ |
| 770 |
char *tcp; |
| 771 |
|
| 772 |
if (!strncasecmp(line, "Status:", 7)) |
| 773 |
tcp = line + 7; |
| 774 |
else if (!strncasecmp(line, "X-Mozilla-Status:", 17)) |
| 775 |
tcp = line + 17; |
| 776 |
else |
| 777 |
tcp = NULL; |
| 778 |
if (tcp) { |
| 779 |
while (*tcp && isspace((unsigned char)*tcp)) tcp++; |
| 780 |
if (!*tcp || ctl->dropstatus) |
| 781 |
{ |
| 782 |
free(line); |
| 783 |
continue; |
| 784 |
} |
| 785 |
} |
| 786 |
} |
| 787 |
|
| 788 |
if (ctl->rewrite) |
| 789 |
line = reply_hack(line, ctl->server.truename, &linelen); |
| 790 |
|
| 791 |
/* |
| 792 |
* OK, this is messy. If we're forwarding by SMTP, it's the |
| 793 |
* SMTP-receiver's job (according to RFC821, page 22, section |
| 794 |
* 4.1.1) to generate a Return-Path line on final delivery. |
| 795 |
* The trouble is, we've already got one because the |
| 796 |
* mailserver's SMTP thought *it* was responsible for final |
| 797 |
* delivery. |
| 798 |
* |
| 799 |
* Stash away the contents of Return-Path (as modified by reply_hack) |
| 800 |
* for use in generating MAIL FROM later on, then prevent the header |
| 801 |
* from being saved with the others. In effect, we strip it off here. |
| 802 |
* |
| 803 |
* If the SMTP server conforms to the standards, and fetchmail gets the |
| 804 |
* envelope sender from the Return-Path, the new Return-Path should be |
| 805 |
* exactly the same as the original one. |
| 806 |
* |
| 807 |
* We do *not* want to ignore empty Return-Path headers. These should |
| 808 |
* be passed through as a way of indicating that a message should |
| 809 |
* not trigger bounces if delivery fails. What we *do* need to do is |
| 810 |
* make sure we never try to rewrite such a blank Return-Path. We |
| 811 |
* handle this with a check for <> in the rewrite logic above. |
| 812 |
* |
| 813 |
* Also, if an email has multiple Return-Path: headers, we only |
| 814 |
* read the first occurance, as some spam email has more than one |
| 815 |
* Return-Path. |
| 816 |
* |
| 817 |
*/ |
| 818 |
if ((already_has_return_path==FALSE) && !strncasecmp("Return-Path:", line, 12) && (cp = nxtaddr(line))) |
| 819 |
{ |
| 820 |
char nulladdr[] = "<>"; |
| 821 |
already_has_return_path = TRUE; |
| 822 |
if (cp[0]=='\0') /* nxtaddr() strips the brackets... */ |
| 823 |
cp=nulladdr; |
| 824 |
strncpy(msgblk.return_path, cp, sizeof(msgblk.return_path)); |
| 825 |
msgblk.return_path[sizeof(msgblk.return_path)-1] = '\0'; |
| 826 |
if (!ctl->mda) { |
| 827 |
free(line); |
| 828 |
continue; |
| 829 |
} |
| 830 |
} |
| 831 |
|
| 832 |
if (!msgblk.headers) |
| 833 |
{ |
| 834 |
oldlen = linelen; |
| 835 |
msgblk.headers = (char *)xmalloc(oldlen + 1); |
| 836 |
(void) memcpy(msgblk.headers, line, linelen); |
| 837 |
msgblk.headers[oldlen] = '\0'; |
| 838 |
free(line); |
| 839 |
line = msgblk.headers; |
| 840 |
} |
| 841 |
else |
| 842 |
{ |
| 843 |
char *newhdrs; |
| 844 |
int newlen; |
| 845 |
|
| 846 |
newlen = oldlen + linelen; |
| 847 |
newhdrs = (char *) realloc(msgblk.headers, newlen + 1); |
| 848 |
if (newhdrs == NULL) { |
| 849 |
free(line); |
| 850 |
return(PS_IOERR); |
| 851 |
} |
| 852 |
msgblk.headers = newhdrs; |
| 853 |
memcpy(msgblk.headers + oldlen, line, linelen); |
| 854 |
msgblk.headers[newlen] = '\0'; |
| 855 |
free(line); |
| 856 |
line = msgblk.headers + oldlen; |
| 857 |
oldlen = newlen; |
| 858 |
} |
| 859 |
|
| 860 |
/* find offsets of various special headers */ |
| 861 |
if (!strncasecmp("From:", line, 5)) |
| 862 |
from_offs = (line - msgblk.headers); |
| 863 |
else if (!strncasecmp("Reply-To:", line, 9)) |
| 864 |
reply_to_offs = (line - msgblk.headers); |
| 865 |
else if (!strncasecmp("Resent-From:", line, 12)) |
| 866 |
resent_from_offs = (line - msgblk.headers); |
| 867 |
else if (!strncasecmp("Apparently-From:", line, 16)) |
| 868 |
app_from_offs = (line - msgblk.headers); |
| 869 |
/* |
| 870 |
* Netscape 4.7 puts "Sender: zap" in mail headers. Perverse... |
| 871 |
* |
| 872 |
* But a literal reading of RFC822 sec. 4.4.2 supports the idea |
| 873 |
* that Sender: *doesn't* have to be a working email address. |
| 874 |
* |
| 875 |
* The definition of the Sender header in RFC822 says, in |
| 876 |
* part, "The Sender mailbox specification includes a word |
| 877 |
* sequence which must correspond to a specific agent (i.e., a |
| 878 |
* human user or a computer program) rather than a standard |
| 879 |
* address." That implies that the contents of the Sender |
| 880 |
* field don't need to be a legal email address at all So |
| 881 |
* ignore any Sender or Resent-Sender lines unless they |
| 882 |
* contain @. |
| 883 |
* |
| 884 |
* (RFC2822 says the contents of Sender must be a valid mailbox |
| 885 |
* address, which is also what RFC822 4.4.4 implies.) |
| 886 |
*/ |
| 887 |
else if (!strncasecmp("Sender:", line, 7) && (strchr(line, '@') || strchr(line, '!'))) |
| 888 |
sender_offs = (line - msgblk.headers); |
| 889 |
else if (!strncasecmp("Resent-Sender:", line, 14) && (strchr(line, '@') || strchr(line, '!'))) |
| 890 |
resent_sender_offs = (line - msgblk.headers); |
| 891 |
|
| 892 |
#ifdef __UNUSED__ |
| 893 |
else if (!strncasecmp("Message-Id:", line, 11)) |
| 894 |
{ |
| 895 |
if (ctl->server.uidl) |
| 896 |
{ |
| 897 |
char id[IDLEN+1]; |
| 898 |
|
| 899 |
line[IDLEN+12] = 0; /* prevent stack overflow */ |
| 900 |
sscanf(line+12, "%s", id); |
| 901 |
if (!str_find( &ctl->newsaved, num)) |
| 902 |
{ |
| 903 |
struct idlist *newl = save_str(&ctl->newsaved,id,UID_SEEN); |
| 904 |
newl->val.status.num = num; |
| 905 |
} |
| 906 |
} |
| 907 |
} |
| 908 |
#endif /* __UNUSED__ */ |
| 909 |
|
| 910 |
/* if multidrop is on, gather addressee headers */ |
| 911 |
if (MULTIDROP(ctl)) |
| 912 |
{ |
| 913 |
if (!strncasecmp("To:", line, 3) |
| 914 |
|| !strncasecmp("Cc:", line, 3) |
| 915 |
|| !strncasecmp("Bcc:", line, 4) |
| 916 |
|| !strncasecmp("Apparently-To:", line, 14)) |
| 917 |
{ |
| 918 |
*to_chainptr = (struct addrblk *)xmalloc(sizeof(struct addrblk)); |
| 919 |
(*to_chainptr)->offset = (line - msgblk.headers); |
| 920 |
to_chainptr = &(*to_chainptr)->next; |
| 921 |
*to_chainptr = NULL; |
| 922 |
} |
| 923 |
|
| 924 |
else if (!strncasecmp("Resent-To:", line, 10) |
| 925 |
|| !strncasecmp("Resent-Cc:", line, 10) |
| 926 |
|| !strncasecmp("Resent-Bcc:", line, 11)) |
| 927 |
{ |
| 928 |
*resent_to_chainptr = (struct addrblk *)xmalloc(sizeof(struct addrblk)); |
| 929 |
(*resent_to_chainptr)->offset = (line - msgblk.headers); |
| 930 |
resent_to_chainptr = &(*resent_to_chainptr)->next; |
| 931 |
*resent_to_chainptr = NULL; |
| 932 |
} |
| 933 |
|
| 934 |
else if (ctl->server.envelope != STRING_DISABLED) |
| 935 |
{ |
| 936 |
if (ctl->server.envelope |
| 937 |
&& strcasecmp(ctl->server.envelope, "Received")) |
| 938 |
{ |
| 939 |
if (env_offs == -1 && !strncasecmp(ctl->server.envelope, |
| 940 |
line, |
| 941 |
strlen(ctl->server.envelope))) |
| 942 |
{ |
| 943 |
if (skipcount++ < ctl->server.envskip) |
| 944 |
continue; |
| 945 |
env_offs = (line - msgblk.headers); |
| 946 |
} |
| 947 |
} |
| 948 |
else if (!received_for && !strncasecmp("Received:", line, 9)) |
| 949 |
{ |
| 950 |
if (skipcount++ < ctl->server.envskip) |
| 951 |
continue; |
| 952 |
received_for = parse_received(ctl, line); |
| 953 |
} |
| 954 |
} |
| 955 |
} |
| 956 |
} |
| 957 |
|
| 958 |
process_headers: |
| 959 |
|
| 960 |
if (retain_mail) { |
| 961 |
return(PS_RETAINED); |
| 962 |
} |
| 963 |
|
| 964 |
if (refuse_mail) |
| 965 |
return(PS_REFUSED); |
| 966 |
/* |
| 967 |
* This is the duplicate-message killer code. |
| 968 |
* |
| 969 |
* When mail delivered to a multidrop mailbox on the server is |
| 970 |
* addressed to multiple people on the client machine, there will |
| 971 |
* be one copy left in the box for each recipient. This is not a |
| 972 |
* problem if we have the actual recipient address to dispatch on |
| 973 |
* (e.g. because we've mined it out of sendmail trace headers, or |
| 974 |
* a qmail Delivered-To line, or a declared sender envelope line). |
| 975 |
* |
| 976 |
* But if we're mining addressees out of the To/Cc/Bcc fields, and |
| 977 |
* if the mail is addressed to N people, each recipient will |
| 978 |
* get N copies. This is bad when N > 1. |
| 979 |
* |
| 980 |
* Foil this by suppressing all but one copy of a message with a |
| 981 |
* given set of headers. |
| 982 |
* |
| 983 |
* Note: This implementation only catches runs of successive |
| 984 |
* messages with the same ID, but that should be good |
| 985 |
* enough. A more general implementation would have to store |
| 986 |
* ever-growing lists of seen message-IDs; in a long-running |
| 987 |
* daemon this would turn into a memory leak even if the |
| 988 |
* implementation were perfect. |
| 989 |
* |
| 990 |
* Don't mess with this code casually. It would be way too easy |
| 991 |
* to break it in a way that blackholed mail. Better to pass |
| 992 |
* the occasional duplicate than to do that... |
| 993 |
* |
| 994 |
* Matthias Andree: |
| 995 |
* The real fix however is to insist on Delivered-To: or similar |
| 996 |
* headers and require that one copy per recipient be dropped. |
| 997 |
* Everything else breaks sooner or later. |
| 998 |
*/ |
| 999 |
if (MULTIDROP(ctl) && msgblk.headers) |
| 1000 |
{ |
| 1001 |
MD5_CTX context; |
| 1002 |
|
| 1003 |
MD5Init(&context); |
| 1004 |
MD5Update(&context, (unsigned char *)msgblk.headers, strlen(msgblk.headers)); |
| 1005 |
MD5Final(ctl->digest, &context); |
| 1006 |
|
| 1007 |
if (!received_for && env_offs == -1 && !delivered_to) |
| 1008 |
{ |
| 1009 |
/* |
| 1010 |
* Hmmm...can MD5 ever yield all zeroes as a hash value? |
| 1011 |
* If so there is a one in 18-quadrillion chance this |
| 1012 |
* code will incorrectly nuke the first message. |
| 1013 |
*/ |
| 1014 |
if (!memcmp(ctl->lastdigest, ctl->digest, DIGESTLEN)) |
| 1015 |
return(PS_REFUSED); |
| 1016 |
} |
| 1017 |
memcpy(ctl->lastdigest, ctl->digest, DIGESTLEN); |
| 1018 |
} |
| 1019 |
|
| 1020 |
/* |
| 1021 |
* Hack time. If the first line of the message was blank, with no headers |
| 1022 |
* (this happens occasionally due to bad gatewaying software) cons up |
| 1023 |
* a set of fake headers. |
| 1024 |
* |
| 1025 |
* If you modify the fake header template below, be sure you don't |
| 1026 |
* make either From or To address @-less, otherwise the reply_hack |
| 1027 |
* logic will do bad things. |
| 1028 |
*/ |
| 1029 |
if (msgblk.headers == (char *)NULL) |
| 1030 |
{ |
| 1031 |
snprintf(buf, sizeof(buf), |
| 1032 |
"From: FETCHMAIL-DAEMON\r\n" |
| 1033 |
"To: %s@%s\r\n" |
| 1034 |
"Subject: Headerless mail from %s's mailbox on %s\r\n", |
| 1035 |
user, fetchmailhost, ctl->remotename, ctl->server.truename); |
| 1036 |
msgblk.headers = xstrdup(buf); |
| 1037 |
} |
| 1038 |
|
| 1039 |
/* |
| 1040 |
* We can now process message headers before reading the text. |
| 1041 |
* In fact we have to, as this will tell us where to forward to. |
| 1042 |
*/ |
| 1043 |
|
| 1044 |
/* Check for MIME headers indicating possible 8-bit data */ |
| 1045 |
ctl->mimemsg = MimeBodyType(msgblk.headers, ctl->mimedecode); |
| 1046 |
|
| 1047 |
#ifdef SDPS_ENABLE |
| 1048 |
if (ctl->server.sdps && sdps_envfrom) |
| 1049 |
{ |
| 1050 |
/* We have the real envelope return-path, stored out of band by |
| 1051 |
* SDPS - that's more accurate than any header is going to be. |
| 1052 |
*/ |
| 1053 |
strlcpy(msgblk.return_path, sdps_envfrom, sizeof(msgblk.return_path)); |
| 1054 |
free(sdps_envfrom); |
| 1055 |
} else |
| 1056 |
#endif /* SDPS_ENABLE */ |
| 1057 |
/* |
| 1058 |
* If there is a Return-Path address on the message, this was |
| 1059 |
* almost certainly the MAIL FROM address given the originating |
| 1060 |
* sendmail. This is the best thing to use for logging the |
| 1061 |
* message origin (it sets up the right behavior for bounces and |
| 1062 |
* mailing lists). Otherwise, fall down to the next available |
| 1063 |
* envelope address (which is the most probable real sender). |
| 1064 |
* *** The order is important! *** |
| 1065 |
* This is especially useful when receiving mailing list |
| 1066 |
* messages in multidrop mode. if a local address doesn't |
| 1067 |
* exist, the bounce message won't be returned blindly to the |
| 1068 |
* author or to the list itself but rather to the list manager |
| 1069 |
* (ex: specified by "Sender:") which is much less annoying. This |
| 1070 |
* is true for most mailing list packages. |
| 1071 |
*/ |
| 1072 |
if( !msgblk.return_path[0] ){ |
| 1073 |
char *ap = NULL; |
| 1074 |
if (resent_sender_offs >= 0 && (ap = nxtaddr(msgblk.headers + resent_sender_offs))); |
| 1075 |
else if (sender_offs >= 0 && (ap = nxtaddr(msgblk.headers + sender_offs))); |
| 1076 |
else if (resent_from_offs >= 0 && (ap = nxtaddr(msgblk.headers + resent_from_offs))); |
| 1077 |
else if (from_offs >= 0 && (ap = nxtaddr(msgblk.headers + from_offs))); |
| 1078 |
else if (reply_to_offs >= 0 && (ap = nxtaddr(msgblk.headers + reply_to_offs))); |
| 1079 |
else if (app_from_offs >= 0 && (ap = nxtaddr(msgblk.headers + app_from_offs))) {} |
| 1080 |
/* multi-line MAIL FROM addresses confuse SMTP terribly */ |
| 1081 |
if (ap && !strchr(ap, '\n')) { |
| 1082 |
strncpy(msgblk.return_path, ap, sizeof(msgblk.return_path)); |
| 1083 |
msgblk.return_path[sizeof(msgblk.return_path)-1] = '\0'; |
| 1084 |
} |
| 1085 |
} |
| 1086 |
|
| 1087 |
/* cons up a list of local recipients */ |
| 1088 |
msgblk.recipients = (struct idlist *)NULL; |
| 1089 |
accept_count = reject_count = 0; |
| 1090 |
/* is this a multidrop box? */ |
| 1091 |
if (MULTIDROP(ctl)) |
| 1092 |
{ |
| 1093 |
#ifdef SDPS_ENABLE |
| 1094 |
if (ctl->server.sdps && sdps_envto) |
| 1095 |
{ |
| 1096 |
/* We have the real envelope recipient, stored out of band by |
| 1097 |
* SDPS - that's more accurate than any header is going to be. |
| 1098 |
*/ |
| 1099 |
find_server_names(sdps_envto, ctl, &msgblk.recipients); |
| 1100 |
free(sdps_envto); |
| 1101 |
} else |
| 1102 |
#endif /* SDPS_ENABLE */ |
| 1103 |
if (env_offs > -1) { /* We have the actual envelope addressee */ |
| 1104 |
if (outlevel >= O_DEBUG) { |
| 1105 |
const char *tmps = msgblk.headers + env_offs; |
| 1106 |
size_t l = strcspn(tmps, "\r\n"); |
| 1107 |
report(stdout, GT_("Parsing envelope \"%s\" names \"%-.*s\"\n"), ctl->server.envelope, UCAST_TO_INT(l), tmps); |
| 1108 |
} |
| 1109 |
find_server_names(msgblk.headers + env_offs, ctl, &msgblk.recipients); |
| 1110 |
} |
| 1111 |
else if (delivered_to && ctl->server.envelope != STRING_DISABLED && |
| 1112 |
ctl->server.envelope && !strcasecmp(ctl->server.envelope, "Delivered-To")) |
| 1113 |
{ |
| 1114 |
if (outlevel >= O_DEBUG) { |
| 1115 |
const char *tmps = delivered_to + 2 + strlen(ctl->server.envelope); |
| 1116 |
size_t l = strcspn(tmps, "\r\n"); |
| 1117 |
report(stdout, GT_("Parsing envelope \"%s\" names \"%-.*s\"\n"), ctl->server.envelope, UCAST_TO_INT(l), tmps); |
| 1118 |
} |
| 1119 |
find_server_names(delivered_to, ctl, &msgblk.recipients); |
| 1120 |
xfree(delivered_to); |
| 1121 |
} else if (received_for) { |
| 1122 |
/* |
| 1123 |
* We have the Received for addressee. |
| 1124 |
* It has to be a mailserver address, or we |
| 1125 |
* wouldn't have got here. |
| 1126 |
* We use find_server_names() to let local |
| 1127 |
* hostnames go through. |
| 1128 |
*/ |
| 1129 |
if (outlevel >= O_DEBUG) { |
| 1130 |
const char *tmps = received_for + 2; |
| 1131 |
size_t l = strcspn(tmps, "\r\n"); |
| 1132 |
report(stdout, GT_("Parsing Received names \"%-.*s\"\n"), UCAST_TO_INT(l), tmps); |
| 1133 |
} |
| 1134 |
find_server_names(received_for, ctl, &msgblk.recipients); |
| 1135 |
} else { |
| 1136 |
/* |
| 1137 |
* We haven't extracted the envelope address. |
| 1138 |
* So check all the "Resent-To" header addresses if |
| 1139 |
* they exist. If and only if they don't, consider |
| 1140 |
* the "To" addresses. |
| 1141 |
*/ |
| 1142 |
register struct addrblk *nextptr; |
| 1143 |
if (outlevel >= O_DEBUG) |
| 1144 |
report(stdout, GT_("No envelope recipient found, resorting to header guessing.\n")); |
| 1145 |
if (resent_to_addrchain) { |
| 1146 |
/* delete the "To" chain and substitute it |
| 1147 |
* with the "Resent-To" list |
| 1148 |
*/ |
| 1149 |
while (to_addrchain) { |
| 1150 |
nextptr = to_addrchain->next; |
| 1151 |
free(to_addrchain); |
| 1152 |
to_addrchain = nextptr; |
| 1153 |
} |
| 1154 |
to_addrchain = resent_to_addrchain; |
| 1155 |
resent_to_addrchain = NULL; |
| 1156 |
} |
| 1157 |
/* now look for remaining adresses */ |
| 1158 |
while (to_addrchain) { |
| 1159 |
if (outlevel >= O_DEBUG) { |
| 1160 |
const char *tmps = msgblk.headers+to_addrchain->offset; |
| 1161 |
size_t l = strcspn(tmps, "\r\n"); |
| 1162 |
report(stdout, GT_("Guessing from header \"%-.*s\".\n"), UCAST_TO_INT(l), tmps); |
| 1163 |
} |
| 1164 |
|
| 1165 |
find_server_names(msgblk.headers+to_addrchain->offset, ctl, &msgblk.recipients); |
| 1166 |
nextptr = to_addrchain->next; |
| 1167 |
free(to_addrchain); |
| 1168 |
to_addrchain = nextptr; |
| 1169 |
} |
| 1170 |
} |
| 1171 |
if (!accept_count) |
| 1172 |
{ |
| 1173 |
no_local_matches = TRUE; |
| 1174 |
save_str(&msgblk.recipients, run.postmaster, XMIT_ACCEPT); |
| 1175 |
if (outlevel >= O_DEBUG) |
| 1176 |
report(stdout, |
| 1177 |
GT_("no local matches, forwarding to %s\n"), |
| 1178 |
run.postmaster); |
| 1179 |
} |
| 1180 |
} |
| 1181 |
else /* it's a single-drop box, use first localname */ |
| 1182 |
save_str(&msgblk.recipients, ctl->localnames->id, XMIT_ACCEPT); |
| 1183 |
|
| 1184 |
|
| 1185 |
/* |
| 1186 |
* Time to either address the message or decide we can't deliver it yet. |
| 1187 |
*/ |
| 1188 |
if (ctl->errcount > olderrs) /* there were DNS errors above */ |
| 1189 |
{ |
| 1190 |
if (outlevel >= O_DEBUG) |
| 1191 |
report(stdout, |
| 1192 |
GT_("forwarding and deletion suppressed due to DNS errors\n")); |
| 1193 |
return(PS_TRANSIENT); |
| 1194 |
} |
| 1195 |
else |
| 1196 |
{ |
| 1197 |
/* set up stuffline() so we can deliver the message body through it */ |
| 1198 |
if ((n = open_sink(ctl, &msgblk, |
| 1199 |
&good_addresses, &bad_addresses)) != PS_SUCCESS) |
| 1200 |
{ |
| 1201 |
return(n); |
| 1202 |
} |
| 1203 |
} |
| 1204 |
|
| 1205 |
n = 0; |
| 1206 |
/* |
| 1207 |
* Some server/sendmail combinations cause problems when our |
| 1208 |
* synthetic Received line is before the From header. Cope |
| 1209 |
* with this... |
| 1210 |
*/ |
| 1211 |
if ((rcv = strstr(msgblk.headers, "Received:")) == (char *)NULL) |
| 1212 |
rcv = msgblk.headers; |
| 1213 |
/* handle ">Received:" lines too */ |
| 1214 |
while (rcv > msgblk.headers && rcv[-1] != '\n') |
| 1215 |
rcv--; |
| 1216 |
if (rcv > msgblk.headers) |
| 1217 |
{ |
| 1218 |
char c = *rcv; |
| 1219 |
|
| 1220 |
*rcv = '\0'; |
| 1221 |
n = stuffline(ctl, msgblk.headers); |
| 1222 |
*rcv = c; |
| 1223 |
} |
| 1224 |
if (!run.invisible && n != -1) |
| 1225 |
{ |
| 1226 |
/* utter any per-message Received information we need here */ |
| 1227 |
if (ctl->server.trueaddr) { |
| 1228 |
char saddr[50]; |
| 1229 |
int e; |
| 1230 |
|
| 1231 |
e = getnameinfo(ctl->server.trueaddr, ctl->server.trueaddr_len, |
| 1232 |
saddr, sizeof(saddr), NULL, 0, |
| 1233 |
NI_NUMERICHOST); |
| 1234 |
if (e) |
| 1235 |
snprintf(saddr, sizeof(saddr), "(%-.*s)", (int)(sizeof(saddr) - 3), gai_strerror(e)); |
| 1236 |
snprintf(buf, sizeof(buf), |
| 1237 |
"Received: from %s [%s]\r\n", |
| 1238 |
ctl->server.truename, saddr); |
| 1239 |
} else { |
| 1240 |
snprintf(buf, sizeof(buf), |
| 1241 |
"Received: from %s\r\n", ctl->server.truename); |
| 1242 |
} |
| 1243 |
n = stuffline(ctl, buf); |
| 1244 |
if (n != -1) |
| 1245 |
{ |
| 1246 |
/* |
| 1247 |
* We SHOULD (RFC-2821 sec. 4.4/p. 53) make sure to only use |
| 1248 |
* IANA registered protocol names here. |
| 1249 |
*/ |
| 1250 |
snprintf(buf, sizeof(buf), |
| 1251 |
"\tby %s with %s (fetchmail-%s", |
| 1252 |
fetchmailhost, |
| 1253 |
protocol->name, |
| 1254 |
VERSION); |
| 1255 |
if (ctl->server.tracepolls) |
| 1256 |
{ |
| 1257 |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
| 1258 |
" polling %s account %s", |
| 1259 |
ctl->server.pollname, |
| 1260 |
ctl->remotename); |
| 1261 |
if (ctl->folder) |
| 1262 |
snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
| 1263 |
" folder %s", |
| 1264 |
ctl->folder); |
| 1265 |
} |
| 1266 |
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), ")\r\n"); |
| 1267 |
n = stuffline(ctl, buf); |
| 1268 |
if (n != -1) |
| 1269 |
{ |
| 1270 |
buf[0] = '\t'; |
| 1271 |
if (good_addresses == 0) |
| 1272 |
{ |
| 1273 |
snprintf(buf+1, sizeof(buf)-1, "for <%s> (by default); ", |
| 1274 |
rcpt_address (ctl, run.postmaster, 0)); |
| 1275 |
} |
| 1276 |
else if (good_addresses == 1) |
| 1277 |
{ |
| 1278 |
for (idp = msgblk.recipients; idp; idp = idp->next) |
| 1279 |
if (idp->val.status.mark == XMIT_ACCEPT) |
| 1280 |
break; /* only report first address */ |
| 1281 |
if (idp) |
| 1282 |
snprintf(buf+1, sizeof(buf)-1, |
| 1283 |
"for <%s>", rcpt_address (ctl, idp->id, 1)); |
| 1284 |
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf)-1, |
| 1285 |
" (%s); ", |
| 1286 |
MULTIDROP(ctl) ? "multi-drop" : "single-drop"); |
| 1287 |
} |
| 1288 |
else |
| 1289 |
buf[1] = '\0'; |
| 1290 |
|
| 1291 |
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s\r\n", |
| 1292 |
rfc822timestamp()); |
| 1293 |
n = stuffline(ctl, buf); |
| 1294 |
} |
| 1295 |
} |
| 1296 |
} |
| 1297 |
|
| 1298 |
if (n != -1) |
| 1299 |
n = stuffline(ctl, rcv); /* ship out rest of msgblk.headers */ |
| 1300 |
|
| 1301 |
if (n == -1) |
| 1302 |
{ |
| 1303 |
report(stdout, GT_("writing RFC822 msgblk.headers\n")); |
| 1304 |
release_sink(ctl); |
| 1305 |
return(PS_IOERR); |
| 1306 |
} |
| 1307 |
|
| 1308 |
if (want_progress()) |
| 1309 |
fputc('#', stdout); |
| 1310 |
|
| 1311 |
/* write error notifications */ |
| 1312 |
if (no_local_matches || has_nuls || bad_addresses) |
| 1313 |
{ |
| 1314 |
int errlen = 0; |
| 1315 |
char errhd[USERNAMELEN + POPBUFSIZE], *errmsg; |
| 1316 |
|
| 1317 |
errmsg = errhd; |
| 1318 |
strlcpy(errhd, "X-Fetchmail-Warning: ", sizeof(errhd)); |
| 1319 |
if (no_local_matches) |
| 1320 |
{ |
| 1321 |
if (reject_count != 1) |
| 1322 |
strlcat(errhd, GT_("no recipient addresses matched declared local names"), sizeof(errhd)); |
| 1323 |
else |
| 1324 |
{ |
| 1325 |
for (idp = msgblk.recipients; idp; idp = idp->next) |
| 1326 |
if (idp->val.status.mark == XMIT_REJECT) |
| 1327 |
break; |
| 1328 |
snprintf(errhd+strlen(errhd), sizeof(errhd)-strlen(errhd), |
| 1329 |
GT_("recipient address %s didn't match any local name"), idp->id); |
| 1330 |
} |
| 1331 |
} |
| 1332 |
|
| 1333 |
if (has_nuls) |
| 1334 |
{ |
| 1335 |
if (errhd[sizeof("X-Fetchmail-Warning: ")]) |
| 1336 |
snprintf(errhd+strlen(errhd), sizeof(errhd)-strlen(errhd), "; "); |
| 1337 |
snprintf(errhd+strlen(errhd), sizeof(errhd)-strlen(errhd), |
| 1338 |
GT_("message has embedded NULs")); |
| 1339 |
} |
| 1340 |
|
| 1341 |
if (bad_addresses) |
| 1342 |
{ |
| 1343 |
if (errhd[sizeof("X-Fetchmail-Warning: ")]) |
| 1344 |
snprintf(errhd+strlen(errhd), sizeof(errhd)-strlen(errhd), "; "); |
| 1345 |
snprintf(errhd+strlen(errhd), sizeof(errhd)-strlen(errhd), |
| 1346 |
GT_("SMTP listener rejected local recipient addresses: ")); |
| 1347 |
errlen = strlen(errhd); |
| 1348 |
for (idp = msgblk.recipients; idp; idp = idp->next) |
| 1349 |
if (idp->val.status.mark == XMIT_RCPTBAD) |
| 1350 |
errlen += strlen(idp->id) + 2; |
| 1351 |
|
| 1352 |
errmsg = (char *)xmalloc(errlen + 3); |
| 1353 |
strcpy(errmsg, errhd); |
| 1354 |
for (idp = msgblk.recipients; idp; idp = idp->next) |
| 1355 |
if (idp->val.status.mark == XMIT_RCPTBAD) |
| 1356 |
{ |
| 1357 |
strcat(errmsg, idp->id); |
| 1358 |
if (idp->next) |
| 1359 |
strcat(errmsg, ", "); |
| 1360 |
} |
| 1361 |
|
| 1362 |
} |
| 1363 |
|
| 1364 |
strcat(errmsg, "\r\n"); |
| 1365 |
|
| 1366 |
/* ship out the error line */ |
| 1367 |
stuffline(ctl, errmsg); |
| 1368 |
|
| 1369 |
if (errmsg != errhd) |
| 1370 |
free(errmsg); |
| 1371 |
} |
| 1372 |
|
| 1373 |
/* issue the delimiter line */ |
| 1374 |
cp = buf; |
| 1375 |
*cp++ = '\r'; |
| 1376 |
*cp++ = '\n'; |
| 1377 |
*cp = '\0'; |
| 1378 |
n = stuffline(ctl, buf); |
| 1379 |
|
| 1380 |
if ((size_t)n == strlen(buf)) |
| 1381 |
return PS_SUCCESS; |
| 1382 |
else |
| 1383 |
return PS_SOCKET; |
| 1384 |
} |
| 1385 |
|
| 1386 |
int readbody(int sock, struct query *ctl, flag forward, int len) |
| 1387 |
/** read and dispose of a message body presented on \a sock */ |
| 1388 |
/** \param ctl query control record */ |
| 1389 |
/** \param sock to which the server is connected */ |
| 1390 |
/** \param forward TRUE to forward */ |
| 1391 |
/** \param len length of message */ |
| 1392 |
{ |
| 1393 |
int linelen; |
| 1394 |
char buf[MSGBUFSIZE+4]; |
| 1395 |
char *inbufp = buf; |
| 1396 |
flag issoftline = FALSE; |
| 1397 |
|
| 1398 |
/* |
| 1399 |
* Pass through the text lines in the body. |
| 1400 |
* |
| 1401 |
* Yes, this wants to be ||, not &&. The problem is that in the most |
| 1402 |
* important delimited protocol, POP3, the length is not reliable. |
| 1403 |
* As usual, the problem is Microsoft brain damage; see FAQ item S2. |
| 1404 |
* So, for delimited protocols we need to ignore the length here and |
| 1405 |
* instead drop out of the loop with a break statement when we see |
| 1406 |
* the message delimiter. |
| 1407 |
*/ |
| 1408 |
while (protocol->delimited || len > 0) |
| 1409 |
{ |
| 1410 |
set_timeout(mytimeout); |
| 1411 |
/* XXX FIXME: for undelimited protocols that ship the size, such |
| 1412 |
* as IMAP, we might want to use the count of remaining characters |
| 1413 |
* instead of the buffer size -- not for fetchmail 6.3.X though */ |
| 1414 |
if ((linelen = SockRead(sock, inbufp, sizeof(buf)-4-(inbufp-buf)))==-1) |
| 1415 |
{ |
| 1416 |
set_timeout(0); |
| 1417 |
release_sink(ctl); |
| 1418 |
return(PS_SOCKET); |
| 1419 |
} |
| 1420 |
set_timeout(0); |
| 1421 |
|
| 1422 |
/* write the message size dots */ |
| 1423 |
if (linelen > 0) |
| 1424 |
{ |
| 1425 |
print_ticker(&sizeticker, linelen); |
| 1426 |
} |
| 1427 |
|
| 1428 |
/* Mike Jones, Manchester University, 2006: |
| 1429 |
* "To fix IMAP MIME Messages in which fetchmail adds the remainder of |
| 1430 |
* the IMAP packet including the ')' character (part of the IMAP) |
| 1431 |
* Protocol causing the addition of an extra MIME boundary locally." |
| 1432 |
* |
| 1433 |
* However, we shouldn't do this for delimited protocols: |
| 1434 |
* many POP3 servers (Microsoft, qmail) goof up message sizes |
| 1435 |
* so we might end truncating messages prematurely. |
| 1436 |
*/ |
| 1437 |
if (!protocol->delimited && linelen > len) { |
| 1438 |
/* FIXME: HACK ALERT! This \r\n is only here to make sure the |
| 1439 |
* \n\0 hunt works later on. The \n generated here was not |
| 1440 |
* part of the original message! |
| 1441 |
* The real fix will be to use buffer + length strings, |
| 1442 |
* rather than 0-terminated C strings. */ |
| 1443 |
inbufp[len++] = '\r'; |
| 1444 |
inbufp[len++] = '\n'; |
| 1445 |
inbufp[len] = '\0'; |
| 1446 |
linelen = len; |
| 1447 |
} |
| 1448 |
|
| 1449 |
len -= linelen; |
| 1450 |
|
| 1451 |
/* check for end of message */ |
| 1452 |
if (protocol->delimited && *inbufp == '.') |
| 1453 |
{ |
| 1454 |
if (EMPTYLINE(inbufp+1)) |
| 1455 |
break; |
| 1456 |
else |
| 1457 |
msgblk.msglen--; /* subtract the size of the dot escape */ |
| 1458 |
} |
| 1459 |
|
| 1460 |
msgblk.msglen += linelen; |
| 1461 |
|
| 1462 |
if (ctl->mimedecode && (ctl->mimemsg & MSG_NEEDS_DECODE)) { |
| 1463 |
issoftline = UnMimeBodyline(&inbufp, protocol->delimited, issoftline); |
| 1464 |
if (issoftline && (sizeof(buf)-1-(inbufp-buf) < 200)) |
| 1465 |
{ |
| 1466 |
/* |
| 1467 |
* Soft linebreak, but less than 200 bytes left in |
| 1468 |
* input buffer. Rather than doing a buffer overrun, |
| 1469 |
* ignore the soft linebreak, NL-terminate data and |
| 1470 |
* deliver what we have now. |
| 1471 |
* (Who writes lines longer than 2K anyway?) |
| 1472 |
*/ |
| 1473 |
*inbufp = '\n'; *(inbufp+1) = '\0'; |
| 1474 |
issoftline = 0; |
| 1475 |
} |
| 1476 |
} |
| 1477 |
|
| 1478 |
/* ship out the text line */ |
| 1479 |
if (forward && (!issoftline)) |
| 1480 |
{ |
| 1481 |
int n; |
| 1482 |
inbufp = buf; |
| 1483 |
|
| 1484 |
/* guard against very long lines */ |
| 1485 |
buf[MSGBUFSIZE+1] = '\r'; |
| 1486 |
buf[MSGBUFSIZE+2] = '\n'; |
| 1487 |
buf[MSGBUFSIZE+3] = '\0'; |
| 1488 |
|
| 1489 |
n = stuffline(ctl, buf); |
| 1490 |
|
| 1491 |
if (n < 0) |
| 1492 |
{ |
| 1493 |
report(stdout, GT_("error writing message text\n")); |
| 1494 |
release_sink(ctl); |
| 1495 |
return(PS_IOERR); |
| 1496 |
} |
| 1497 |
else if (want_progress()) |
| 1498 |
{ |
| 1499 |
fputc('*', stdout); |
| 1500 |
fflush(stdout); |
| 1501 |
} |
| 1502 |
} |
| 1503 |
} |
| 1504 |
|
| 1505 |
return(PS_SUCCESS); |
| 1506 |
} |
| 1507 |
|
| 1508 |
void init_transact(const struct method *proto) |
| 1509 |
/** initialize state for the send and receive functions */ |
| 1510 |
{ |
| 1511 |
suppress_tags = FALSE; |
| 1512 |
tagnum = 0; |
| 1513 |
tag[0] = '\0'; /* nuke any tag hanging out from previous query */ |
| 1514 |
protocol = proto; |
| 1515 |
shroud[0] = '\0'; |
| 1516 |
} |
| 1517 |
|
| 1518 |
/** shroud a password in the given buffer */ |
| 1519 |
static void enshroud(char *buf) |
| 1520 |
{ |
| 1521 |
char *cp; |
| 1522 |
|
| 1523 |
if (shroud[0] && (cp = strstr(buf, shroud))) |
| 1524 |
{ |
| 1525 |
char *sp; |
| 1526 |
|
| 1527 |
sp = cp + strlen(shroud); |
| 1528 |
*cp++ = '*'; |
| 1529 |
while (*sp) |
| 1530 |
*cp++ = *sp++; |
| 1531 |
*cp = '\0'; |
| 1532 |
} |
| 1533 |
} |
| 1534 |
|
| 1535 |
#if defined(HAVE_STDARG_H) |
| 1536 |
/** assemble command in printf(3) style and send to the server */ |
| 1537 |
void gen_send(int sock, const char *fmt, ... ) |
| 1538 |
#else |
| 1539 |
void gen_send(sock, fmt, va_alist) |
| 1540 |
int sock; /** socket to which server is connected */ |
| 1541 |
const char *fmt; /** printf-style format */ |
| 1542 |
va_dcl |
| 1543 |
#endif |
| 1544 |
{ |
| 1545 |
char buf [MSGBUFSIZE+1]; |
| 1546 |
va_list ap; |
| 1547 |
|
| 1548 |
if (protocol->tagged && !suppress_tags) |
| 1549 |
snprintf(buf, sizeof(buf) - 2, "%s ", GENSYM); |
| 1550 |
else |
| 1551 |
buf[0] = '\0'; |
| 1552 |
|
| 1553 |
#if defined(HAVE_STDARG_H) |
| 1554 |
va_start(ap, fmt); |
| 1555 |
#else |
| 1556 |
va_start(ap); |
| 1557 |
#endif |
| 1558 |
vsnprintf(buf + strlen(buf), sizeof(buf)-2-strlen(buf), fmt, ap); |
| 1559 |
va_end(ap); |
| 1560 |
|
| 1561 |
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\r\n"); |
| 1562 |
SockWrite(sock, buf, strlen(buf)); |
| 1563 |
|
| 1564 |
if (outlevel >= O_MONITOR) |
| 1565 |
{ |
| 1566 |
enshroud(buf); |
| 1567 |
buf[strlen(buf)-2] = '\0'; |
| 1568 |
report(stdout, "%s> %s\n", protocol->name, buf); |
| 1569 |
} |
| 1570 |
} |
| 1571 |
|
| 1572 |
/** get one line of input from the server */ |
| 1573 |
int gen_recv(int sock /** socket to which server is connected */, |
| 1574 |
char *buf /** buffer to receive input */, |
| 1575 |
int size /** length of buffer */) |
| 1576 |
{ |
| 1577 |
size_t n; |
| 1578 |
int oldphase = phase; /* we don't have to be re-entrant */ |
| 1579 |
|
| 1580 |
phase = SERVER_WAIT; |
| 1581 |
set_timeout(mytimeout); |
| 1582 |
if (SockRead(sock, buf, size) == -1) |
| 1583 |
{ |
| 1584 |
set_timeout(0); |
| 1585 |
phase = oldphase; |
| 1586 |
if(is_idletimeout()) |
| 1587 |
{ |
| 1588 |
resetidletimeout(); |
| 1589 |
return(PS_IDLETIMEOUT); |
| 1590 |
} |
| 1591 |
else |
| 1592 |
return(PS_SOCKET); |
| 1593 |
} |
| 1594 |
else |
| 1595 |
{ |
| 1596 |
set_timeout(0); |
| 1597 |
n = strlen(buf); |
| 1598 |
if (n > 0 && buf[n-1] == '\n') |
| 1599 |
buf[--n] = '\0'; |
| 1600 |
if (n > 0 && buf[n-1] == '\r') |
| 1601 |
buf[--n] = '\0'; |
| 1602 |
if (outlevel >= O_MONITOR) |
| 1603 |
report(stdout, "%s< %s\n", protocol->name, buf); |
| 1604 |
phase = oldphase; |
| 1605 |
return(PS_SUCCESS); |
| 1606 |
} |
| 1607 |
} |
| 1608 |
|
| 1609 |
/** \addtogroup gen_recv_split |
| 1610 |
* @{ |
| 1611 |
* gen_recv_split() splits the response from a server which is too |
| 1612 |
* long to fit into the buffer into multiple lines. If the prefix is |
| 1613 |
* set as "MY FEATURES" and the response from the server is too long |
| 1614 |
* to fit in the buffer, as in: |
| 1615 |
* |
| 1616 |
* "MY FEATURES ABC DEF GHI JKLMNOPQRS TU VWX YZ" |
| 1617 |
* |
| 1618 |
* Repeated calls to gen_recv_split() may return: |
| 1619 |
* |
| 1620 |
* "MY FEATURES ABC DEF GHI" |
| 1621 |
* "MY FEATURES JKLMNOPQRS" |
| 1622 |
* "MY FEATURES TU VWX YZ" |
| 1623 |
* |
| 1624 |
* A response not beginning with the prefix "MY FEATURES" will not be |
| 1625 |
* split. |
| 1626 |
* |
| 1627 |
* To use: |
| 1628 |
* - Declare a variable of type struct RecvSplit |
| 1629 |
* - Call gen_recv_split_init() once |
| 1630 |
* - Call gen_recv_split() in a loop, preferably with the same buffer |
| 1631 |
* size as the "buf" array in struct RecvSplit |
| 1632 |
*/ |
| 1633 |
|
| 1634 |
static void overrun(const char *f, size_t l) __attribute__((noreturn)); |
| 1635 |
|
| 1636 |
/** Internal error report function. If this happens, the calling site |
| 1637 |
* needs to be adjusted to set a shorter prefix, or the prefix capacity |
| 1638 |
* needs to be raised in struct RecvSplit. */ |
| 1639 |
static void overrun(const char *f, size_t l) |
| 1640 |
{ |
| 1641 |
report(stderr, GT_("Buffer too small. This is a bug in the caller of %s:%lu.\n"), f, (unsigned long)l); |
| 1642 |
abort(); |
| 1643 |
} |
| 1644 |
|
| 1645 |
/** Initialize \a rs for later use by gen_recv_split. */ |
| 1646 |
void gen_recv_split_init (const char *prefix /** prefix to match/repeat */, |
| 1647 |
struct RecvSplit *rs /** structure to be initialized */) |
| 1648 |
{ |
| 1649 |
if (strlcpy(rs->prefix, prefix, sizeof(rs->prefix)) > sizeof(rs->prefix)) |
| 1650 |
overrun(__FILE__, __LINE__); |
| 1651 |
rs->cached = 0; |
| 1652 |
rs->buf[0] = '\0'; |
| 1653 |
} |
| 1654 |
|
| 1655 |
/** Function to split replies at blanks, and duplicate prefix. |
| 1656 |
* gen_recv_split_init() must be called before this can be used. */ |
| 1657 |
int gen_recv_split(int sock /** socket to which server is connected */, |
| 1658 |
char *buf /** buffer to receive input */, |
| 1659 |
int size /** length of buffer, must be the same for all calls */, |
| 1660 |
struct RecvSplit *rs /** cached information across calls */) |
| 1661 |
{ |
| 1662 |
size_t n = 0; |
| 1663 |
int foundnewline = 0; |
| 1664 |
char *p; |
| 1665 |
int oldphase = phase; /* we don't have to be re-entrant */ |
| 1666 |
|
| 1667 |
assert(size > 0); |
| 1668 |
|
| 1669 |
/* if this is not our first call, prepare the buffer */ |
| 1670 |
if (rs->cached) |
| 1671 |
{ |
| 1672 |
/* |
| 1673 |
* if this condition is not met, we lose data |
| 1674 |
* because the cached data does not fit into the buffer. |
| 1675 |
* this cannot happen if size is the same throughout all calls. |
| 1676 |
*/ |
| 1677 |
assert(strlen(rs->prefix) + strlen(rs->buf) + 1 <= (size_t)size); |
| 1678 |
|
| 1679 |
if ((strlcpy(buf, rs->prefix, size) >= (size_t)size) |
| 1680 |
|| (strlcat(buf, rs->buf, size) >= (size_t)size)) { |
| 1681 |
overrun(__FILE__, __LINE__); |
| 1682 |
} |
| 1683 |
|
| 1684 |
n = strlen(buf); |
| 1685 |
/* clear the cache for the next call */ |
| 1686 |
rs->cached = 0; |
| 1687 |
rs->buf[0] = '\0'; |
| 1688 |
} |
| 1689 |
|
| 1690 |
if ((size_t)size > n) { |
| 1691 |
int rr; |
| 1692 |
|
| 1693 |
phase = SERVER_WAIT; |
| 1694 |
set_timeout(mytimeout); |
| 1695 |
rr = SockRead(sock, buf + n, size - n); |
| 1696 |
set_timeout(0); |
| 1697 |
phase = oldphase; |
| 1698 |
if (rr == -1) |
| 1699 |
return PS_SOCKET; |
| 1700 |
} |
| 1701 |
|
| 1702 |
n = strlen(buf); |
| 1703 |
if (n > 0 && buf[n-1] == '\n') |
| 1704 |
{ |
| 1705 |
buf[--n] = '\0'; |
| 1706 |
foundnewline = 1; |
| 1707 |
} |
| 1708 |
if (n > 0 && buf[n-1] == '\r') |
| 1709 |
buf[--n] = '\0'; |
| 1710 |
|
| 1711 |
if (foundnewline /* we have found a complete line */ |
| 1712 |
|| strncasecmp(buf, rs->prefix, strlen(rs->prefix)) /* mismatch in prefix */ |
| 1713 |
|| !(p = strrchr(buf, ' ')) /* no space found in response */ |
| 1714 |
|| p < buf + strlen(rs->prefix)) /* space is at the wrong location */ |
| 1715 |
{ |
| 1716 |
if (outlevel >= O_MONITOR) |
| 1717 |
report(stdout, "%s< %s\n", protocol->name, buf); |
| 1718 |
return(PS_SUCCESS); |
| 1719 |
} |
| 1720 |
|
| 1721 |
/* we are ready to cache some information now. */ |
| 1722 |
rs->cached = 1; |
| 1723 |
if (strlcpy(rs->buf, p, sizeof(rs->buf)) >= sizeof(rs->buf)) { |
| 1724 |
overrun(__FILE__, __LINE__); |
| 1725 |
} |
| 1726 |
*p = '\0'; /* chop off what we've cached */ |
| 1727 |
if (outlevel >= O_MONITOR) |
| 1728 |
report(stdout, "%s< %s\n", protocol->name, buf); |
| 1729 |
if (outlevel >= O_DEBUG) |
| 1730 |
report(stdout, "%s< %s%s...\n", protocol->name, rs->prefix, rs->buf); |
| 1731 |
return(PS_SUCCESS); |
| 1732 |
} |
| 1733 |
/** @} */ |
| 1734 |
|
| 1735 |
#if defined(HAVE_STDARG_H) |
| 1736 |
int gen_transact(int sock, const char *fmt, ... ) |
| 1737 |
#else |
| 1738 |
int gen_transact(int sock, fmt, va_alist) |
| 1739 |
int sock; /** socket to which server is connected */ |
| 1740 |
const char *fmt; /** printf-style format */ |
| 1741 |
va_dcl |
| 1742 |
#endif |
| 1743 |
/** assemble command in printf(3) style, send to server, fetch a response */ |
| 1744 |
{ |
| 1745 |
int ok; |
| 1746 |
char buf [MSGBUFSIZE+1]; |
| 1747 |
va_list ap; |
| 1748 |
int oldphase = phase; /* we don't have to be re-entrant */ |
| 1749 |
|
| 1750 |
phase = SERVER_WAIT; |
| 1751 |
|
| 1752 |
if (protocol->tagged && !suppress_tags) |
| 1753 |
snprintf(buf, sizeof(buf) - 2, "%s ", GENSYM); |
| 1754 |
else |
| 1755 |
buf[0] = '\0'; |
| 1756 |
|
| 1757 |
#if defined(HAVE_STDARG_H) |
| 1758 |
va_start(ap, fmt) ; |
| 1759 |
#else |
| 1760 |
va_start(ap); |
| 1761 |
#endif |
| 1762 |
vsnprintf(buf + strlen(buf), sizeof(buf)-2-strlen(buf), fmt, ap); |
| 1763 |
va_end(ap); |
| 1764 |
|
| 1765 |
snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "\r\n"); |
| 1766 |
ok = SockWrite(sock, buf, strlen(buf)); |
| 1767 |
if (ok == -1 || (size_t)ok != strlen(buf)) { |
| 1768 |
/* short write, bail out */ |
| 1769 |
return PS_SOCKET; |
| 1770 |
} |
| 1771 |
|
| 1772 |
if (outlevel >= O_MONITOR) |
| 1773 |
{ |
| 1774 |
enshroud(buf); |
| 1775 |
buf[strlen(buf)-2] = '\0'; |
| 1776 |
report(stdout, "%s> %s\n", protocol->name, buf); |
| 1777 |
} |
| 1778 |
|
| 1779 |
/* we presume this does its own response echoing */ |
| 1780 |
ok = (protocol->parse_response)(sock, buf); |
| 1781 |
|
| 1782 |
phase = oldphase; |
| 1783 |
return(ok); |
| 1784 |
} |
| 1785 |
|
| 1786 |
/* transact.c ends here */ |