/* -*- C++ -*- * * kPPP: A pppd front end for the KDE project * * $Id$ * * Copyright (C) 1997 Bernd Johannes Wuebben * wuebben@math.cornell.edu * * This file 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include "accounting.h" #include "pppdata.h" #include "pppstats.h" // defines the maximum duration until the current costs // are saved again (to prevent loss due to "kill") // specifying -1 disables the features #define UPDATE_TIME (5*60*1000) extern PPPData gpppdata; ///////////////////////////////////////////////////////////////////////////// // // Helper functions // ///////////////////////////////////////////////////////////////////////////// static QString timet2qstring(time_t t) { QString s; s.sprintf("%lu", t); return s; } ///////////////////////////////////////////////////////////////////////////// // // The base class for the accounting system provides a base set of usefull // and common functions, but does not do any accounting by itself. The // accounting is accomplished withing it's derived classes // ///////////////////////////////////////////////////////////////////////////// AccountingBase::AccountingBase(QObject *parent) : QObject(parent), _total(0), _session(0) { QDate dt = QDate::currentDate(); LogFileName = QString("%1-%2.log") .arg(QDate::shortMonthName(dt.month())) .arg(dt.year(), 4); LogFileName = KGlobal::dirs()->saveLocation("appdata", "Log") + "/" + LogFileName; kdDebug(5002) << "LogFileName: " << LogFileName << endl; } AccountingBase::~AccountingBase() { if(running()) slotStop(); } double AccountingBase::total() const { return _total + _session; } double AccountingBase::session() const { return _session; } // set costs back to zero ( typically once per month) void AccountingBase::resetCosts(const QString & accountname){ QString prev_account = gpppdata.accname(); gpppdata.setAccount(accountname); gpppdata.setTotalCosts(""); gpppdata.setAccount(prev_account); } void AccountingBase::resetVolume(const QString & accountname){ QString prev_account = gpppdata.accname(); gpppdata.setAccount(accountname); gpppdata.setTotalBytes(0); gpppdata.setAccount(prev_account); } void AccountingBase::logMessage(QString s, bool newline) { int old_umask = umask(0077); QFile f(LogFileName); bool result = f.open(IO_ReadWrite); if(result) { // move to eof, and place \n if necessary if(f.size() > 0) { if(newline) { f.at(f.size()); char c = 0; f.readBlock(&c, 1); if(c != '\n') f.writeBlock("\n", 1); } else f.at(f.size()); } QCString tmp = s.local8Bit(); f.writeBlock(tmp, tmp.length()); f.close(); } // restore umask umask(old_umask); } QString AccountingBase::getCosts(const QString & accountname) { QString prev_account = gpppdata.accname(); gpppdata.setAccount(accountname); QString val = gpppdata.totalCosts(); // ### currency from rule file // QString val = KGlobal::locale()->formatMoney(gpppdata.totalCosts().toDouble(), currency); gpppdata.setAccount(prev_account); return val; } bool AccountingBase::saveCosts() { if(!_name.isNull() && _name.length() > 0) { QString val; val.setNum(total()); gpppdata.setTotalCosts(val); gpppdata.save(); return TRUE; } else return FALSE; } bool AccountingBase::loadCosts() { QString val = gpppdata.totalCosts(); if(val.isNull()) // QString will segfault if isnull and toDouble called _total = 0.0; else { bool ok; _total = val.toDouble(&ok); if(!ok) _total = 0.0; } return TRUE; } QString AccountingBase::getAccountingFile(const QString &accountname) { QString f = "kppp/Rules/"; f += accountname; QString d = locate("data", f); if(d.isNull()) return ""; else return d; } ///////////////////////////////////////////////////////////////////////////// // // Accounting class for ruleset files // ///////////////////////////////////////////////////////////////////////////// Accounting::Accounting(QObject *parent, PPPStats *st) : AccountingBase(parent), acct_timer_id(0), update_timer_id(0), stats(st) { } bool Accounting::running() const { return (bool)(acct_timer_id != 0); } void Accounting::timerEvent(QTimerEvent *t) { if(t->timerId() == acct_timer_id) { double newCosts; double newLen; double connect_time = difftime(time(0), start_time); rules.getActiveRule(QDateTime::currentDateTime(), connect_time, newCosts, newLen); if(newLen < 1) { // changed to < 1 slotStop(); return; // no default rule found } // check if we have a new rule. If yes, // kill the timer and restart it with new // duration if((newCosts != _lastcosts) || (newLen != _lastlen)) { kdDebug(5002).form("SWITCHING RULES, new costs = %0.2f, new len = %0.2f\n", newCosts, newLen); killTimer(acct_timer_id); if(newLen > 0) acct_timer_id = startTimer((int)(newLen * 1000.0)); _lastlen = newLen; _lastcosts = newCosts; } // emit changed() signal if necessary if(newCosts != 0) { _session += newCosts; emit changed(rules.currencyString(total()), rules.currencyString(session())); } } // if(t->timerId() == acct_timer_id)... if(t->timerId() == update_timer_id) { // just to be sure, save the current costs // every n seconds (see UPDATE_TIME) saveCosts(); } } void Accounting::slotStart() { if(!running()) { loadCosts(); _lastcosts = 0.0; _lastlen = 0.0; _session = rules.perConnectionCosts(); rules.setStartTime(QDateTime::currentDateTime()); acct_timer_id = startTimer(1); if(UPDATE_TIME > 0) update_timer_id = startTimer(UPDATE_TIME); start_time = time(0); QString s; s = timet2qstring(start_time); s += ":"; s += gpppdata.accname(); s += ":"; s += rules.currencySymbol(); logMessage(s, TRUE); } } void Accounting::slotStop() { if(running()) { killTimer(acct_timer_id); if(update_timer_id != 0) killTimer(update_timer_id); acct_timer_id = 0; update_timer_id = 0; QString s; s.sprintf(":%s:%0.4e:%0.4e:%u:%u\n", timet2qstring(time(0)).utf8().data(), session(), total(), stats->ibytes, stats->obytes); logMessage(s, FALSE); saveCosts(); } } bool Accounting::loadRuleSet(const QString & name) { if (name.isEmpty()) { rules.load(""); // delete old rules return TRUE; } QString d = AccountingBase::getAccountingFile(name); QFileInfo fg(d); if(fg.exists()) { int ret = rules.load(d); _name = rules.name(); return (bool)(ret == 0); } return FALSE; } double Accounting::total() const { if(rules.minimumCosts() <= _session) return _total + _session; else return _total + rules.minimumCosts(); } double Accounting::session() const { if(rules.minimumCosts() <= _session) return _session; else return rules.minimumCosts(); } ExecutableAccounting::ExecutableAccounting(PPPStats *st, QObject *parent) : AccountingBase(parent), proc(0), stats(st) { } bool ExecutableAccounting::running() const { return (proc != 0) || proc->isRunning(); } bool ExecutableAccounting::loadRuleSet(const QString &) { QString s = AccountingBase::getAccountingFile(gpppdata.accountingFile()); return (access(QFile::encodeName(s), X_OK) == 0); } void ExecutableAccounting::gotData(KProcess */*proc*/, char *buffer, int /*buflen*/) { QString field[8]; int nFields = 0; int pos, last_pos = 0; // split string QString b(buffer); pos = b.find(':'); while(pos != -1 && nFields < 8) { field[nFields++] = b.mid(last_pos, pos-last_pos); last_pos = pos+1; pos = b.find(':', last_pos); } for(int i = 0; i < nFields;i++) fprintf(stderr, "FIELD[%d] = %s\n", i, field[i].local8Bit().data()); QString __total, __session; QString s(buffer); int del1, del2, del3; del1 = s.find(':'); del2 = s.find(':', del1+1); del3 = s.find(':', del2+1); if(del1 == -1 || del2 == -1 || del3 == -1) { // TODO: do something usefull here return; } provider = s.left(del1); currency = s.mid(del1, del2-del1); __total = s.mid(del2, del2-del1); __session = s.mid(del3, s.length()-del3+1); bool ok1, ok2; _total = __total.toDouble(&ok1); _session = __session.toDouble(&ok2); if(!ok1 || !ok2) { // TODO: do something usefull here return; } printf("PROVIDER=%s, CURRENCY=%s, TOTAL=%0.3e, SESSION=%0.3e\n", provider.local8Bit().data(), currency.local8Bit().data(), _total, _session); } void ExecutableAccounting::slotStart() { if(proc != 0) slotStop(); // just to make sure loadCosts(); QString s = AccountingBase::getAccountingFile(gpppdata.accountingFile()); proc = new KProcess; QString s_total; s_total.sprintf("%0.8f", total()); *proc << s << s_total; connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(gotData(KProcess *, char *, int))); proc->start(); time_t start_time = time(0); s = timet2qstring(start_time); s += ":"; s += gpppdata.accname(); s += ":"; s += currency; logMessage(s, TRUE); } void ExecutableAccounting::slotStop() { if(proc != 0) { proc->kill(); delete proc; proc = 0; QString s; s.sprintf(":%s:%0.4e:%0.4e:%u:%u\n", timet2qstring(time(0)).local8Bit().data(), session(), total(), stats->ibytes, stats->obytes); logMessage(s, FALSE); saveCosts(); } } #include "accounting.moc"