Ejemplo n.º 1
0
void
httpHeaderPutTime(HttpHeader * hdr, http_hdr_type id, time_t htime)
{
    assert_eid(id);
    assert(Headers[id].type == ftDate_1123);	/* must be of an appropriate type */
    assert(htime >= 0);
    httpHeaderAddEntry(hdr, httpHeaderEntryCreate(id, NULL, mkrfc1123(htime)));
}
Ejemplo n.º 2
0
static void
print_trailer(void)
{
    printf("<HR>\n");
    printf("<ADDRESS>\n");
    printf("Generated %s, by %s/%s@%s\n",
	mkrfc1123(now), progname, SQUID_VERSION, getfullhostname());
    printf("</ADDRESS></BODY></HTML>\n");
}
static void
gopherHTMLFooter(StoreEntry * e)
{
    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
    storeAppendPrintf(e, "<ADDRESS>\n");
    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
	mkrfc1123(squid_curtime),
	getMyHostname(),
	visible_appname_string);
    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
}
Ejemplo n.º 4
0
/* create MIME Header for Gopher Data */
static void
gopherMimeCreate(GopherStateData * gopherState)
{
    MemBuf mb;

    memBufDefInit(&mb);

    memBufPrintf(&mb,
	"HTTP/1.0 200 OK Gatewaying\r\n"
	"Server: Squid/%s\r\n"
	"Date: %s\r\n"
	"MIME-version: 1.0\r\n",
	version_string, mkrfc1123(squid_curtime));

    switch (gopherState->type_id) {

    case GOPHER_DIRECTORY:
    case GOPHER_INDEX:
    case GOPHER_HTML:
    case GOPHER_WWW:
    case GOPHER_CSO:
	memBufPrintf(&mb, "Content-Type: text/html\r\n");
	break;
    case GOPHER_GIF:
    case GOPHER_IMAGE:
    case GOPHER_PLUS_IMAGE:
	memBufPrintf(&mb, "Content-Type: image/gif\r\n");
	break;
    case GOPHER_SOUND:
    case GOPHER_PLUS_SOUND:
	memBufPrintf(&mb, "Content-Type: audio/basic\r\n");
	break;
    case GOPHER_PLUS_MOVIE:
	memBufPrintf(&mb, "Content-Type: video/mpeg\r\n");
	break;
    case GOPHER_MACBINHEX:
    case GOPHER_DOSBIN:
    case GOPHER_UUENCODED:
    case GOPHER_BIN:
	/* Rightnow We have no idea what it is. */
	gopher_mime_content(&mb, gopherState->request, def_gopher_bin);
	break;
    case GOPHER_FILE:
    default:
	gopher_mime_content(&mb, gopherState->request, def_gopher_text);
	break;
    }
    memBufPrintf(&mb, "\r\n");
    EBIT_CLR(gopherState->entry->flags, ENTRY_FWD_HDR_WAIT);
    storeAppend(gopherState->entry, mb.buf, mb.size);
    memBufClean(&mb);
}
Ejemplo n.º 5
0
int
main()
{
    char *x;
    time_t t, pt;

    t = time(NULL);
    x = mkrfc1123(t);
    printf("HTTP Time: %s\n", x);

    pt = parse_rfc1123(x);
    printf("Parsed: %d vs. %d\n", pt, t);
}
Ejemplo n.º 6
0
static const char *
errorConvert(char token, ErrorState * err)
{
    request_t *r = err->request;
    static MemBuf mb = MemBufNULL;
    const char *p = NULL;	/* takes priority over mb if set */
    int do_quote = 1;

    memBufReset(&mb);
    switch (token) {
    case 'a':
	if (r && r->auth_user_request)
	    p = authenticateUserRequestUsername(r->auth_user_request);
	if (!p)
	    p = "-";
	break;
    case 'B':
	p = r ? ftpUrlWith2f(r) : "[no URL]";
	break;
    case 'c':
	p = errorPageName(err->type);
	break;
    case 'e':
	memBufPrintf(&mb, "%d", err->xerrno);
	break;
    case 'E':
	if (err->xerrno)
	    memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
	else
	    memBufPrintf(&mb, "[No Error]");
	break;
    case 'f':
	/* FTP REQUEST LINE */
	if (err->ftp.request)
	    p = err->ftp.request;
	else
	    p = "nothing";
	break;
    case 'F':
	/* FTP REPLY LINE */
	if (err->ftp.request)
	    p = err->ftp.reply;
	else
	    p = "nothing";
	break;
    case 'g':
	/* FTP SERVER MESSAGE */
	wordlistCat(err->ftp.server_msg, &mb);
	break;
    case 'h':
	memBufPrintf(&mb, "%s", getMyHostname());
	break;
    case 'H':
	if (r) {
	    if (r->hier.host)
		p = r->hier.host;
	    else
		p = r->host;
	} else
	    p = "[unknown host]";
	break;
    case 'i':
	memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
	break;
    case 'I':
	if (r && r->hier.host) {
	    memBufPrintf(&mb, "%s", r->hier.host);
	} else
	    p = "[unknown]";
	break;
    case 'L':
	if (Config.errHtmlText) {
	    memBufPrintf(&mb, "%s", Config.errHtmlText);
	    do_quote = 0;
	} else
	    p = "[not available]";
	break;
    case 'm':
	p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
	break;
    case 'M':
	p = r ? RequestMethods[r->method].str : "[unknown method]";
	break;
    case 'o':
	p = external_acl_message;
	if (!p)
	    p = "[not available]";
	break;
    case 'p':
	if (r) {
	    memBufPrintf(&mb, "%d", (int) r->port);
	} else {
	    p = "[unknown port]";
	}
	break;
    case 'P':
	p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
	break;
    case 'R':
	if (NULL != r) {
	    Packer p;
	    memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
		RequestMethods[r->method].str,
		strLen(r->urlpath) ? strBuf(r->urlpath) : "/",
		r->http_ver.major, r->http_ver.minor);
	    packerToMemInit(&p, &mb);
	    httpHeaderPackInto(&r->header, &p);
	    packerClean(&p);
	} else if (err->request_hdrs) {
	    p = err->request_hdrs;
	} else {
	    p = "[no request]";
	}
	break;
    case 's':
	p = visible_appname_string;
	break;
    case 'S':
	/* signature may contain %-escapes, recursion */
	if (err->page_id != ERR_SQUID_SIGNATURE) {
	    const int saved_id = err->page_id;
	    MemBuf sign_mb;
	    err->page_id = ERR_SQUID_SIGNATURE;
	    sign_mb = errorBuildContent(err);
	    memBufPrintf(&mb, "%s", sign_mb.buf);
	    memBufClean(&sign_mb);
	    err->page_id = saved_id;
	    do_quote = 0;
	} else {
	    /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
	    p = "[%S]";
	}
	break;
    case 't':
	memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
	break;
    case 'T':
	memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
	break;
    case 'U':
	p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
	break;
    case 'u':
	p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]";
	break;
    case 'w':
	if (Config.adminEmail)
	    memBufPrintf(&mb, "%s", Config.adminEmail);
	else
	    p = "[unknown]";
	break;
    case 'z':
	if (err->dnsserver_msg)
	    p = err->dnsserver_msg;
	else
	    p = "[unknown]";
	break;
    case '%':
	p = "%";
	break;
    default:
	memBufPrintf(&mb, "%%%c", token);
	do_quote = 0;
	break;
    }
    if (!p)
	p = mb.buf;		/* do not use mb after this assignment! */
    assert(p);
    debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
    if (do_quote)
	p = html_quote(p);
    return p;
}
int
main(int argc, char *argv[])
{
    int conn, c, len, bytesWritten;
    int port, to_stdout, reload;
    int ping, pcount;
    int keep_alive = 0;
    int opt_noaccept = 0;
    int opt_verbose = 0;
    const char *hostname, *localhost;
    char url[BUFSIZ], msg[BUFSIZ], buf[BUFSIZ];
    char extra_hdrs[BUFSIZ];
    const char *method = "GET";
    extern char *optarg;
    time_t ims = 0;
    int max_forwards = -1;
    struct timeval tv1, tv2;
    int i = 0, loops;
    long ping_int;
    long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0;
    char *proxy_user = NULL;
    char *proxy_password = NULL;
    char *www_user = NULL;
    char *www_password = NULL;

    /* set the defaults */
    hostname = "localhost";
    localhost = NULL;
    extra_hdrs[0] = '\0';
    port = CACHE_HTTP_PORT;
    to_stdout = 1;
    reload = 0;
    ping = 0;
    pcount = 0;
    ping_int = 1 * 1000;

    if (argc < 2) {
	usage(argv[0]);		/* need URL */
    } else if (argc >= 2) {
	strncpy(url, argv[argc - 1], BUFSIZ);
	url[BUFSIZ - 1] = '\0';
	if (url[0] == '-')
	    usage(argv[0]);
	while ((c = getopt(argc, argv, "ah:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:?")) != -1)
	    switch (c) {
	    case 'a':
		opt_noaccept = 1;
		break;
	    case 'h':		/* remote host */
		if (optarg != NULL)
		    hostname = optarg;
		break;
	    case 'l':		/* local host */
		if (optarg != NULL)
		    localhost = optarg;
		break;
	    case 's':		/* silent */
		to_stdout = 0;
		break;
	    case 'k':		/* backward compat */
		keep_alive = 1;
		break;
	    case 'r':		/* reload */
		reload = 1;
		break;
	    case 'p':		/* port number */
		sscanf(optarg, "%d", &port);
		if (port < 1)
		    port = CACHE_HTTP_PORT;	/* default */
		break;
	    case 'P':
		put_file = xstrdup(optarg);
		break;
	    case 'i':		/* IMS */
		ims = (time_t) atoi(optarg);
		break;
	    case 'm':
		method = xstrdup(optarg);
		break;
	    case 't':
		method = xstrdup("TRACE");
		max_forwards = atoi(optarg);
		break;
	    case 'g':
		ping = 1;
		pcount = atoi(optarg);
		to_stdout = 0;
		break;
	    case 'I':
		if ((ping_int = atoi(optarg) * 1000) <= 0)
		    usage(argv[0]);
		break;
	    case 'H':
		if (strlen(optarg)) {
		    char *t;
		    strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
		    while ((t = strstr(extra_hdrs, "\\n")))
			*t = '\r', *(t + 1) = '\n';
		}
		break;
	    case 'T':
		io_timeout = atoi(optarg);
		break;
	    case 'u':
		proxy_user = optarg;
		break;
	    case 'w':
		proxy_password = optarg;
		break;
	    case 'U':
		www_user = optarg;
		break;
	    case 'W':
		www_password = optarg;
		break;
	    case 'v':
		/* undocumented: may increase verb-level by giving more -v's */
		opt_verbose++;
		break;
	    case '?':		/* usage */
	    default:
		usage(argv[0]);
		break;
	    }
    }
#ifdef _SQUID_MSWIN_
    {
	WSADATA wsaData;
	WSAStartup(2, &wsaData);
    }
#endif
    /* Build the HTTP request */
    if (strncmp(url, "mgr:", 4) == 0) {
	char *t = xstrdup(url + 4);
	snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
	xfree(t);
    }
    if (put_file) {
	put_fd = open(put_file, O_RDONLY);
	set_our_signal();
	if (put_fd < 0) {
	    fprintf(stderr, "%s: can't open file (%s)\n", argv[0],
		xstrerror());
	    exit(-1);
	}
#ifdef _SQUID_WIN32_
	setmode(put_fd, O_BINARY);
#endif
	fstat(put_fd, &sb);
    }
    snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url);
    if (reload) {
	snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n");
	strcat(msg, buf);
    }
    if (put_fd > 0) {
	snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size);
	strcat(msg, buf);
    }
    if (opt_noaccept == 0) {
	snprintf(buf, BUFSIZ, "Accept: */*\r\n");
	strcat(msg, buf);
    }
    if (ims) {
	snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
	strcat(msg, buf);
    }
    if (max_forwards > -1) {
	snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
	strcat(msg, buf);
    }
    if (proxy_user) {
	char *user = proxy_user;
	char *password = proxy_password;
#if HAVE_GETPASS
	if (!password)
	    password = getpass("Proxy password: "******"ERROR: Proxy password missing\n");
	    exit(1);
	}
	snprintf(buf, BUFSIZ, "%s:%s", user, password);
	snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", base64_encode(buf));
	strcat(msg, buf);
    }
    if (www_user) {
	char *user = www_user;
	char *password = www_password;
#if HAVE_GETPASS
	if (!password)
	    password = getpass("WWW password: "******"ERROR: WWW password missing\n");
	    exit(1);
	}
	snprintf(buf, BUFSIZ, "%s:%s", user, password);
	snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", base64_encode(buf));
	strcat(msg, buf);
    }
    if (keep_alive) {
	if (port != 80)
	    snprintf(buf, BUFSIZ, "Proxy-Connection: keep-alive\r\n");
	else
	    snprintf(buf, BUFSIZ, "Connection: keep-alive\r\n");
	strcat(msg, buf);
    }
    strcat(msg, extra_hdrs);
    snprintf(buf, BUFSIZ, "\r\n");
    strcat(msg, buf);

    if (opt_verbose)
	fprintf(stderr, "headers: '%s'\n", msg);

    if (ping) {
#if HAVE_SIGACTION
	struct sigaction sa, osa;
	if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) {
	    sa.sa_handler = catchSignal;
	    sa.sa_flags = 0;
	    sigemptyset(&sa.sa_mask);
	    (void) sigaction(SIGINT, &sa, NULL);
	}
