2 * kPPP: A pppd front end for the KDE project
6 * Copyright (C) 1997 Bernd Johannes Wuebben
7 * wuebben@math.cornell.edu
9 * This file was contributed by Mario Weilguni <mweilguni@sime.com>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
42 _currency_symbol
= "$";
45 flat_init_costs
= 0.0;
46 flat_init_duration
= 0;
47 have_flat_init_costs
= false;
52 // this function is shamelessly stolen from pppcosts 0.5 :-)
53 /* calculates the easter sunday in day_of_year style */
54 QDate
RuleSet::get_easter(int year
) {
55 /* not optimized, I took the original names */
56 signed int a
,b
,m
,q
,w
,p
,n
,tt
,mm
;
58 /* calculating easter is really funny */
59 /* this is O'Beirne's algorithm, only valid 1900-2099 */
62 b
= (int)((7*a
+1)/19);
74 return QDate(year
, mm
, tt
);
77 int RuleSet::dayNameToInt(const char *s
) {
78 const char *const name
[] = {"monday", "tuesday", "wednesday",
79 "thursday", "friday", "saturday",
82 for(int i
= 0; name
[i
] != NULL
; i
++)
83 if(qstricmp(name
[i
], s
) == 0)
89 int RuleSet::load(const QString
&filename
) {
91 flat_init_costs
= 0.0;
92 flat_init_duration
= 0;
93 have_flat_init_costs
= false;
101 // ignore "No Accounting"
102 if(filename
.isEmpty())
108 if(!f
.open(QIODevice::ReadOnly
))
111 char buffer
[2048]; // safe
115 // read continued lines
119 int br
= f
.readLine(buffer
, sizeof(buffer
));
121 if((br
> 0) && (buffer
[br
-1] == '\n'))
126 line
.append(QString::fromUtf8(buffer
));
127 backslashed
= (line
.right(1) == "\\");
128 } while(!f
.atEnd() && backslashed
);
131 line
= line
.replace(QRegExp("[ \t\r]"), "");
132 // skip comment lines
133 if((line
.left(1) == "#") || line
.isEmpty())
137 if(!parseLine(line
)) {
139 kError(5002) << "ERROR IN LINE " << lineno
<< endl
;
146 if(_name
.length() > 0)
149 kError(5002) << "NO NAME DEFINED" << endl
;
154 void RuleSet::addRule(RULE r
) {
155 // check for a default rule
157 (r
.weekday
.from
== 0) && (r
.weekday
.until
== 6) &&
158 (r
.from
== midnight()) &&
159 (r
.until
== beforeMidnight()))
161 default_costs
= r
.costs
;
166 // if from < until (i.e on (monday..friday)
167 // from (21:00..05:00) use (0.2,16)
168 // split it into two rules
169 // ... between (21:00..23:59) use ...
170 // ... between (00:00..05:00) use ...
171 if(r
.from
> r
.until
) {
175 r1
.until
= beforeMidnight();
176 r2
.from
= midnight();
177 rules
.resize(rules
.size()+2);
178 rules
[rules
.size()-2] = r1
;
179 rules
[rules
.size()-1] = r2
;
181 rules
.resize(rules
.size()+1);
182 rules
[rules
.size()-1] = r
;
186 bool RuleSet::parseEntry(RULE
&ret
, QString s
, int year
) {
187 if(s
.contains(QRegExp("^[0-9]+/[0-9]+$"))) {
189 sscanf(s
.toAscii(), "%d/%d", &m
, &d
);
191 ret
.date
.from
= QDate(year
, m
, d
);
192 ret
.date
.until
= QDate(year
, m
, d
);
196 if(s
.contains(QRegExp("^[0-9]+\\.[0-9]+$"))) {
198 sscanf(s
.toAscii(), "%d.%d", &d
, &m
);
200 ret
.date
.from
= QDate(year
, m
, d
);
201 ret
.date
.until
= QDate(year
, m
, d
);
205 if(s
.right(3) == "day") {
206 int d
= dayNameToInt(s
.toAscii());
209 ret
.weekday
.from
= d
;
210 ret
.weekday
.until
= d
;
215 if(s
.left(6) == "easter") {
216 QDate d
= get_easter(year
);
219 QString val
= s
.mid(6, 1000);
223 off
= val
.toInt(&ok
);
240 bool RuleSet::parseEntries(QString s
, int year
,
242 double costs
, double len
, double after
)
244 // special rule: on() is the same as on(monday..sunday)
246 s
= "monday..sunday";
249 int pos
= s
.indexOf(',');
256 s
= s
.right(s
.length()-pos
-1);
259 // we've a token, now check if it defines a
262 if(token
.contains("..")) {
264 left
= token
.left(token
.indexOf(".."));
265 right
= token
.right(token
.length()-2-left
.length());
267 if(parseEntry(lr
, left
, year
) && parseEntry(rr
, right
, year
)) {
268 if(lr
.type
== rr
.type
) {
272 r
.date
.from
= lr
.date
.from
;
273 r
.date
.until
= rr
.date
.from
;
276 r
.weekday
.from
= lr
.weekday
.from
;
277 r
.weekday
.until
= rr
.weekday
.from
;
283 if(!parseEntry(r
, token
, year
))
297 bool RuleSet::parseTime(QTime
&t1
, QTime
&t2
, QString s
) {
300 t2
= beforeMidnight();
303 int t1m
, t1h
, t2m
, t2h
;
304 if(sscanf(s
.toAscii(), "%d:%d..%d:%d", &t1h
, &t1m
, &t2h
, &t2m
) == 4) {
305 t1
.setHMS(t1h
, t1m
, 0);
306 t2
.setHMS(t2h
, t2m
, 0);
313 bool RuleSet::parseRate(double &costs
, double &len
, double &after
, QString s
) {
315 int fields
= sscanf(s
.toAscii(), "%lf,%lf,%lf", &costs
, &len
, &after
);
316 return (fields
== 2) || (fields
== 3);
319 bool RuleSet::parseLine(const QString
&s
) {
321 // ### use QRegExp::cap() instead of mid() and find()
323 // for our french friends -- Bernd
324 if(s
.contains(QRegExp("flat_init_costs=\\(.*"))) {
325 // parse the time fields
326 QString token
= s
.mid(s
.indexOf("flat_init_costs=(") + 17,
327 s
.indexOf(")")-s
.indexOf("flat_init_costs=(") - 17);
328 // printf("TOKEN=%s\n",token.ascii());
331 if(!parseRate(flat_init_costs
, flat_init_duration
, after
, token
))
334 //printf("COST %f DURATION %f\n",flat_init_costs,flat_init_duration);
336 if(! (flat_init_costs
>= 0.0) )
338 if(! (flat_init_duration
>= 0.0))
341 have_flat_init_costs
= true;
346 if(s
.contains(QRegExp("on\\(.*\\)between\\(.*\\)use\\(.*\\)"))) {
347 // parse the time fields
348 QString token
= s
.mid(s
.indexOf("between(") + 8,
349 s
.indexOf(")use")-s
.indexOf("between(") - 8);
351 if(!parseTime(t1
, t2
, token
))
354 // parse the rate fields
355 token
= s
.mid(s
.indexOf("use(") + 4,
356 s
.lastIndexOf(")")-s
.indexOf("use(") - 4);
360 if(!parseRate(costs
, len
, after
, token
))
364 token
= s
.mid(s
.indexOf("on(") + 3,
365 s
.indexOf(")betw")-s
.indexOf("on(") - 3);
366 if(!parseEntries(token
, QDate::currentDate().year(),
367 t1
, t2
, costs
, len
, after
))
373 // check for the name
374 if(s
.contains(QRegExp("name=.*"))) {
375 _name
= s
.right(s
.length()-5);
376 return !_name
.isEmpty();
380 // check default entry
381 if(s
.contains(QRegExp("default=\\(.*\\)"))) {
382 QString token
= s
.mid(9, s
.length() - 10);
384 if(parseRate(default_costs
, default_len
, after
, token
))
388 // check for "minimum costs"
389 if(s
.contains(QRegExp("minimum_costs=.*"))) {
390 QString token
= s
.right(s
.length() - strlen("minimum_costs="));
392 _minimum_costs
= token
.toDouble(&ok
);
396 // check currency settings
397 if(s
.startsWith("currency_symbol=")) {
398 _currency_symbol
= s
.mid(16);
402 if(s
.contains(QRegExp("currency_digits=.*"))) {
403 QString token
= s
.mid(16, s
.length() - 16);
405 _currency_digits
= token
.toInt(&ok
);
406 return ok
&& (_currency_digits
>= 0);
409 // "currency_position" is deprecated so we'll simply ignore it
410 if(s
.contains(QRegExp("currency_position=.*")))
413 // check per connection fee
414 if(s
.contains(QRegExp("per_connection="))) {
415 QString token
= s
.mid(15, s
.length()-15);
417 pcf
= token
.toDouble(&ok
);
424 void RuleSet::setStartTime(const QDateTime
&dt
){
430 void RuleSet::getActiveRule(const QDateTime
&dt
, double connect_time
, double &costs
, double &len
) {
431 // use default costs first
432 costs
= default_costs
;
435 //printf("In getActiveRule\n");
436 if(have_flat_init_costs
){
438 costs
= flat_init_costs
;
439 len
= flat_init_duration
;
440 have_flat_init_costs
= false;
441 //printf("getActiveRule FLATINITCOSTS\n");
446 for(int i
= 0; i
< (int)rules
.size(); i
++) {
452 // since rules do not have a year's entry, use the one
454 QDate from
= r
.date
.from
;
455 QDate until
= r
.date
.until
;
456 from
.setYMD(dt
.date().year(), from
.month(), from
.day());
457 until
.setYMD(dt
.date().year(), until
.month(), until
.day());
458 if((from
<= dt
.date()) && (dt
.date() <= until
)) {
460 if((r
.from
<= dt
.time()) && (dt
.time() <= r
.until
) && (connect_time
>= r
.after
)) {
468 case 2: // one or more weekdays
469 // check if the range overlaps sunday.
470 // (i.e. "on(saturday..monday)")
471 if(r
.weekday
.from
<= r
.weekday
.until
) {
472 if((r
.weekday
.from
<= dt
.date().dayOfWeek() - 1) &&
473 (r
.weekday
.until
>= dt
.date().dayOfWeek() - 1))
476 if((r
.from
<= dt
.time()) && (dt
.time() <= r
.until
) && (connect_time
>= r
.after
)) {
481 } else { // yes, they overlap sunday
482 if((r
.weekday
.from
>= dt
.date().dayOfWeek() - 1) &&
483 (dt
.date().dayOfWeek() - 1 <= r
.weekday
.until
))
486 if((r
.from
<= dt
.time()) && (dt
.time() <= r
.until
) && (connect_time
>= r
.after
)) {
498 static double round(double d
, int digits
) {
499 d
*= pow(10, digits
);
501 d
/= pow(10, digits
);
506 QString
RuleSet::currencySymbol() const {
507 return _currency_symbol
;
510 QString
RuleSet::currencyString(double f
) const {
511 return KGlobal::locale()->formatMoney(f
, _currency_symbol
, _currency_digits
);
515 double RuleSet::perConnectionCosts() const {
520 QString
RuleSet::name() const {
525 double RuleSet::minimumCosts() const {
526 return _minimum_costs
;
529 QTime
RuleSet::midnight() const {
530 return QTime(0, 0, 0, 0);
533 QTime
RuleSet::beforeMidnight() const {
534 return QTime(23,59,59,999);
537 int RuleSet::checkRuleFile(const QString
&rulefile
) {
538 if(rulefile
.isEmpty()) {
539 fputs(i18n("kppp: no rulefile specified\n").toLocal8Bit(), stderr
);
545 fprintf(stderr
, i18n("kppp: rulefile \"%s\" not found\n").toLocal8Bit(), rulefile
.toLocal8Bit().data());
549 if(rulefile
.right(4) != ".rst") {
550 fputs(i18n("kppp: rulefiles must have the extension \".rst\"\n").toLocal8Bit(), stderr
);
555 int err
= r
.load(rulefile
);
559 fputs(i18n("kppp: error parsing the ruleset\n").toLocal8Bit(), stderr
);
564 fprintf(stderr
, i18n("kppp: parse error in line %d\n").toLocal8Bit(), err
);
568 // check for the existence of a default rule
569 if((r
.default_costs
< 0) || (r
.default_len
< 0)) {
570 fputs(i18n("kppp: rulefile does not contain a default rule\n").toLocal8Bit(), stderr
);
574 if(r
.name().length() == 0) {
575 fputs(i18n("kppp: rulefile does not contain a \"name=...\" line\n").toLocal8Bit(), stderr
);
579 fputs(i18n("kppp: rulefile is ok\n").toLocal8Bit(), stderr
);