summaryrefslogtreecommitdiffstats
path: root/kppp/accounting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kppp/accounting.cpp')
-rw-r--r--kppp/accounting.cpp477
1 files changed, 477 insertions, 0 deletions
diff --git a/kppp/accounting.cpp b/kppp/accounting.cpp
new file mode 100644
index 00000000..36751190
--- /dev/null
+++ b/kppp/accounting.cpp
@@ -0,0 +1,477 @@
+/* -*- 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, <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 <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <qdir.h>
+
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <time.h>
+
+#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"
+