#else
	void (*osig) ();
	if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL)
	    (void) signal(SIGINT, osig);
#endif
    }
    loops = ping ? pcount : 1;
    for (i = 0; loops == 0 || i < loops; i++) {
	squid_off_t fsize = 0;
	/* Connect to the server */
	if ((conn = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
	    perror("client: socket");
	    exit(1);
	}
	if (localhost && client_comm_bind(conn, localhost) < 0) {
	    perror("client: bind");
	    exit(1);
	}
	if (client_comm_connect(conn, hostname, port, (ping || opt_verbose) ? &tv1 : NULL) < 0) {
	    if (errno == 0) {
		fprintf(stderr, "client: ERROR: Cannot connect to %s:%d: Host unknown.\n", hostname, port);
	    } else {
		char tbuf[BUFSIZ];
		snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s:%d",
		    hostname, port);
		perror(tbuf);
	    }
	    exit(1);
	}
	/* Send the HTTP request */
	bytesWritten = mywrite(conn, msg, strlen(msg));
	if (bytesWritten < 0) {
	    perror("client: ERROR: write");
	    exit(1);
	} else if (bytesWritten != strlen(msg)) {
	    fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg);
	    exit(1);
	}
	if (put_file) {
	    int x;
	    lseek(put_fd, 0, SEEK_SET);
#ifdef _SQUID_MSWIN_
	    while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
#else
	    while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
#endif
		x = mywrite(conn, buf, x);
		total_bytes += x;
		if (x <= 0)
		    break;
	    }
	    if (x != 0)
		fprintf(stderr, "client: ERROR: Cannot send file.\n");
	}
	/* Read the data */

