Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
smradius
smradius
Commits
029e240b
Commit
029e240b
authored
Sep 25, 2016
by
Nigel Kukard
Browse files
Merge branch 'nk-autotopup' into 'master'
FEATURE: Auto-topup See merge request
!419
parents
9440002c
4297ea5c
Pipeline
#290
passed with stages
in 3 minutes and 17 seconds
Changes
14
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
.gitlab-ci.yml
View file @
029e240b
...
...
@@ -27,7 +27,7 @@ make-test:
-
apt-get install -y git make
-
apt-get install -y libdevel-cover-perl libpod-coverage-perl libtest-most-perl
-
apt-get install -y libnet-server-perl libconfig-inifiles-perl libdatetime-perl libcache-fastmmap-perl libtimedate-perl
libcrypt-des-perl libcrypt-rc4-perl libdigest-sha-perl libdigest-md4-perl
libcrypt-des-perl libcrypt-rc4-perl libdigest-sha-perl libdigest-md4-perl
libmime-lite-perl
-
apt-get install -y mysql-server
# Start services and create dirs we need
...
...
database/convert-tsql
deleted
100755 → 0
View file @
9440002c
#!/bin/bash
# Database translation/creation script
# Copyright (C) 2009-2016, AllWorldIT
# Copyright (C) 2008, LinuxRulz
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU 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.
database
=
"
$1
"
file
=
"
$2
"
prefix
=
"
$3
"
# Display usage info
display_usage
()
{
echo
"Usage:
$0
<database type> <file> [prefix]"
echo
echo
"Valid database types:"
echo
" mysql - For MySQL v5.5+"
echo
" pgsql - For PostgreSQL"
echo
" sqlite - For SQLite v3"
echo
exit
}
# Check we have our params
if
[
-z
"
$database
"
-o
-z
"
$file
"
]
then
display_usage
fi
# Check file exists
if
[
!
-f
"
$file
"
]
then
echo
"ERROR: Cannot open file '
$file
'"
exit
1
fi
# Check what we converting for
case
"
$database
"
in
"mysql"
)
sed
\
-e
"s/@PREFIX@/
$prefix
/g"
\
-e
's/@PRELOAD@/SET FOREIGN_KEY_CHECKS=0;/'
\
-e
's/@POSTLOAD@/SET FOREIGN_KEY_CHECKS=1;/'
\
-e
's/@CREATE_TABLE_SUFFIX@/ENGINE=InnoDB CHARACTER SET latin1 COLLATE latin1_bin/'
\
-e
's/@SERIAL_TYPE@/SERIAL/'
\
-e
's/@BIGINT_UNSIGNED@/BIGINT UNSIGNED/'
\
-e
's/@INT_UNSIGNED@/INT UNSIGNED/'
\
-e
's/@TRACK_KEY_LEN@/512/'
\
-e
's/@SERIAL_REF_TYPE@/BIGINT UNSIGNED/'
<
"
$file
"
;;
"pgsql"
)
sed
\
-e
"s/@PREFIX@/
$prefix
/g"
\
-e
's/@PRELOAD@/SET CONSTRAINTS ALL DEFERRED;/'
\
-e
's/@POSTLOAD@//'
\
-e
's/@CREATE_TABLE_SUFFIX@//'
\
-e
's/@SERIAL_TYPE@/SERIAL PRIMARY KEY/'
\
-e
's/@BIGINT_UNSIGNED@/INT8/'
\
-e
's/@INT_UNSIGNED@/INT8/'
\
-e
's/@TRACK_KEY_LEN@/512/'
\
-e
's/@SERIAL_REF_TYPE@/INT8/'
<
"
$file
"
;;
"sqlite"
)
sed
\
-e
"s/@PREFIX@/
$prefix
/g"
\
-e
's/@PRELOAD@//'
\
-e
's/@POSTLOAD@//'
\
-e
's/@CREATE_TABLE_SUFFIX@//'
\
-e
's/@SERIAL_TYPE@/INTEGER PRIMARY KEY AUTOINCREMENT/'
\
-e
's/@BIGINT_UNSIGNED@/INT8/'
\
-e
's/@INT_UNSIGNED@/INT8/'
\
-e
's/@TRACK_KEY_LEN@/512/'
\
-e
's/@SERIAL_REF_TYPE@/INT8/'
<
"
$file
"
;;
*
)
echo
"ERROR: Invalid database type '
$database
'"
exit
1
;;
esac
lib/smradius/attributes.pm
View file @
029e240b
...
...
@@ -68,7 +68,19 @@ my @attributeReplyIgnoreList = (
'
SMRadius-Username-Transform
',
'
SMRadius-Evaluate
',
'
SMRadius-Peer-Address
',
'
SMRadius-Disable-WebUITopup
'
'
SMRadius-Disable-WebUITopup
',
'
SMRadius-AutoTopup-Traffic-Enabled
',
'
SMRadius-AutoTopup-Traffic-Amount
',
'
SMRadius-AutoTopup-Traffic-Limit
',
'
SMRadius-AutoTopup-Traffic-Notify
',
'
SMRadius-AutoTopup-Traffic-NotifyTemplate
',
'
SMRadius-AutoTopup-Traffic-Threshold
',
'
SMRadius-AutoTopup-Uptime-Enabled
',
'
SMRadius-AutoTopup-Uptime-Amount
',
'
SMRadius-AutoTopup-Uptime-Limit
',
'
SMRadius-AutoTopup-Uptime-Notify
',
'
SMRadius-AutoTopup-Uptime-NotifyTemplate
',
'
SMRadius-AutoTopup-Uptime-Threshold
',
);
my
@attributeVReplyIgnoreList
=
(
);
...
...
lib/smradius/daemon.pm
View file @
029e240b
...
...
@@ -80,6 +80,12 @@ if (!eval {require Cache::FastMmap; 1;}) {
eval
{
use
AWITPT::
Cache
;};
}
# Check MIME::Lite is installed
if
(
!
eval
{
require
MIME::
Lite
;
1
;})
{
print
STDERR
"
You're missing MIME::Lite, try 'apt-get install libmime-lite-perl'
\n
";
exit
1
;
}
## no critic (BuiltinFunctions::ProhibitStringyEval)
eval
qq{
...
...
@@ -817,15 +823,6 @@ sub process_request {
}
}
# Tell the NAS we got its packet
my
$resp
=
smradius::Radius::
Packet
->
new
(
$self
->
{'
radius
'}
->
{'
dictionary
'});
$resp
->
set_code
('
Accounting-Response
');
$resp
->
set_identifier
(
$pkt
->
identifier
);
$resp
->
set_authenticator
(
$pkt
->
authenticator
);
$server
->
{'
client
'}
->
send
(
auth_resp
(
$resp
->
pack
,
getAttributeValue
(
$user
->
{'
ConfigAttributes
'},"
SMRadius-Config-Secret
"))
);
# Are we going to POD the user?
my
$PODUser
=
0
;
...
...
@@ -860,6 +857,15 @@ sub process_request {
}
}
# Tell the NAS we got its packet
my
$resp
=
smradius::Radius::
Packet
->
new
(
$self
->
{'
radius
'}
->
{'
dictionary
'});
$resp
->
set_code
('
Accounting-Response
');
$resp
->
set_identifier
(
$pkt
->
identifier
);
$resp
->
set_authenticator
(
$pkt
->
authenticator
);
$server
->
{'
client
'}
->
send
(
auth_resp
(
$resp
->
pack
,
getAttributeValue
(
$user
->
{'
ConfigAttributes
'},"
SMRadius-Config-Secret
"))
);
# Build a list of our attributes in the packet
my
$acctAttributes
;
foreach
my
$attr
(
$pkt
->
attributes
)
{
...
...
lib/smradius/modules/accounting/mod_accounting_sql.pm
View file @
029e240b
...
...
@@ -355,7 +355,7 @@ sub getUsage
# Fetch data
my
$sth
=
DBSelect
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
E
rror
());
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
e
rror
());
return
;
}
...
...
@@ -463,7 +463,7 @@ sub acct_log
# Fetch previous records of the same session
my
$sth
=
DBSelect
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
E
rror
());
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
e
rror
());
return
;
}
...
...
@@ -555,7 +555,7 @@ sub acct_log
$sth
=
DBDo
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Failed to update accounting ALIVE record:
"
.
AWITPT::DB::DBLayer::
E
rror
());
AWITPT::DB::DBLayer::
e
rror
());
return
MOD_RES_NACK
;
}
...
...
@@ -584,7 +584,7 @@ sub acct_log
my
$sth
=
DBDo
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Failed to insert accounting START record:
"
.
AWITPT::DB::DBLayer::
E
rror
());
AWITPT::DB::DBLayer::
e
rror
());
return
MOD_RES_NACK
;
}
# Update first login?
...
...
@@ -611,7 +611,7 @@ sub acct_log
# Update database (status)
my
$sth
=
DBDo
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Failed to update accounting STOP record: %s
",
AWITPT::DB::DBLayer::
E
rror
());
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Failed to update accounting STOP record: %s
",
AWITPT::DB::DBLayer::
e
rror
());
return
MOD_RES_NACK
;
}
}
...
...
@@ -633,7 +633,7 @@ sub fixDuplicates
# Select duplicates
my
$sth
=
DBSelect
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
E
rror
());
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
e
rror
());
return
;
}
...
...
@@ -656,7 +656,7 @@ sub fixDuplicates
# Delete duplicates
$sth
=
DBDo
(
@dbDoParams
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
E
rror
());
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Database query failed: %s
",
AWITPT::DB::DBLayer::
e
rror
());
DBRollback
();
return
;
}
...
...
@@ -704,7 +704,7 @@ sub cleanup
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Cleanup => Failed to delete accounting summary record:
"
.
AWITPT::DB::DBLayer::
E
rror
());
AWITPT::DB::DBLayer::
e
rror
());
DBRollback
();
return
;
}
...
...
@@ -729,7 +729,7 @@ sub cleanup
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Cleanup => Failed to select accounting record:
"
.
AWITPT::DB::DBLayer::
E
rror
());
AWITPT::DB::DBLayer::
e
rror
());
return
;
}
...
...
@@ -836,7 +836,7 @@ sub cleanup
);
if
(
!
$sth
)
{
$server
->
log
(
LOG_ERR
,"
[MOD_ACCOUNTING_SQL] Cleanup => Failed to create accounting summary record:
"
.
AWITPT::DB::DBLayer::
E
rror
());
AWITPT::DB::DBLayer::
e
rror
());
DBRollback
();
return
;
}
...
...
lib/smradius/modules/features/mod_feature_capping.pm
View file @
029e240b
...
...
@@ -27,7 +27,9 @@ use smradius::logging;
use
smradius::
util
;
use
AWITPT::
Util
;
use
POSIX
qw(floor)
;
use
List::
Util
qw( min )
;
use
MIME::
Lite
;
use
POSIX
qw( floor )
;
# Load exporter
...
...
@@ -59,9 +61,6 @@ my $UPTIME_LIMIT_ATTRIBUTE = 'SMRadius-Capping-Uptime-Limit';
my
$TRAFFIC_TOPUP_ATTRIBUTE
=
'
SMRadius-Capping-Traffic-Topup
';
my
$TIME_TOPUP_ATTRIBUTE
=
'
SMRadius-Capping-Uptime-Topup
';
my
$TRAFFIC_AUTOTOPUP_ATTRIBUTE
=
'
SMRadius-Capping-Traffic-AutoTopup
';
my
$TIME_AUTOTOPUP_ATTRIBUTE
=
'
SMRadius-Capping-Uptime-AutoTopup
';
my
$config
;
...
...
@@ -137,19 +136,15 @@ sub post_auth_hook
# Get valid traffic and uptime topups
#
# Check if there was any data returned at all
my
$uptimeTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TIME_TOPUP_ATTRIBUTE
)
//
0
;
my
$trafficTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TRAFFIC_TOPUP_ATTRIBUTE
)
//
0
;
my
$uptimeAutoTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TIME_AUTOTOPUP_ATTRIBUTE
)
//
0
;
my
$trafficAutoTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TRAFFIC_AUTOTOPUP_ATTRIBUTE
)
//
0
;
#
# Set the new uptime and traffic limits (limit, if any.. + topups)
#
# Uptime..
# // is a defined operator, $a ? defined($a) : $b
my
$uptimeLimitWithTopups
=
(
$uptimeLimit
//
0
)
+
$uptimeTopupAmount
;
...
...
@@ -159,18 +154,35 @@ sub post_auth_hook
my
$trafficLimitWithTopups
=
(
$trafficLimit
//
0
)
+
$trafficTopupAmount
;
#
# Do auto-topups for both traffic and uptime
#
my
$autoTopupTrafficAdded
=
_doAutoTopup
(
$server
,
$user
,
$accountingUsage
->
{'
TotalDataUsage
'},"
traffic
",
$trafficLimitWithTopups
,
1
);
if
(
defined
(
$autoTopupTrafficAdded
))
{
$trafficLimitWithTopups
+=
$autoTopupTrafficAdded
;
}
my
$autoTopupUptimeAdded
=
_doAutoTopup
(
$server
,
$user
,
$accountingUsage
->
{'
TotalSessionTime
'},"
uptime
",
$uptimeLimitWithTopups
,
2
);
if
(
defined
(
$autoTopupUptimeAdded
))
{
$uptimeLimitWithTopups
+=
$autoTopupUptimeAdded
;
}
#
# Display our usages
#
_logUptimeUsage
(
$server
,
$accountingUsage
,
$uptimeLimit
,
$uptimeTopupAmount
);
_logTrafficUsage
(
$server
,
$accountingUsage
,
$trafficLimit
,
$trafficTopupAmount
);
_logUsage
(
$server
,
$accountingUsage
->
{'
TotalDataUsage
'},
$uptimeLimit
,
$uptimeTopupAmount
,'
traffic
');
_logUsage
(
$server
,
$accountingUsage
->
{'
TotalSessionTime
'},
$uptimeLimit
,
$uptimeTopupAmount
,'
uptime
');
#
# Add conditional variables
#
# Add attribute conditionals BEFORE override
addAttributeConditionalVariable
(
$user
,"
SMRadius_Capping_TotalDataUsage
",
$accountingUsage
->
{'
TotalDataUsage
'});
addAttributeConditionalVariable
(
$user
,"
SMRadius_Capping_TotalSessionTime
",
$accountingUsage
->
{'
TotalSessionTime
'});
...
...
@@ -334,8 +346,6 @@ sub post_acct_hook
# Check if there was any data returned at all
my
$uptimeTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TIME_TOPUP_ATTRIBUTE
)
//
0
;
my
$trafficTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TRAFFIC_TOPUP_ATTRIBUTE
)
//
0
;
my
$uptimeAutoTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TIME_AUTOTOPUP_ATTRIBUTE
)
//
0
;
my
$trafficAutoTopupAmount
=
_getConfigAttributeNumeric
(
$server
,
$user
,
$TRAFFIC_AUTOTOPUP_ATTRIBUTE
)
//
0
;
#
...
...
@@ -350,12 +360,30 @@ sub post_acct_hook
# // is a defined operator, $a ? defined($a) : $b
my
$trafficLimitWithTopups
=
(
$trafficLimit
//
0
)
+
$trafficTopupAmount
;
#
# Do auto-topups for both traffic and uptime
#
my
$autoTopupTrafficAdded
=
_doAutoTopup
(
$server
,
$user
,
$accountingUsage
->
{'
TotalDataUsage
'},"
traffic
",
$trafficLimitWithTopups
,
1
);
if
(
defined
(
$autoTopupTrafficAdded
))
{
$trafficLimitWithTopups
+=
$autoTopupTrafficAdded
;
}
my
$autoTopupUptimeAdded
=
_doAutoTopup
(
$server
,
$user
,
$accountingUsage
->
{'
TotalSessionTime
'},"
uptime
",
$uptimeLimitWithTopups
,
2
);
if
(
defined
(
$autoTopupUptimeAdded
))
{
$uptimeLimitWithTopups
+=
$autoTopupUptimeAdded
;
}
#
# Display our usages
#
_logU
ptimeU
sage
(
$server
,
$accountingUsage
,
$uptimeLimit
,
$uptimeTopupAmount
);
_log
Traffic
Usage
(
$server
,
$accountingUsage
,
$trafficLimit
,
$traffic
TopupAmount
);
_logUsage
(
$server
,
$accountingUsage
->
{'
TotalDataUsage
'}
,
$uptimeLimit
,
$uptimeTopupAmount
,'
traffic
'
);
_logUsage
(
$server
,
$accountingUsage
->
{'
TotalSessionTime
'},
$uptimeLimit
,
$uptime
TopupAmount
,'
uptime
'
);
#
...
...
@@ -476,62 +504,30 @@ sub _getAccountingUsage
## @internal
# Code snippet to log our uptime usage
sub
_logU
ptimeU
sage
sub
_logUsage
{
my
(
$server
,
$accountingUsage
,
$
uptimeLimit
,
$uptimeT
opupAmount
)
=
@_
;
my
(
$server
,
$accountingUsage
,
$
limit
,
$t
opupAmount
,
$type
)
=
@_
;
# Check if our limit is defined
if
(
!
defined
(
$uptimeLimit
))
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Uptime => Usage total:
"
.
$accountingUsage
->
{'
TotalSessionTime
'}
.
"
min (Limit: Prepaid, Topups:
"
.
$uptimeTopupAmount
.
"
min)
");
return
;
}
# If so, check if its > 0, which would depict its capped
if
(
$uptimeLimit
>
0
)
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Uptime => Usage total:
"
.
$accountingUsage
->
{'
TotalSessionTime
'}
.
"
min (Limit:
"
.
$uptimeLimit
.
"
min, Topups:
"
.
$uptimeTopupAmount
.
"
min)
");
}
else
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Uptime => Usage total:
"
.
$accountingUsage
->
{'
TotalSessionTime
'}
.
"
min (Limit: none, Topups:
"
.
$uptimeTopupAmount
.
"
min)
");
}
return
;
}
## @internal
# Code snippet to log our traffic usage
sub
_logTrafficUsage
{
my
(
$server
,
$accountingUsage
,
$trafficLimit
,
$trafficTopupAmount
)
=
@_
;
my
$typeKey
=
ucfirst
(
$type
);
# Check if our limit is defined
if
(
!
defined
(
$trafficLimit
))
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Bandwidth => Usage total:
"
.
$accountingUsage
->
{'
TotalDataUsage
'}
.
"
Mbyte (Limit: Prepaid, Topups:
"
.
$trafficTopupAmount
.
"
Mbyte)
");
return
;
}
# If so, check if its > 0, which would depict its capped
if
(
$trafficLimit
>
0
)
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Bandwidth => Usage total:
"
.
$accountingUsage
->
{'
TotalDataUsage
'}
.
"
Mbyte (Limit:
"
.
$trafficLimit
.
"
Mbyte, Topups:
"
.
$trafficTopupAmount
.
"
Mbyte)
");
if
(
defined
(
$limit
)
&&
!
$limit
)
{
$limit
=
'
-none-
';
}
else
{
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] Bandwidth => Usage total:
"
.
$accountingUsage
->
{'
TotalDataUsage
'}
.
"
Mbyte (Limit: none, Topups:
"
.
$trafficTopupAmount
.
"
Mbyte)
");
$limit
=
'
-topup-
';
}
$server
->
log
(
LOG_INFO
,"
[MOD_FEATURE_CAPPING] Capping information [type: %s, total: %s, limit: %s, topups: %s]
",
$type
,
$accountingUsage
,
$limit
,
$topupAmount
);
return
;
}
## @internal
# Function snippet to return a
numeric configuration
attribute
# Function snippet to return a
user
attribute
sub
_getConfigAttributeNumeric
{
my
(
$server
,
$user
,
$attributeName
)
=
@_
;
...
...
@@ -559,5 +555,255 @@ sub _getConfigAttributeNumeric
## @internal
# Function snippet to return a attribute
sub
_getAttribute
{
my
(
$server
,
$user
,
$attributeName
)
=
@_
;
# Check the attribute exists
return
if
(
!
defined
(
$user
->
{'
Attributes
'}
->
{
$attributeName
}));
$server
->
log
(
LOG_DEBUG
,"
[MOD_FEATURE_CAPPING] User attribute '
"
.
$attributeName
.
"
' is defined
");
# Check the required operator is present in this case :=
if
(
!
defined
(
$user
->
{'
Attributes
'}
->
{
$attributeName
}
->
{'
:=
'}))
{
$server
->
log
(
LOG_NOTICE
,"
[MOD_FEATURE_CAPPING] User attribute '
"
.
$attributeName
.
"
' has no ':=' operator
");
return
;
}
# Check the operator value is defined...
if
(
!
defined
(
$user
->
{'
Attributes
'}
->
{
$attributeName
}
->
{'
:=
'}
->
{'
Value
'}))
{
$server
->
log
(
LOG_NOTICE
,"
[MOD_FEATURE_CAPPING] User attribute '
"
.
$attributeName
.
"
' has no value
");
return
;
}
return
$user
->
{'
Attributes
'}
->
{
$attributeName
}
->
{'
:=
'}
->
{'
Value
'};
}
## @internal
# Function which impelments our auto-topup functionality
sub
_doAutoTopup
{
my
(
$server
,
$user
,
$accountingUsage
,
$type
,
$usageLimit
,
$topupType
)
=
@_
;
my
$scfg
=
$server
->
{'
inifile
'};
# Get the key, which has the first letter uppercased
my
$typeKey
=
ucfirst
(
$type
);
# Booleanize the attribute and check if its enabled
if
(
my
$enabled
=
booleanize
(
_getAttribute
(
$server
,
$user
,"
SMRadius-AutoTopup-
$typeKey
-Enabled
")))
{
$server
->
log
(
LOG_INFO
,'
[MOD_FEATURE_CAPPING] AutoToups for %s is enabled
',
$type
);
}
else
{
$server
->
log
(
LOG_DEBUG
,'
[MOD_FEATURE_CAPPING] AutoToups for %s is not enabled
',
$type
);
return
;
}
# Do sanity checks on the auto-topup amount
my
$autoTopupAmount
=
_getAttribute
(
$server
,
$user
,"
SMRadius-AutoTopup-
$typeKey
-Amount
");
if
(
!
defined
(
$autoTopupAmount
))
{
$server
->
log
(
LOG_WARN
,'
[MOD_FEATURE_CAPPING] SMRadius-AutoToup-%s-Amount must have a value
',
$typeKey
);
return
;
}
if
(
!
isNumber
(
$autoTopupAmount
)){
$server
->
log
(
LOG_WARN
,'
[MOD_FEATURE_CAPPING] SMRadius-AutoToup-%s-Amount must be a number and be > 0, instead it was
'
.
'
\'
%s
\'
, IGNORING SMRadius-AutoTopup-%s-Enabled
',
$typeKey
,
$autoTopupAmount
,
$typeKey
);
return
;
}
# Do sanity checks on the auto-topup threshold
my
$autoTopupThreshold
=
_getAttribute
(
$server
,
$user
,"
SMRadius-AutoTopup-
$typeKey
-Threshold
");
if
(
defined
(
$autoTopupThreshold
)
&&
!
isNumber
(
$autoTopupThreshold
)){
$server
->
log
(
LOG_WARN
,'
[MOD_FEATURE_CAPPING] SMRadius-AutoToup-%s-Threshold must be a number and be > 0, instead it was
'
.
'
\'
%s
\'
, IGNORING SMRadius-AutoTopup-%s-Threshold
',
$typeKey
,
$autoTopupAmount
,
$typeKey
);
$autoTopupThreshold
=
undef
;
}
# Check that if the auto-topup limit is defined, that it is > 0
my
$autoTopupLimit
=
_getAttribute
(
$server
,
$user
,"
SMRadius-AutoTopup-
$typeKey
-Limit
");
if
(
defined
(
$autoTopupLimit
)
&&
!
isNumber
(
$autoTopupLimit
))
{
$server
->
log
(
LOG_WARN
,'
[MOD_FEATURE_CAPPING] SMRadius-AutoToup-%s-Limit must be a number and be > 0, instead it was
'
.
'
\'
%s
\'
, IGNORING SMRadius-AutoTopup-%s-Enabled
',
$typeKey
,
$autoTopupAmount
,
$typeKey
);
return
;
}
# Pull in ahow many auto-topups were already added
my
$autoTopupsAdded
=
_getConfigAttributeNumeric
(
$server
,
$user
,"
SMRadius-Capping-
$typeKey
-AutoTopup
")
//
0
;
# Default to an auto-topup threshold of the topup amount divided by two if none has been provided
$autoTopupThreshold
//
=
floor
(
$autoTopupAmount
/
2
);
# Check if we're still within our usage limit
return
if
(
$accountingUsage
+
$autoTopupThreshold
<
$usageLimit
+
$autoTopupsAdded
);
# Check the difference between our accounting usage and our usage limit
my
$usageDelta
=
$accountingUsage
-
$usageLimit
;
# Make sure our delta is at least 0
$usageDelta
=
0
if
(
$usageDelta
<
0
);
# Calculate how many topups are needed
my
$autoTopupsRequired
=
floor
(
$usageDelta
/
$autoTopupAmount
)
+
1
;
# Default the topups to add to the number required
my
$autoTopupsToAdd
=
$autoTopupsRequired
;
# If we have an auto-topup limit, recalculate how many we must add... maybe it exceeds
if
(
defined
(
$autoTopupLimit
))
{
my
$autoTopupsAllowed
=
floor
((
$autoTopupLimit
-
$autoTopupsAdded
)
/
$autoTopupAmount
);
$autoTopupsToAdd
=
min
(
$autoTopupsRequired
,
$autoTopupsAllowed
);
# We cannot add a negative amount of auto-topups, if we have a negative amount, we have hit our limit
$autoTopupsToAdd
=
0
if
(
$autoTopupsToAdd
<
0
);
}
# Total topup amount
my
$autoTopupsToAddAmount
=
$autoTopupsToAdd
*
$autoTopupAmount
;
# The datetime now
my
$now
=
DateTime
->
now
->
set_time_zone
(
$server
->
{'
smradius
'}
->
{'
event_timezone
'});
# Use truncate to set all values after 'month' to their default values
my
$thisMonth
=
$now
->
clone
()
->
truncate
(
to
=>
"
month
"
);
# This month, in string form
my
$thisMonth_str
=
$thisMonth
->
strftime
("
%Y-%m-%d
");
# Next month..
my
$nextMonth
=
$thisMonth
->
clone
()
->
add
(
months
=>
1
);
my
$nextMonth_str
=
$nextMonth
->
strftime
("
%Y-%m-%d
");
# Lets see if a module accepts to add a topup
my
$res
;
foreach
my
$module
(
@
{
$server
->
{'
module_list
'}})
{
# Do we have the correct plugin?
if
(
defined
(
$module
->
{'
Feature_Config_Topop_add
'}))
{
$server
->
log
(
LOG_INFO
,"
[MOD_FEATURE_CAPPING] Found plugin: '
"
.
$module
->
{'
Name
'}
.
"
'
");
# Try add topup
$res
=
$module
->
{'
Feature_Config_Topop_add
'}(
$server
,
$user
,
$thisMonth_str
,
$nextMonth_str
,
(
$topupType
|
4
),
$autoTopupAmount
);
# Skip to end if we added a topup
if
(
$res
==
MOD_RES_ACK
)
{
my
$topupsRemaining
=
$autoTopupsToAdd
-
1
;
while
(
$topupsRemaining
>
0
)
{
# Try add another topup
$res
=
$module
->
{'
Feature_Config_Topop_add
'}(
$server
,
$user
,
$thisMonth_str
,
$nextMonth_str
,
(
$topupType
|
4
),
$autoTopupAmount
);
$topupsRemaining
--
;
}
last
;
}
}
}
# If not, return undef
if
(
!
defined
(
$res
)
||
$res
!=
MOD_RES_ACK
)
{
$server
->
log
(
LOG_WARN
,'
[MOD_FEATURE_CAPPING] Auto-Topup(s) cannot be added, no module replied with ACK