30d21d9316dd2563e3f728839ee1cb688fe4594f
[freeside.git] / FS / FS / session.pm
1 package FS::session;
2
3 use strict;
4 use vars qw( @ISA $conf $start $stop );
5 use FS::Record qw( qsearchs );
6 use FS::svc_acct;
7 use FS::port;
8 use FS::nas;
9
10 @ISA = qw(FS::Record);
11
12 $FS::UID::callback{'FS::session'} = sub {
13   $conf = new FS::Conf;
14   $start = $conf->exists('session-start') ? $conf->config('session-start') : '';
15   $stop = $conf->exists('session-stop') ? $conf->config('session-stop') : '';
16 };
17
18 =head1 NAME
19
20 FS::session - Object methods for session records
21
22 =head1 SYNOPSIS
23
24   use FS::session;
25
26   $record = new FS::session \%hash;
27   $record = new FS::session {
28     'portnum' => 1,
29     'svcnum'  => 2,
30     'login'   => $timestamp,
31     'logout'  => $timestamp,
32   };
33
34   $error = $record->insert;
35
36   $error = $new_record->replace($old_record);
37
38   $error = $record->delete;
39
40   $error = $record->check;
41
42   $error = $record->nas_heartbeat($timestamp);
43
44 =head1 DESCRIPTION
45
46 An FS::session object represents an user login session.  FS::session inherits
47 from FS::Record.  The following fields are currently supported:
48
49 =over 4
50
51 =item sessionnum - primary key
52
53 =item portnum - NAS port for this session - see L<FS::port>
54
55 =item svcnum - User for this session - see L<FS::svc_acct>
56
57 =item login - timestamp indicating the beginning of this user session.
58
59 =item logout - timestamp indicating the end of this user session.  May be null,
60                which indicates a currently open session.
61
62 =back
63
64 =head1 METHODS
65
66 =over 4
67
68 =item new HASHREF
69
70 Creates a new session.  To add the session to the database, see L<"insert">.
71
72 Note that this stores the hash reference, not a distinct copy of the hash it
73 points to.  You can ask the object for a copy with the I<hash> method.
74
75 =cut
76
77 # the new method can be inherited from FS::Record, if a table method is defined
78
79 sub table { 'session'; }
80
81 =item insert
82
83 Adds this record to the database.  If there is an error, returns the error,
84 otherwise returns false.  If the `login' field is empty, it is replaced with
85 the current time.
86
87 =cut
88
89 sub insert {
90   my $self = shift;
91   my $error;
92
93   local $SIG{HUP} = 'IGNORE';
94   local $SIG{INT} = 'IGNORE';
95   local $SIG{QUIT} = 'IGNORE';
96   local $SIG{TERM} = 'IGNORE';
97   local $SIG{TSTP} = 'IGNORE';
98   local $SIG{PIPE} = 'IGNORE';
99
100   $error = $self->check;
101   return $error if $error;
102
103   return "a session on that port is already open!"
104     if qsearchs('session', { 'portnum' => $self->portnum, 'logout' => '' } );
105
106   $self->setfield('login', time()) unless $self->getfield('login');
107
108   $error = $self->SUPER::insert;
109   return $error if $error;
110
111   $self->nas_heartbeat($self->getfield('login'));
112
113   #session-starting callback
114     #redundant with heartbeat, yuck
115   my $port = qsearchs('port',{'portnum'=>$self->portnum});
116   my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
117     #kcuy
118   my( $ip, $nasip, $nasfqdn ) = ( $port->ip, $nas->nasip, $nas->nasfqdn );
119   system( eval qq("$start") ) if $start;
120
121   '';
122
123 }
124
125 =item delete
126
127 Delete this record from the database.
128
129 =cut
130
131 # the delete method can be inherited from FS::Record
132
133 =item replace OLD_RECORD
134
135 Replaces the OLD_RECORD with this one in the database.  If there is an error,
136 returns the error, otherwise returns false.  If the `logout' field is empty,
137 it is replaced with the current time.
138
139 =cut
140
141 sub replace {
142   my($self, $old) = @_;
143   my $error;
144
145   local $SIG{HUP} = 'IGNORE';
146   local $SIG{INT} = 'IGNORE';
147   local $SIG{QUIT} = 'IGNORE';
148   local $SIG{TERM} = 'IGNORE';
149   local $SIG{TSTP} = 'IGNORE';
150   local $SIG{PIPE} = 'IGNORE';
151
152   $error = $self->check;
153   return $error if $error;
154
155   $self->setfield('logout', time()) unless $self->getfield('logout');
156
157   $error = $self->SUPER::replace($old);
158   return $error if $error;
159
160   $self->nas_heartbeat($self->getfield('logout'));
161
162   #session-ending callback
163   #redundant with heartbeat, yuck
164   my $port = qsearchs('port',{'portnum'=>$self->portnum});
165   my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
166     #kcuy
167   my( $ip, $nasip, $nasfqdn ) = ( $port->ip, $nas->nasip, $nas->nasfqdn );
168   system( eval qq("$stop") ) if $stop;
169
170   '';
171 }
172
173 =item check
174
175 Checks all fields to make sure this is a valid session.  If there is
176 an error, returns the error, otherwise returns false.  Called by the insert
177 and replace methods.
178
179 =cut
180
181 # the check method should currently be supplied - FS::Record contains some
182 # data checking routines
183
184 sub check {
185   my $self = shift;
186   my $error =
187     $self->ut_numbern('sessionnum')
188     || $self->ut_number('portnum')
189     || $self->ut_number('svcnum')
190     || $self->ut_numbern('login')
191     || $self->ut_numbern('logout')
192   ;
193   return $error if $error;
194   return "Unknown svcnum"
195     unless qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
196   '';
197 }
198
199 =item nas_heartbeat
200
201 Heartbeats the nas associated with this session (see L<FS::nas>).
202
203 =cut
204
205 sub nas_heartbeat {
206   my $self = shift;
207   my $port = qsearchs('port',{'portnum'=>$self->portnum});
208   my $nas = qsearchs('nas',{'nasnum'=>$port->nasnum});
209   $nas->heartbeat(shift);
210 }
211
212 =item svc_acct
213
214 Returns the svc_acct record associated with this session (see L<FS::svc_acct>).
215
216 =cut
217
218 sub svc_acct {
219   my $self = shift;
220   qsearchs('svc_acct', { 'svcnum' => $self->svcnum } );
221 }
222
223 =back
224
225 =head1 VERSION
226
227 $Id: session.pm,v 1.5 2001-02-27 00:59:36 ivan Exp $
228
229 =head1 BUGS
230
231 Maybe you shouldn't be able to insert a session if there's currently an open
232 session on that port.  Or maybe the open session on that port should be flagged
233 as problematic?  autoclosed?  *sigh*
234
235 Hmm, sessions refer to current svc_acct records... probably need to constrain
236 deletions to svc_acct records such that no svc_acct records are deleted which
237 have a session (even if long-closed).
238
239 =head1 SEE ALSO
240
241 L<FS::Record>, schema.html from the base documentation.
242
243 =cut
244
245 1;
246