#ifdef _SQUID_MSWIN_
	setmode(1, O_BINARY);
#endif
	while ((len = myread(conn, buf, sizeof(buf))) > 0) {
	    fsize += len;
	    if (to_stdout)
		fwrite(buf, len, 1, stdout);
	}
#ifdef _SQUID_MSWIN_
	setmode(1, O_TEXT);
#endif
	(void) close(conn);	/* done with socket */

	if (interrupted)
	    break;

	if (ping || opt_verbose) {
	    struct tm *tmp;
	    time_t t2s;
	    long elapsed_msec;

	    (void) Now(&tv2);
	    elapsed_msec = tvSubMsec(tv1, tv2);
	    t2s = tv2.tv_sec;
	    tmp = localtime(&t2s);
	    fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s (%" PRINTF_OFF_T "KB)\n",
		tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
		tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1,
		elapsed_msec / 1000, elapsed_msec % 1000,
		elapsed_msec ? (double) fsize / elapsed_msec * 1000 / 1024 : -1.0,
		(fsize + 1023) / 1024);
	    if (i == 0 || elapsed_msec < ping_min)
		ping_min = elapsed_msec;
	    if (i == 0 || elapsed_msec > ping_max)
		ping_max = elapsed_msec;
	    ping_sum += elapsed_msec;
	    /* Delay until next "ping_int" boundary */
	    if (ping && (loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) {
		struct timeval tvs;
		long msec_left = ping_int - elapsed_msec;

		tvs.tv_sec = msec_left / 1000;
		tvs.tv_usec = (msec_left % 1000) * 1000;
		select(0, NULL, NULL, NULL, &tvs);
	    }
	}
    }

    if (ping && i) {
	ping_mean = ping_sum / i;
	fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = "
	    "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i,
	    ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000,
	    ping_max / 1000, ping_max % 1000);
    }
    exit(0);
    /*NOTREACHED */
    return 0;
}

