summaryrefslogtreecommitdiffstats
path: root/kppp/ruleset.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kppp/ruleset.cpp')
-rw-r--r--kppp/ruleset.cpp581
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;
+}
+