/* -*- 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 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "ruleset.h" #include #include #include #include #include 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 */ TQDate 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 TQDate(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(tqstricmp(name[i], s) == 0) return i; return -1; } int RuleSet::load(const TQString &filename) { flat_init_costs = 0.0; flat_init_duration = 0; have_flat_init_costs = false; TQFile 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 TQString 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(TQString::fromUtf8(buffer)); backslashed = (line.right(1) == "\\"); } while(!f.atEnd() && backslashed); // strip whitespace line = line.replace(TQRegExp("[ \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, TQString s, int year) { if(s.contains(TQRegExp("^[0-9]+/[0-9]+$"))) { int d, m; sscanf(s.ascii(), "%d/%d", &m, &d); ret.type = 1; ret.date.from = TQDate(year, m, d); ret.date.until = TQDate(year, m, d); return TRUE; } if(s.contains(TQRegExp("^[0-9]+\\.[0-9]+$"))) { int d, m; sscanf(s.ascii(), "%d.%d", &d, &m); ret.type = 1; ret.date.from = TQDate(year, m, d); ret.date.until = TQDate(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") { TQDate d = get_easter(year); int off; bool ok = TRUE; TQString 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(TQString s, int year, TQTime t1, TQTime 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(','); TQString 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("..")) { TQString 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(TQTime &t1, TQTime &t2, TQString 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, TQString s) { after = 0; int fields = sscanf(s.ascii(), "%lf,%lf,%lf", &costs, &len, &after); return (fields == 2) || (fields == 3); } bool RuleSet::parseLine(const TQString &s) { // ### use TQRegExp::cap() instead of mid() and find() // for our french friends -- Bernd if(s.contains(TQRegExp("flat_init_costs=\\(.*"))) { // parse the time fields TQString 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(TQRegExp("on\\(.*\\)between\\(.*\\)use\\(.*\\)"))) { // parse the time fields TQString token = s.mid(s.find("between(") + 8, s.find(")use")-s.find("between(") - 8); TQTime 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, TQDate::currentDate().year(), t1, t2, costs, len, after)) return FALSE; return TRUE; } // check for the name if(s.contains(TQRegExp("name=.*"))) { _name = s.right(s.length()-5); return !_name.isEmpty(); } // check default entry if(s.contains(TQRegExp("default=\\(.*\\)"))) { TQString 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(TQRegExp("minimum_costs=.*"))) { TQString 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(TQRegExp("currency_digits=.*"))) { TQString 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(TQRegExp("currency_position=.*"))) return TRUE; // check per connection fee if(s.contains(TQRegExp("per_connection="))) { TQString token = s.mid(15, s.length()-15); bool ok; pcf = token.toDouble(&ok); return ok; } return FALSE; } void RuleSet::setStartTime(TQDateTime dt){ starttime = dt; } void RuleSet::getActiveRule(TQDateTime 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 TQDate from = r.date.from; TQDate 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 TQString RuleSet::currencySymbol() const { return _currency_symbol.copy(); } TQString RuleSet::currencyString(double f) const { return TDEGlobal::locale()->formatMoney(f, _currency_symbol, _currency_digits); } double RuleSet::perConnectionCosts() const { return pcf; } TQString RuleSet::name() const { return _name; } double RuleSet::minimumCosts() const { return _minimum_costs; } TQTime RuleSet::midnight() const { return TQTime(0, 0, 0, 0); } TQTime RuleSet::beforeMidnight() const { return TQTime(23,59,59,999); } int RuleSet::checkRuleFile(const TQString &rulefile) { if(rulefile == NULL) { fputs(i18n("kppp: no rulefile specified\n").local8Bit(), stderr); return 1; } TQFile 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; }