--- squid-trunk/squid/trunk/configure.ac 2011-04-04 21:43:13.000000000 +0200 +++ squid-trunk-time-quota/squid/trunk/configure.ac 2011-04-04 22:11:25.000000000 +0200 @@ -3434,6 +3434,7 @@ helpers/external_acl/session/Makefile \ helpers/external_acl/unix_group/Makefile \ helpers/external_acl/wbinfo_group/Makefile \ + helpers/external_acl/time_quota/Makefile \ helpers/log_daemon/Makefile \ helpers/log_daemon/DB/Makefile \ helpers/log_daemon/file/Makefile \ --- squid-trunk/squid/trunk/errors/templates/ERR_TIME_QUOTA_EXCEEDED 1970-01-01 01:00:00.000000000 +0100 +++ squid-trunk-time-quota/squid/trunk/errors/templates/ERR_TIME_QUOTA_EXCEEDED 2011-04-05 10:12:07.000000000 +0200 @@ -0,0 +1,41 @@ + + + +ERROR: The requested URL could not be retrieved + + +
+

ERROR

+

The requested URL could not be retrieved

+
+
+ +
+

The following error was encountered while trying to retrieve the URL: %U

+ +
+

time quota exceeded

+
+ +

The system returned: %E

+ +

This means:

+
+

Your system limits the amount of time accessing this proxy. Your time budget is now empty but will be refilled when the configured time period starts again.