static int
client_comm_bind(int sock, const char *local_host)
{
    static const struct hostent *hp = NULL;
    static struct sockaddr_in from_addr;

    /* Set up the source socket address from which to send. */
    if (hp == NULL) {
	from_addr.sin_family = AF_INET;

	if ((hp = gethostbyname(local_host)) == 0) {
	    return (-1);
	}
	xmemcpy(&from_addr.sin_addr, hp->h_addr, hp->h_length);
	from_addr.sin_port = 0;
    }
    return bind(sock, (struct sockaddr *) &from_addr, sizeof(struct sockaddr_in));
}

static int
client_comm_connect(int sock, const char *dest_host, u_short dest_port, struct timeval *tvp)
{
    static const struct hostent *hp = NULL;
    static struct sockaddr_in to_addr;

    /* Set up the destination socket address for message to send to. */
    if (hp == NULL) {
	to_addr.sin_family = AF_INET;

	if ((hp = gethostbyname(dest_host)) == 0) {
	    return (-1);
	}
	xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length);
	to_addr.sin_port = htons(dest_port);
    }
    if (tvp)
	(void) Now(tvp);
    return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in));
}

static int
Now(struct timeval *tp)
{
#if GETTIMEOFDAY_NO_TZP
    return gettimeofday(tp);
#else
    return gettimeofday(tp, NULL);
#endif
}				/* ARGSUSED */

