diff options
Diffstat (limited to 'kppp/ruleset.cpp')
-rw-r--r-- | kppp/ruleset.cpp | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/kppp/ruleset.cpp b/kppp/ruleset.cpp new file mode 100644 index 00000000..1775b5c2 --- /dev/null +++ b/kppp/ruleset.cpp @@ -0,0 +1,581 @@ +/* -*- C++ -*- + * kPPP: A pppd front end for the KDE project + * + * $Id$ + * + * Copyright (C) 1997 Bernd Johannes Wuebben + * wuebben@math.cornell.edu + * + * This file was contributed by Mario Weilguni <mweilguni@sime.com> + * Thanks Mario ! + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include "ruleset.h" + +#include <qregexp.h> +#include <qfile.h> + +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> + +RuleSet::RuleSet() { + default_costs = -1; + default_len = -1; + _currency_symbol = "$"; + _currency_digits = 2; + _minimum_costs = 0; + flat_init_costs = 0.0; + flat_init_duration = 0; + have_flat_init_costs = false; + + pcf = 0.0; +} + +// this function is shamelessly stolen from pppcosts 0.5 :-) +/* calculates the easter sunday in day_of_year style */ +QDate RuleSet::get_easter(int year) { + /* not optimized, I took the original names */ + signed int a,b,m,q,w,p,n,tt,mm; + + /* calculating easter is really funny */ + /* this is O'Beirne's algorithm, only valid 1900-2099 */ + n = year - 1900; + a = n % 19; + b = (int)((7*a+1)/19); + m = (11*a+4-b) % 29; + q = (int)(n/4); + w = (n+q+31-m) % 7; + p = 25-m-w; + if (p>0) + {tt=p; + mm=4;} + else + {tt=p+31; + mm=3;} + + return QDate(year, mm, tt); +} + +int RuleSet::dayNameToInt(const char *s) { + const char *const name[] = {"monday", "tuesday", "wednesday", + "thursday", "friday", "saturday", + "sunday", NULL}; + + for(int i = 0; name[i] != NULL; i++) + if(qstricmp(name[i], s) == 0) + return i; + + return -1; +} + +int RuleSet::load(const QString &filename) { + + flat_init_costs = 0.0; + flat_init_duration = 0; + have_flat_init_costs = false; + + QFile f(filename); + + // delete old rules + rules.resize(0); + _name = ""; + + // ignore "No Accounting" + if(filename.isEmpty()) + return 0; + + if(!f.exists()) + return -1; + + if(!f.open(IO_ReadOnly)) + return -1; + + char buffer[2048]; // safe + int lineno=0; + + while(!f.atEnd()) { + // read continued lines + QString line; + bool backslashed; + do { + int br = f.readLine(buffer, sizeof(buffer)); + if((br > 0) && (buffer[br-1] == '\n')) + buffer[br-1] = 0; + else + buffer[br] = 0; + lineno++; + line.append(QString::fromUtf8(buffer)); + backslashed = (line.right(1) == "\\"); + } while(!f.atEnd() && backslashed); + + // strip whitespace + line = line.replace(QRegExp("[ \t\r]"), ""); + // skip comment lines + if((line.left(1) == "#") || line.isEmpty()) + continue; + + // parse line + if(!parseLine(line)) { + f.close(); + kdError(5002) << "ERROR IN LINE " << lineno << endl; + return lineno; + } + } + + f.close(); + + if(_name.length() > 0) + return 0; + else { + kdError(5002) << "NO NAME DEFINED" << endl; + return -1; + } +} + +void RuleSet::addRule(RULE r) { + // check for a default rule + if((r.type == 2) && + (r.weekday.from == 0) && (r.weekday.until == 6) && + (r.from == midnight()) && + (r.until == beforeMidnight())) + { + default_costs = r.costs; + default_len = r.len; + return; + } + + // if from < until (i.e on (monday..friday) + // from (21:00..05:00) use (0.2,16) + // split it into two rules + // ... between (21:00..23:59) use ... + // ... between (00:00..05:00) use ... + if(r.from > r.until) { + RULE r1, r2; + r1 = r; + r2 = r; + r1.until = beforeMidnight(); + r2.from = midnight(); + rules.resize(rules.size()+2); + rules[rules.size()-2] = r1; + rules[rules.size()-1] = r2; + } else { + rules.resize(rules.size()+1); + rules[rules.size()-1] = r; + } +} + +bool RuleSet::parseEntry(RULE &ret, QString s, int year) { + if(s.contains(QRegExp("^[0-9]+/[0-9]+$"))) { + int d, m; + sscanf(s.ascii(), "%d/%d", &m, &d); + ret.type = 1; + ret.date.from = QDate(year, m, d); + ret.date.until = QDate(year, m, d); + return TRUE; + } + + if(s.contains(QRegExp("^[0-9]+\\.[0-9]+$"))) { + int d, m; + sscanf(s.ascii(), "%d.%d", &d, &m); + ret.type = 1; + ret.date.from = QDate(year, m, d); + ret.date.until = QDate(year, m, d); + return TRUE; + } + + if(s.right(3) == "day") { + int d = dayNameToInt(s.ascii()); + if(d != -1) { + ret.type = 2; + ret.weekday.from = d; + ret.weekday.until = d; + return TRUE; + } + } + + if(s.left(6) == "easter") { + QDate d = get_easter(year); + int off; + bool ok = TRUE; + QString val = s.mid(6, 1000); + if(val.isEmpty()) + off = 0; + else + off = val.toInt(&ok); + + if(ok) { + d = d.addDays(off); + ret.type = 1; + ret.date.from = d; + ret.date.until = d; + return TRUE; + } + } + + ret.type = 0; + return FALSE; +} + + + +bool RuleSet::parseEntries(QString s, int year, + QTime t1, QTime t2, + double costs, double len, double after) +{ + // special rule: on() is the same as on(monday..sunday) + if(s.isEmpty()) + s = "monday..sunday"; + + while(s.length()) { + int pos = s.find(','); + QString token; + if(pos == -1) { + token = s; + s = ""; + } else { + token = s.left(pos); + s = s.right(s.length()-pos-1); + } + + // we've a token, now check if it defines a + // range + RULE r; + if(token.contains("..")) { + QString left, right; + left = token.left(token.find("..")); + right = token.right(token.length()-2-left.length()); + RULE lr, rr; + if(parseEntry(lr, left, year) && parseEntry(rr, right, year)) { + if(lr.type == rr.type) { + r.type = lr.type; + switch(lr.type) { + case 1: + r.date.from = lr.date.from; + r.date.until = rr.date.from; + break; + case 2: + r.weekday.from = lr.weekday.from; + r.weekday.until = rr.weekday.from; + } + } else + return FALSE; + } + } else + if(!parseEntry(r, token, year)) + return FALSE; + + r.costs = costs; + r.len = len; + r.after = after; + r.from = t1; + r.until = t2; + addRule(r); + } + + return TRUE; +} + +bool RuleSet::parseTime(QTime &t1, QTime &t2, QString s) { + if(s.isEmpty()) { + t1 = midnight(); + t2 = beforeMidnight(); + return TRUE; + } else { + int t1m, t1h, t2m, t2h; + if(sscanf(s.ascii(), "%d:%d..%d:%d", &t1h, &t1m, &t2h, &t2m) == 4) { + t1.setHMS(t1h, t1m, 0); + t2.setHMS(t2h, t2m, 0); + return TRUE; + } else + return FALSE; + } +} + +bool RuleSet::parseRate(double &costs, double &len, double &after, QString s) { + after = 0; + int fields = sscanf(s.ascii(), "%lf,%lf,%lf", &costs, &len, &after); + return (fields == 2) || (fields == 3); +} + +bool RuleSet::parseLine(const QString &s) { + + // ### use QRegExp::cap() instead of mid() and find() + + // for our french friends -- Bernd + if(s.contains(QRegExp("flat_init_costs=\\(.*"))) { + // parse the time fields + QString token = s.mid(s.find("flat_init_costs=(") + 17, + s.find(")")-s.find("flat_init_costs=(") - 17); + // printf("TOKEN=%s\n",token.ascii()); + + double after; + if(!parseRate(flat_init_costs, flat_init_duration, after, token)) + return FALSE; + + //printf("COST %f DURATION %f\n",flat_init_costs,flat_init_duration); + + if(! (flat_init_costs >= 0.0) ) + return FALSE; + if(! (flat_init_duration >= 0.0)) + return FALSE; + + have_flat_init_costs = true; + return TRUE; + } + + + if(s.contains(QRegExp("on\\(.*\\)between\\(.*\\)use\\(.*\\)"))) { + // parse the time fields + QString token = s.mid(s.find("between(") + 8, + s.find(")use")-s.find("between(") - 8); + QTime t1, t2; + if(!parseTime(t1, t2, token)) + return FALSE; + + // parse the rate fields + token = s.mid(s.find("use(") + 4, + s.findRev(")")-s.find("use(") - 4); + double costs; + double len; + double after; + if(!parseRate(costs, len, after, token)) + return FALSE; + + // parse the days + token = s.mid(s.find("on(") + 3, + s.find(")betw")-s.find("on(") - 3); + if(!parseEntries(token, QDate::currentDate().year(), + t1, t2, costs, len, after)) + return FALSE; + + return TRUE; + } + + // check for the name + if(s.contains(QRegExp("name=.*"))) { + _name = s.right(s.length()-5); + return !_name.isEmpty(); + } + + + // check default entry + if(s.contains(QRegExp("default=\\(.*\\)"))) { + QString token = s.mid(9, s.length() - 10); + double after; + if(parseRate(default_costs, default_len, after, token)) + return TRUE; + } + + // check for "minimum costs" + if(s.contains(QRegExp("minimum_costs=.*"))) { + QString token = s.right(s.length() - strlen("minimum_costs=")); + bool ok; + _minimum_costs = token.toDouble(&ok); + return ok; + } + + // check currency settings + if(s.startsWith("currency_symbol=")) { + _currency_symbol = s.mid(16); + return TRUE; + } + + if(s.contains(QRegExp("currency_digits=.*"))) { + QString token = s.mid(16, s.length() - 16); + bool ok; + _currency_digits = token.toInt(&ok); + return ok && (_currency_digits >= 0); + } + + // "currency_position" is deprecated so we'll simply ignore it + if(s.contains(QRegExp("currency_position=.*"))) + return TRUE; + + // check per connection fee + if(s.contains(QRegExp("per_connection="))) { + QString token = s.mid(15, s.length()-15); + bool ok; + pcf = token.toDouble(&ok); + return ok; + } + + return FALSE; +} + +void RuleSet::setStartTime(QDateTime dt){ + + starttime = dt; + +} + +void RuleSet::getActiveRule(QDateTime dt, double connect_time, double &costs, double &len) { + // use default costs first + costs = default_costs; + len = default_len; + + //printf("In getActiveRule\n"); + if(have_flat_init_costs){ + + costs = flat_init_costs; + len = flat_init_duration; + have_flat_init_costs = false; + //printf("getActiveRule FLATINITCOSTS\n"); + return; + } + + // check every rule + for(int i = 0; i < (int)rules.size(); i++) { + RULE r = rules[i]; + + switch(r.type) { + case 1: // a date + { + // since rules do not have a year's entry, use the one + // from dt + QDate from = r.date.from; + QDate until = r.date.until; + from.setYMD(dt.date().year(), from.month(), from.day()); + until.setYMD(dt.date().year(), until.month(), until.day()); + if((from <= dt.date()) && (dt.date() <= until)) { + // check time + if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) { + costs = r.costs; + len = r.len; + } + } + } + break; + + case 2: // one or more weekdays + // check if the range overlaps sunday. + // (i.e. "on(saturday..monday)") + if(r.weekday.from <= r.weekday.until) { + if((r.weekday.from <= dt.date().dayOfWeek() - 1) && + (r.weekday.until >= dt.date().dayOfWeek() - 1)) + { + // check time + if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) { + costs = r.costs; + len = r.len; + } + } + } else { // yes, they overlap sunday + if((r.weekday.from >= dt.date().dayOfWeek() - 1) && + (dt.date().dayOfWeek() - 1 <= r.weekday.until)) + { + // check time + if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) { + costs = r.costs; + len = r.len; + } + } + } + } + } +} + + +#if 0 +static double round(double d, int digits) { + d *= pow(10, digits); + d = rint(d); + d /= pow(10, digits); + return d; +} +#endif + +QString RuleSet::currencySymbol() const { + return _currency_symbol.copy(); +} + +QString RuleSet::currencyString(double f) const { + return KGlobal::locale()->formatMoney(f, _currency_symbol, _currency_digits); +} + + +double RuleSet::perConnectionCosts() const { + return pcf; +} + + +QString RuleSet::name() const { + return _name; +} + + +double RuleSet::minimumCosts() const { + return _minimum_costs; +} + +QTime RuleSet::midnight() const { + return QTime(0, 0, 0, 0); +} + +QTime RuleSet::beforeMidnight() const { + return QTime(23,59,59,999); +} + +int RuleSet::checkRuleFile(const QString &rulefile) { + if(rulefile == NULL) { + fputs(i18n("kppp: no rulefile specified\n").local8Bit(), stderr); + return 1; + } + + QFile fl(rulefile); + if(!fl.exists()) { + fprintf(stderr, i18n("kppp: rulefile \"%s\" not found\n").local8Bit(), rulefile.local8Bit().data()); + return 1; + } + + if(rulefile.right(4) != ".rst") { + fputs(i18n("kppp: rulefiles must have the extension \".rst\"\n").local8Bit(), stderr); + return 1; + } + + RuleSet r; + int err = r.load(rulefile); + fl.close(); + + if(err == -1) { + fputs(i18n("kppp: error parsing the ruleset\n").local8Bit(), stderr); + return 1; + } + + if(err > 0) { + fprintf(stderr, i18n("kppp: parse error in line %d\n").local8Bit(), err); + return 1; + } + + // check for the existance of a default rule + if((r.default_costs < 0) || (r.default_len < 0)) { + fputs(i18n("kppp: rulefile does not contain a default rule\n").local8Bit(), stderr); + return 1; + } + + if(r.name().length() == 0) { + fputs(i18n("kppp: rulefile does not contain a \"name=...\" line\n").local8Bit(), stderr); + return 1; + } + + fputs(i18n("kppp: rulefile is ok\n").local8Bit(), stderr); + return 0; +} + |