+
+ +
+
+ +
+ + --- squid-trunk/squid/trunk/helpers/external_acl/Makefile.am 2011-04-04 21:43:13.000000000 +0200 +++ squid-trunk-time-quota/squid/trunk/helpers/external_acl/Makefile.am 2011-04-04 22:11:25.000000000 +0200 @@ -7,6 +7,7 @@ LM_group \ session \ unix_group \ - wbinfo_group + wbinfo_group \ + time_quota SUBDIRS=$(EXTERNAL_ACL_HELPERS) --- squid-trunk/squid/trunk/helpers/external_acl/time_quota/config.test 1970-01-01 01:00:00.000000000 +0100 +++ squid-trunk-time-quota/squid/trunk/helpers/external_acl/time_quota/config.test 2011-04-04 22:11:25.000000000 +0200 @@ -0,0 +1,10 @@ +#!/bin/sh + +# Actual intended test +if [ -f /usr/include/db_185.h ]; then + exit 0 +fi +if [ -f /usr/include/db.h ] && grep dbopen /usr/include/db.h; then + exit 0 +fi +exit 1 --- squid-trunk/squid/trunk/helpers/external_acl/time_quota/ext_time_quota_acl.8 1970-01-01 01:00:00.000000000 +0100 +++ squid-trunk-time-quota/squid/trunk/helpers/external_acl/time_quota/ext_time_quota_acl.8 2011-04-05 10:14:13.000000000 +0200 @@ -0,0 +1,246 @@ +.if !'po4a'hide' .TH ext_time_quota_acl 8 "22 March 2011" +. +.SH NAME +.if !'po4a'hide' .B ext_time_quota_acl +.if !'po4a'hide' \- +Squid time quota external acl helper. +.PP +Version 1.0 +. +.SH SYNOPSIS +.if !'po4a'hide' .B ext_time_quota_acl +.if !'po4a'hide' .B "[\-b database] [\-l logfile] [\-d] [\-p pauselen] [\-h] configfile +. +.SH DESCRIPTION +.B ext_time_quota_acl +allows an administrator to define time budgets for the users of squid +to limit the time using squid. +.PP +This is useful for corporate lunch time allocations, wifi portal +pay-per-minute installations or for parental control of children. The +administrator can define a time budget (e.g. 1 hour per day) which is enforced +through this helper. +. +.SH OPTIONS +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B "\-b database" +.B Filename +of persistent database. This defaults to ext_time_quota.db in Squids state +directory. +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B "\-p pauselen" +.B Pauselen +is given in seconds and defines the pause between two requests to be treated +as pause. Defaults to 300 seconds. See IMPLEMENTATION for a detailed +description. +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B "\-l logfile" +.B Filename +where all logging and debugging information will be written. If none is given, +then stderr will be used and the logging will go to Squids main cache.log. +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B "\-d" +Enables debug logging in the logfile. +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B "\-h" +show a short command line help. +. +.if !'po4a'hide' .TP +.if !'po4a'hide' .B configfile +This file contains the definition of the time budgets for the users. +.PP +. +.SH USER AUTHENTICATION +.PP +This helper needs to know the identity of the user to associate a time budget +with this user. Currently only proxy_auth is able to deliver this +information. Please see the manual page for "basic_ncsa_auth" for +configuration. +. +.SH CONFIGURATION +.PP +The time quotas of the users are defined in a text file typically +residing in /etc/squid/time_quota. Any line starting with "#" contains +a comment and is ignored. Every line must start with a username +followed by a time budget and a corresponding time period separated by +"/". Here is an example file: +.PP +.if !'po4a'hide' .RS +# username budget / period +.if !'po4a'hide' .br +.if !'po4a'hide' .B john 8h / 1d +.if !'po4a'hide' .br +.if !'po4a'hide' .B littlejoe 1h / 1d +.if !'po4a'hide' .br +.if !'po4a'hide' .B babymary 30m / 1w +.if !'po4a'hide' .br +.if !'po4a'hide' .RE +.PP +John has a time budget of 8 hours every day, littlejoe is only allowed +1 hour and the poor babymary only 30 minutes a week. +.PP +You can use "s" for seconds, "m" for minutes, "h" for hours, "d" for +days and "w" for weeks. Numerical values can be given as integer +values or with a fraction. E.g. "0.5h" means 30 minutes. +.PP +This helper is installed in squid.conf where you first declare the helper, +then define a ACL which then decides when to allow or deny. Enter the +following text +.if !'po4a'hide' .B after +the user authentication. +.PP +.if !'po4a'hide' .RS +# +.if !'po4a'hide' .br +# Define program and quota file +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +.if !'po4a'hide' .B external_acl_type time_quota ttl=60 children-max=1 %LOGIN /usr/libexec/ext_time_quota_acl /etc/squid/time_quota +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +# Define ACL for time_quota helper +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +.if !'po4a'hide' .br +.if !'po4a'hide' .B acl time_quota external time_quota +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +# Deny access if quota exceeded +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +.if !'po4a'hide' .B http_access deny !time_quota +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +# If quota exceeds, tell user +.if !'po4a'hide' .br +# +.if !'po4a'hide' .br +.if !'po4a'hide' .B deny_info ERR_TIME_QUOTA_EXCEEDED time_quota +.if !'po4a'hide' .RE +.PP +Username is just a unique key value. The above example uses %LOGIN but any of +the external_acl_type format tags can be substituted in its place. %EXT_TAG, +%LOGIN, %IDENT, %EXT_USER, %SRC and the %SRCEUI* ones are all likely to be +popular. Please read the wiki pages "ConfigExamples" for more information. +.PP +After restarting Squid it should allow access only for authenticated +users as long as they have time budget left. If the buget is exceeded +or an invalid username or password is given, the user will be prompted +for a correct username and password having time quota left. +. +.SH LOGGING +. +Whenever you hit a problem, then start the helper with +.if !'po4a'hide' .B -l logfile +(e.g. "-l /tmp/time_quota.log") and look into that log file to find +any problem. +.PP +Increase logged information by using command line parameter +.B -d +. +.SH LIMITATIONS +This helper only controls access to the internet through HTTP. It does +not control other protocols, like VOIP, ICQ, IRC, FTP, IMAP, SMTP or +SSH. +.PP +Desktop browsers are typically able to deal with HTTP proxies like +squid. But more and more different programs and devices (smartphones, +games on mobile devices, ...) are using the internet over HTTP. These +devices are often not able to work through an authenticating proxy. +Sometimes one can sucessfully use the internet browser on those devices +but often online games and other stuff fails. +.PP +A more general control to internet access could be a captive portal +(like pfSense or ChilliSpot) or maybe a 802.11X solution. But the +latter is often not supported by mobile devices. +. +.SH IMPLEMENTATION +When the helper is called it will be asked if the current user is allowed to +access squid. The helper will reduce the remaining time budget of this user +and return "OK" if there is budget left. Otherwise it will return "ERR". +.PP +The ttl=N parameter in squid.conf determines how oftem the helper will be +called, the example config uses 1 minute TTL. The interaction is that Squid +will only call the helper on new requests +.B if +there has been more than TTL seconds passed since last check. So the correct +description is: "at most once per TTL period". This handling creates an +amount of slippage outside the quota by whatever amount. TTL can be set as +short as desired, down to and including zero. Though values of 1 or more are +recommended due to a quota resolution of one second. +.PP +If the configured time period (e.g. "1w" for babymary) is over, the +time budget will be restored to the configured value thus allowing the +user to access squid with a fresh budget. +.PP +If the time between the current request and the previous request is +greater than +.B pauselen +(defaulting to 5 minutes and adjustable with command line parameter "-p"), the +current request will be considered as a new request and the time budget will +not be decreased. If the time is less than 5 minutes, than both request will +be considered as part of the same active time period and the time budget will +be decreased by the time difference. This allows the user to make arbitrary +breaks during internet access without decreasing the time budget. +. +.SH FURTHER IDEAS +The following ideas could further improve this helper. Maybe someone +wants to help? Any support or feedback is welcome! +.if !'po4a'hide' .TP +There should be a way for a user to see its configured and remaining +time budget. This could be realized by implementing a web page +accessing the database of the helper showing the corresponding +data. One of the problems to be solved is user authentication. +.if !'po4a'hide' .TP +We could always return "OK" and use the module simply as an internet +usage tracker showing who has staid how long in the WWW. +.PP +. +.SH AUTHOR +This program and documentation was written by +.if !'po4a'hide' .I Dr. Tilmann Bubeck +. +.SH COPYRIGHT +This program and documentation is copyright to the authors named above. +.PP +Distributed under the GNU General Public License (GNU GPL) version 2 or later (GPLv2+). +. +.SH QUESTIONS +Questions on the usage of this program can be sent to the +.I Squid Users mailing list +.if !'po4a'hide' +. +.SH REPORTING BUGS +Bug reports need to be made in English. +See http://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report. +.PP +Report bugs or bug fixes using http://bugs.squid-cache.org/ +.PP +Report serious security bugs to +.I Squid Bugs +.PP +Report ideas for new improvements to the +.I Squid Developers mailing list +.if !'po4a'hide' +. +.SH SEE ALSO +.if !'po4a'hide' .BR squid "(8), " +.if !'po4a'hide' .BR basic_ncsa_auth "(8), " +.if !'po4a'hide' .BR GPL "(7), " +.br +The Squid FAQ wiki +.if !'po4a'hide' http://wiki.squid-cache.org/SquidFaq +.br +The Squid Configuration Manual +.if !'po4a'hide' http://www.squid-cache.org/Doc/config/ --- squid-trunk/squid/trunk/helpers/external_acl/time_quota/ext_time_quota_acl.cc 1970-01-01 01:00:00.000000000 +0100 +++ squid-trunk-time-quota/squid/trunk/helpers/external_acl/time_quota/ext_time_quota_acl.cc 2011-04-05 19:42:08.000000000 +0200 @@ -0,0 +1,448 @@ +/* + * ext_time_quota_acl: Squid external acl helper for quota on usage. + * + * Copyright (C) 2011 Dr. Tilmann Bubeck + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif +#include "helpers/defines.h" + +#include +#include +#include +#include +#include +#include +#if HAVE_UNISTD_H +#include +#endif +#include +#include +#if HAVE_GETOPT_H +#include +#endif + +/* At this point all Bit Types are already defined, so we must + protect from multiple type definition on platform where + __BIT_TYPES_DEFINED__ is not defined. + */ +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ +#endif + +#if HAVE_DB_185_H +#include +#elif HAVE_DB_H +#include +#endif + +#ifndef DEFAULT_QUOTA_DB +#error "Please define DEFAULT_QUOTA_DB preprocessor constant." +#endif + +const char *db_path = DEFAULT_QUOTA_DB; +const char *program_name; + +DB *db = NULL; + +#define KEY_LAST_ACTIVITY "last-activity" +#define KEY_PERIOD_START "period-start" +#define KEY_PERIOD_LENGTH_CONFIGURED "period-length-configured" +#define KEY_TIME_BUDGET_LEFT "time-budget-left" +#define KEY_TIME_BUDGET_CONFIGURED "time-budget-configured" + +/** Maximum size of buffers used to read or display lines. */ +#define TQ_BUFFERSIZE 1024 + +/** If there is more than this given number of seconds between two + * sucessive requests, than the second request will be treated as a + * new request and the time between first and seconds request will + * be treated as a activity pause. + * + * Otherwise the following request will be treated as belonging to the + * same activity and the quota will be reduced. + */ +static int pauseLength = 300; + +static FILE *logfile = stderr; +static int tq_debug_enabled = false; + +static void open_log(const char *logfilename) { + logfile = fopen(logfilename, "a"); + if ( logfile == NULL ) { + perror(logfilename); + logfile = stderr; + } +} + +static void vlog(const char *level, const char *format, va_list args) +{ + time_t now = time(NULL); + + fprintf(logfile, "%ld %s| %s: ", now, program_name, level); + vfprintf (logfile, format, args); + fflush(logfile); +} + +static void log_debug(const char *format, ...) +{ + va_list args; + + if ( tq_debug_enabled ) { + va_start (args, format); + vlog("DEBUG", format, args); + va_end (args); + } +} + +static void log_info(const char *format, ...) +{ + va_list args; + + va_start (args, format); + vlog("INFO", format, args); + va_end (args); +} + +static void log_error(const char *format, ...) +{ + va_list args; + + va_start (args, format); + vlog("ERROR", format, args); + va_end (args); +} + +static void log_fatal(const char *format, ...) +{ + va_list args; + + va_start (args, format); + vlog("FATAL", format, args); + va_end (args); +} + +static void init_db(void) +{ + log_info("opening time quota database \"%s\".\n", db_path); + db = dbopen(db_path, O_CREAT | O_RDWR, 0666, DB_BTREE, NULL); + if (!db) { + log_fatal("Failed to open time_quota db '%s'\n", db_path); + exit(1); + } +} + +static void shutdown_db(void) +{ + db->close(db); +} + +static void writeTime(const char *user_key, const char *sub_key, time_t t) +{ + char keybuffer[TQ_BUFFERSIZE]; + DBT key, data; + + if ( strlen(user_key) + strlen(sub_key) + 1 + 1 > sizeof(keybuffer) ) { + log_error("key too long (%s,%s)\n", user_key, sub_key); + } else { + snprintf(keybuffer, sizeof(keybuffer), "%s-%s", user_key, sub_key); + + key.data = (void *)keybuffer; + key.size = strlen(keybuffer); + data.data = &t; + data.size = sizeof(t); + db->put(db, &key, &data, 0); + log_debug("writeTime(\"%s\", %d)\n", keybuffer, t); + } +} + +static time_t readTime(const char *user_key, const char *sub_key) +{ + char keybuffer[TQ_BUFFERSIZE]; + DBT key, data; + time_t t = 0; + + if ( strlen(user_key) + 1 + strlen(sub_key) + 1 > sizeof(keybuffer) ) { + log_error("key too long (%s,%s)\n", user_key, sub_key); + } else { + snprintf(keybuffer, sizeof(keybuffer), "%s-%s", user_key, sub_key); + + key.data = (void *)keybuffer; + key.size = strlen(keybuffer); + if (db->get(db, &key, &data, 0) == 0) { + if (data.size != sizeof(t)) { + log_error("CORRUPTED DATABASE (%s)\n", keybuffer); + } else { + memcpy(&t, data.data, sizeof(t)); + } + } + log_debug("readTime(\"%s\")=%d\n", keybuffer, t); + } + + return t; +} + +static void parseTime(const char *s, time_t *secs, time_t *start) +{ + double value; + char unit; + struct tm *ltime; + int periodLength = 3600; + + *secs = 0; + *start = time(NULL); + ltime = localtime(start); + + sscanf(s, " %lf %c", &value, &unit); + switch (unit) { + case 's': + periodLength = 1; + break; + case 'm': + periodLength = 60; + *start -= ltime->tm_sec; + break; + case 'h': + periodLength = 3600; + *start -= ltime->tm_min * 60 + ltime->tm_sec; + break; + case 'd': + periodLength = 24 * 3600; + *start -= ltime->tm_hour * 3600 + ltime->tm_min * 60 + ltime->tm_sec; + break; + case 'w': + periodLength = 7 * 24 * 3600; + *start -= ltime->tm_hour * 3600 + ltime->tm_min * 60 + ltime->tm_sec; + *start -= ltime->tm_wday * 24 * 3600; + *start += 24 * 3600; // in europe, the week starts monday + break; + default: + log_error("Wrong time unit \"%c\". Only \"m\", \"h\", \"d\", or \"w\" allowed.\n", unit); + break; + } + + *secs = (long)(periodLength * value); +} + + +/** This function parses the time quota file and stores it + * in memory. + */ +static void readConfig(const char *filename) +{ + char line[TQ_BUFFERSIZE]; /* the buffer for the lines read + from the dict file */ + char *cp; /* a char pointer used to parse + each line */ + char *username; /* for the username */ + char *budget; + char *period; + FILE *FH; + time_t t; + time_t budgetSecs, periodSecs; + time_t start; + + log_info("reading config file \"%s\".\n", filename); + + FH = fopen(filename, "r"); + if ( FH ) { + /* the pointer to the first entry in the linked list */ + while ((cp = fgets (line, sizeof(line), FH)) != NULL) { + if (line[0] == '#') { + continue; + } + if ((cp = strchr (line, '\n')) != NULL) { + /* chop \n characters */ + *cp = '\0'; + } + log_debug("read config line \"%s\".\n", line); + if ((cp = strtok (line, "\t ")) != NULL) { + username = cp; + + /* get the time budget */ + budget = strtok (NULL, "/"); + period = strtok (NULL, "/"); + + parseTime(budget, &budgetSecs, &start); + parseTime(period, &periodSecs, &start); + + log_debug("read time quota for user \"%s\": %lds / %lds starting %lds\n", + username, budgetSecs, periodSecs, start); + + writeTime(username, KEY_PERIOD_START, start); + writeTime(username, KEY_PERIOD_LENGTH_CONFIGURED, periodSecs); + writeTime(username, KEY_TIME_BUDGET_CONFIGURED, budgetSecs); + t = readTime(username, KEY_TIME_BUDGET_CONFIGURED); + writeTime(username, KEY_TIME_BUDGET_LEFT, t); + } + } + fclose(FH); + } else { + perror(filename); + } +} + +static void processActivity(const char *user_key) +{ + time_t now = time(NULL); + time_t lastActivity; + time_t activityLength; + time_t periodStart; + time_t periodLength; + time_t userPeriodLength; + time_t timeBudgetCurrent; + time_t timeBudgetConfigured; + char message[TQ_BUFFERSIZE]; + + log_debug("processActivity(\"%s\")\n", user_key); + + // [1] Reset period if over + periodStart = readTime(user_key, KEY_PERIOD_START); + if ( periodStart == 0 ) { + // This is the first period ever. + periodStart = now; + writeTime(user_key, KEY_PERIOD_START, periodStart); + } + + periodLength = now - periodStart; + userPeriodLength = readTime(user_key, KEY_PERIOD_LENGTH_CONFIGURED); + if ( userPeriodLength == 0 ) { + // This user is not configured. Allow anything. + log_debug("No period length found for user \"%s\". Quota for this user disabled.\n", user_key); + writeTime(user_key, KEY_TIME_BUDGET_LEFT, pauseLength); + } else { + if ( periodLength >= userPeriodLength ) { + // a new period has started. + log_debug("New time period started for user \"%s\".\n", user_key); + while ( periodStart < now ) { + periodStart += periodLength; + } + writeTime(user_key, KEY_PERIOD_START, periodStart); + timeBudgetConfigured = readTime(user_key, KEY_TIME_BUDGET_CONFIGURED); + if ( timeBudgetConfigured == 0 ) { + log_debug("No time budget configured for user \"%s\". Quota for this user disabled.\n", user_key); + writeTime(user_key, KEY_TIME_BUDGET_LEFT, pauseLength); + } else { + writeTime(user_key, KEY_TIME_BUDGET_LEFT, timeBudgetConfigured); + } + } + } + + // [2] Decrease time budget iff activity + lastActivity = readTime(user_key, KEY_LAST_ACTIVITY); + if ( lastActivity == 0 ) { + // This is the first request ever + writeTime(user_key, KEY_LAST_ACTIVITY, now); + } else { + activityLength = now - lastActivity; + if ( activityLength >= pauseLength ) { + // This is an activity pause. + log_debug("Activity pause detected for user \"%s\".\n", user_key); + writeTime(user_key, KEY_LAST_ACTIVITY, now); + } else { + // This is real usage. + writeTime(user_key, KEY_LAST_ACTIVITY, now); + + log_debug("Time budget reduced by %ld for user \"%s\".\n", + activityLength, user_key); + timeBudgetCurrent = readTime(user_key, KEY_TIME_BUDGET_LEFT); + timeBudgetCurrent -= activityLength; + writeTime(user_key, KEY_TIME_BUDGET_LEFT, timeBudgetCurrent); + } + } + + timeBudgetCurrent = readTime(user_key, KEY_TIME_BUDGET_LEFT); + snprintf(message, TQ_BUFFERSIZE, "message=\"Remaining quota for '%s' is %d seconds.\"", user_key, (int)timeBudgetCurrent); + if ( timeBudgetCurrent > 0 ) { + log_debug("OK %s.\n", message); + SEND_OK(message); + } else { + log_debug("ERR %s\n", message); + SEND_ERR("Time budget exceeded."); + } + + db->sync(db, 0); +} + +static void usage(void) +{ + log_error("Wrong usage. Please reconfigure in squid.conf.\n"); + + fprintf(stderr, "Usage: %s [-d] [-l logfile] [-b dbpath] [-p pauselen] [-h] configfile\n", program_name); + fprintf(stderr, " -d enable debugging output to logfile\n"); + fprintf(stderr, " -l logfile log messages to logfile\n"); + fprintf(stderr, " -b dbpath Path where persistent session database will be kept\n"); + fprintf(stderr, " If option is not used, then " DEFAULT_QUOTA_DB " will be used.\n"); + fprintf(stderr, " -p pauselen length in seconds to describe a pause between 2 requests.\n"); + fprintf(stderr, " -h show show command line help.\n"); + fprintf(stderr, "configfile is a file containing time quota definitions.\n"); +} + +int main(int argc, char **argv) +{ + char request[HELPER_INPUT_BUFFER]; + int opt; + + program_name = argv[0]; + + while ((opt = getopt(argc, argv, "dp:l:b:h")) != -1) { + switch (opt) { + case 'd': + tq_debug_enabled = true; + break; + case 'l': + open_log(optarg); + break; + case 'b': + db_path = optarg; + break; + case 'p': + pauseLength = atoi(optarg); + break; + case 'h': + usage(); + exit(0); + break; + } + } + + log_info("Starting %s\n", __FILE__); + setbuf(stdout, NULL); + + init_db(); + + if ( optind + 1 != argc ) { + usage(); + exit(1); + } else { + readConfig(argv[optind]); + } + + log_info("Waiting for requests...\n"); + while (fgets(request, HELPER_INPUT_BUFFER, stdin)) { + // we expect the following line syntax: "%LOGIN + const char *user_key = NULL; + user_key = strtok(request, " \n"); + + processActivity(user_key); + } + log_info("Ending %s\n", __FILE__); + shutdown_db(); + return 0; +} --- squid-trunk/squid/trunk/helpers/external_acl/time_quota/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ squid-trunk-time-quota/squid/trunk/helpers/external_acl/time_quota/Makefile.am 2011-04-05 09:33:51.000000000 +0200 @@ -0,0 +1,12 @@ +include $(top_srcdir)/src/Common.am + +libexec_PROGRAMS = ext_time_quota_acl +man_MANS = ext_time_quota_acl.8 +EXTRA_DIST = ext_time_quota_acl.8 config.test +ext_time_quota_acl_SOURCES = ext_time_quota_acl.cc + +DEFS += -DDEFAULT_QUOTA_DB=\"$(localstatedir)/ext_time_quota.db\" + +LDADD = \ + $(COMPAT_LIB) \ + $(LIB_DB)