static void
catchSignal(int sig)
{
    interrupted = 1;
    fprintf(stderr, "Interrupted.\n");
}
/*  return 1 if the entry must be revalidated within delta seconds
 *         0 otherwise
 *
 *  note: request maybe null (e.g. for cache digests build)
 */
static int
refreshCheck(const StoreEntry * entry, request_t * request, time_t delta)
{
    const refresh_t *R;
    const char *uri = NULL;
    time_t age = 0;
    time_t check_time = squid_curtime;
    int staleness;
    stale_flags sf;
    if (entry->mem_obj)
	uri = entry->mem_obj->url;
    else if (request)
	uri = urlCanonical(request);

    debug(22, 3) ("refreshCheck: '%s'\n", uri ? uri : "<none>");

    if (delta > 0)
	check_time += delta;
    if (check_time > entry->timestamp)
	age = check_time - entry->timestamp;
    R = uri ? refreshLimits(uri) : refreshUncompiledPattern(".");
    if (NULL == R)
	R = &DefaultRefresh;
    memset(&sf, '\0', sizeof(sf));
    staleness = refreshStaleness(entry, check_time, age, R, &sf);
    debug(22, 3) ("Staleness = %d\n", staleness);

    debug(22, 3) ("refreshCheck: Matched '%s %d %d%% %d'\n",
	R->pattern, (int) R->min, (int) (100.0 * R->pct), (int) R->max);
    debug(22, 3) ("refreshCheck: age = %d\n", (int) age);
    debug(22, 3) ("\tcheck_time:\t%s\n", mkrfc1123(check_time));
    debug(22, 3) ("\tentry->timestamp:\t%s\n", mkrfc1123(entry->timestamp));

    if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1) {
	debug(22, 3) ("refreshCheck: YES: Must revalidate stale response\n");
	return STALE_MUST_REVALIDATE;
    }
    /* request-specific checks */
    if (request) {
	HttpHdrCc *cc = request->cache_control;
#if HTTP_VIOLATIONS
	if (!request->flags.nocache_hack) {
	    (void) 0;
	} else if (R->flags.ignore_reload) {
	    /* The clients no-cache header is ignored */
	    debug(22, 3) ("refreshCheck: MAYBE: ignore-reload\n");
	} else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) {
	    /* The clients no-cache header is changed into a IMS query */
	    debug(22, 3) ("refreshCheck: YES: reload-into-ims\n");
	    return STALE_RELOAD_INTO_IMS;
	} else {
	    /* The clients no-cache header is not overridden on this request */
	    debug(22, 3) ("refreshCheck: YES: client reload\n");
	    request->flags.nocache = 1;
	    return STALE_FORCED_RELOAD;
	}
#endif
	if (NULL != cc) {
	    if (cc->max_age > -1) {
#if HTTP_VIOLATIONS
		if (R->flags.ignore_reload && cc->max_age == 0) {
		} else
#endif
		if (age > cc->max_age) {
		    debug(22, 3) ("refreshCheck: YES: age > client-max-age\n");
		    return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
		}
	    }
	    if (EBIT_TEST(cc->mask, CC_MAX_STALE) && staleness >= 0) {
		if (cc->max_stale < 0) {
		    /* max-stale directive without a value */
		    debug(22, 3) ("refreshCheck: NO: max-stale wildcard\n");
		    return FRESH_REQUEST_MAX_STALE_ALL;
		} else if (staleness < cc->max_stale) {
		    debug(22, 3) ("refreshCheck: NO: staleness < max-stale\n");
		    return FRESH_REQUEST_MAX_STALE_VALUE;
		}
	    }
	}
    }
    if (staleness < 0) {
	if (sf.expires)
	    return FRESH_EXPIRES;
	assert(!sf.max);
	if (sf.lmfactor)
	    return FRESH_LMFACTOR_RULE;
	assert(sf.min);
	return FRESH_MIN_RULE;
    }
    /*
     * At this point the response is stale, unless one of
     * the override options kicks in.
     */
    if (delta < 0 && staleness + delta < 0) {
	return STALE_WITHIN_DELTA;
    }
    if (sf.expires) {
#if HTTP_VIOLATIONS
	if (R->flags.override_expire && age < R->min) {
	    debug(22, 3) ("refreshCheck: NO: age < min && override-expire\n");
	    return FRESH_OVERRIDE_EXPIRES;
	}
#endif
	return STALE_EXPIRES;
    }
    if (sf.max)
	return STALE_MAX_RULE;
    if (sf.lmfactor) {
#if HTTP_VIOLATIONS
	if (R->flags.override_lastmod && age < R->min) {
	    debug(22, 3) ("refreshCheck: NO: age < min && override-lastmod\n");
	    return FRESH_OVERRIDE_LASTMOD;
	}
#endif
	return STALE_LMFACTOR_RULE;
    }
    return STALE_DEFAULT;
}
/*
 * Calculate how stale the response is (or will be at the check_time).
 * Staleness calculation is based on the following: (1) response
 * expiration time, (2) age greater than configured maximum, (3)
 * last-modified factor, and (4) age less than configured minimum.
 *
 * If the response is fresh, return -1.  Otherwise return its
 * staleness.  NOTE return value of 0 means the response is stale.
 *
 * The 'stale_flags' structure is used to tell the calling function
 * _why_ this response is fresh or stale.  Its used, for example,
 * when the admin wants to override expiration and last-modified
 * times.
 */
static int
refreshStaleness(const StoreEntry * entry, time_t check_time, time_t age, const refresh_t * R, stale_flags * sf)
{
    /*
     * Check for an explicit expiration time.
     */
    if (entry->expires > -1) {
	sf->expires = 1;
	if (entry->expires > check_time) {
	    debug(22, 3) ("FRESH: expires %d >= check_time %d \n",
		(int) entry->expires, (int) check_time);
	    return -1;
	} else {
	    debug(22, 3) ("STALE: expires %d < check_time %d \n",
		(int) entry->expires, (int) check_time);
	    return (check_time - entry->expires);
	}
    }
    assert(age >= 0);
    /*
     * Use local heuristics to determine staleness.  Start with the
     * max age from the refresh_pattern rule.
     */
    if (age > R->max) {
	debug(22, 3) ("STALE: age %d > max %d \n", (int) age, (int) R->max);
	sf->max = 1;
	return (age - R->max);
    }
    if (check_time < entry->timestamp) {
	debug(22, 1) ("STALE: Entry's timestamp greater than check time. Clock going backwards?\n");
	debug(22, 1) ("\tcheck_time:\t%s\n", mkrfc1123(check_time));
	debug(22, 1) ("\tentry->timestamp:\t%s\n", mkrfc1123(entry->timestamp));
	debug(22, 1) ("\tstaleness:\t%ld\n", (long int) entry->timestamp - check_time);
	return (entry->timestamp - check_time);
    }
    /*
     * Try the last-modified factor algorithm.
     */
    if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
	/*
	 * stale_age is the Age of the response when it became/becomes
	 * stale according to the last-modified factor algorithm.
	 */
	time_t stale_age = (entry->timestamp - entry->lastmod) * R->pct;
	sf->lmfactor = 1;
	if (age >= stale_age) {
	    debug(22, 3) ("STALE: age %d > stale_age %d\n",
		(int) age, (int) stale_age);
	    return (age - stale_age);
	} else {
	    debug(22, 3) ("FRESH: age %d <= stale_age %d\n",
		(int) age, (int) stale_age);
	    return -1;
	}
    }
    /*
     * If we are here, staleness is determined by the refresh_pattern
     * configured minimum age.
     */
    if (age <= R->min) {
	debug(22, 3) ("FRESH: age %d <= min %d\n", (int) age, (int) R->min);
	sf->min = 1;
	return -1;
    }
    debug(22, 3) ("STALE: age %d > min %d\n", (int) age, (int) R->min);
    return (age - R->min);
}