File Coverage

blib/lib/Google/ProvisioningAPI.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             #A class that encapsulates the Google Apps for Your Domain Provisioning API V1.0
2             #see http://code.google.com/apis/apps-for-your-domain/google_apps_provisioning_api_v1.0_reference.html
3             #(C) 2006 Johan Reinalda, johan at reinalda dot net
4             #
5             #skeleton generated with h2xs -AXc -n Google::ProvisioningAPI
6             #
7             package Google::ProvisioningAPI;
8              
9 1     1   39891 use 5.008005;
  1         4  
  1         51  
10              
11 1     1   6 use strict;
  1         2  
  1         44  
12 1     1   6 use warnings;
  1         8  
  1         56  
13 1     1   5 use vars qw($VERSION);
  1         1  
  1         67  
14              
15 1     1   6 use Carp;
  1         2  
  1         101  
16 1     1   30544 use LWP::UserAgent qw(:strict);
  1         177471  
  1         40  
17 1     1   11 use HTTP::Request qw(:strict);
  1         1  
  1         26  
18 1     1   1409 use Encode;
  1         26543  
  1         649  
19 1     1   654 use XML::Simple;
  0            
  0            
20              
21             #I don't see the need for this - JKR
22             #require Exporter;
23              
24             #NOT NEEDED FOR THIS CLASS
25             #our @ISA = qw(Exporter AutoLoader);
26              
27             # Items to export into callers namespace by default. Note: do not export
28             # names by default without a very good reason. Use EXPORT_OK instead.
29             # Do not simply export all your public functions/methods/constants.
30              
31             # This allows declaration use Google::ProvisioningAPI ':all';
32             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
33             # will save memory.
34              
35             #I don't see the need for this - JKR
36             #our %EXPORT_TAGS = ( 'all' => [ qw(
37             #
38             #) ] );
39             #
40             #our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
41             #
42             #our @EXPORT = qw(
43             #
44             #);
45              
46             our $VERSION = '0.11';
47             our $APIVersion = '1.0';
48              
49             #some constants
50             #web agent identification
51             use constant GOOGLEAGENT => 'Google_ProvisioningAPI-perl/0.11';
52              
53             #url for Google API token login
54             use constant GOOGLEHOST => 'www.google.com';
55             use constant GOOGLETOKENURL => 'https://www.google.com/accounts/ClientLogin';
56             use constant MAXTOKENAGE => 24 * 60 * 60; #24 hours, see API docs
57              
58             #base url to the Google REST API
59             use constant GOOGLEBASEURL => 'https://www.google.com/a/services/v1.0/';
60              
61             use constant SUCCESSCODE => 'Success(2000)';
62             use constant FAILURECODE => 'Failure(2001)';
63              
64             #some size constants
65             use constant MAXNAMELEN => 40;
66             use constant MAXUSERNAMELEN => 30;
67              
68              
69             # Preloaded methods go here.
70              
71             #the constructor
72             sub new
73             {
74             #parse parameters, if any
75             (@_ == 4) || croak 'Constructor takes 3 arguments: domain, admin, adminpassword';
76              
77             my $object = shift();
78             my $class = ref($object) || $object;
79              
80             my $self = {
81             #Google related variables
82             domain => shift(), #the Google hosted domain we are accessing
83             admin => shift(), #the account to use when authenticating
84             password => shift(), #the password to use when authenticating
85             refreshtoken => 0, #if set, will force a re-authentication
86             authtoken => '', #the authentication token returned from google
87             authtime => 0, #time when authentication happened; only valid for 24 hours
88             requestcontent => '', #the last http content posted to Google
89             replyheaders => '', #the http headers of the last reply
90             replycontent => '', #the http content of the last reply
91             result => {}, #the resulting hash from the last reply data as parsed by XML::Simple
92            
93             #some other variables
94             debug => 0, #when turned on, will spit out debug info to STDERR
95            
96             #some statistics that are 'read-only'
97             stats => {
98             ctime => time, #object creation time
99             rtime => 0, #time of last request
100             requests => 0, #number of API requests made
101             success => 0, #number of successes
102             logins => 0, #number of authentications performed
103             }
104             };
105             #return object
106             bless( $self, 'Google::ProvisioningAPI');
107             return $self;
108            
109             }
110              
111             #method used to (re)login to the API, either first time, or as token times out
112             sub Relogin
113             {
114            
115             #get object reference
116             my $self = shift();
117              
118             $self->dprint("Relogin called\n");
119              
120             my $retval = 0;
121            
122             #adjust stats counter
123             $self->{stats}->{logins}++;
124            
125             #clear last results
126             $self->{replyheaders} = $self->{replycontent} = '';
127             $self->{result} = {};
128            
129             # Create an LWP object to make the HTTP POST request
130             my $lwp = LWP::UserAgent->new;
131              
132             if(defined($lwp)) {
133             $lwp->agent(GOOGLEAGENT);
134             $lwp->from($self->{admin}.'@'.$self->{domain});
135             # Submit the request with values for
136             # accountType, Email and Passwd variables.
137             my $response = $lwp->post( GOOGLETOKENURL,
138             [ 'accountType' => 'HOSTED',
139             'Email' => $self->{admin}.'@'.$self->{domain},
140             'Passwd' => $self->{password}
141             ]
142             );
143             #save reply page
144             $self->{replyheaders} = $response->headers->as_string;
145             $self->{replycontent} = $response->content;
146            
147             if ($response->is_success) {
148             # Extract the authentication token from the response
149             foreach my $line (split/\n/, $response->content) {
150             #$self->dprint( "RECV'd: $line" );
151             if ($line =~ m/^SID=(.+)$/) {
152             $self->{authtoken} = $1;
153             $self->{authtime} = time;
154             $self->dprint("Token found: $self->{authtoken}\n");
155             #clear refresh
156             $self->{refreshtoken} = 0;
157             $retval = 1;
158             last;
159             }
160             }
161             }
162             else {
163             $self->dprint("Error in login: " . $response->status_line . "\n");
164             $self->{result}->{reason} = "Error in login: " . $response->status_line;
165              
166             }
167             } else {
168             $self->dprint("Error getting lwp object: $!\n");
169             $self->{result}->{reason} = "Error getting lwp object: $!";
170             }
171             return $retval;
172             }
173              
174              
175              
176             #check if we are authenticated. If not, try to re-login
177             sub IsAuthenticated {
178              
179             #get object reference
180             my $self = shift();
181              
182             if( $self->{refreshtoken} or ( (time - $self->{authtime}) > MAXTOKENAGE ) ) {
183             return $self->Relogin();
184             }
185             #we are still okay!
186             return 1;
187             }
188              
189             #generic request routine that handles most functionality
190             #requires 3 arguments: Type, Action, Body
191             #Type is the object type to action upon. ('Account', 'Alias', 'MailingList')
192             #Action is what needs to be done
193             #Body is the xml specific to the action
194             sub Request
195             {
196             my $retval = 0;
197              
198             #get object reference
199             my $self = shift();
200              
201             $self->dprint( "***REQUEST***\n");
202            
203             #clear last results
204             $self->{replyheaders} = $self->{replycontent} = '';
205             $self->{result} = {};
206            
207             if(@_ != 3) {
208             $self->{result}->{reason} = 'Invalid number of arguments to request()';
209             return 0;
210             }
211            
212             #get parameters
213             my($type,$action,$body) = @_;
214            
215             $self->dprint( "Type: $type\nAction: $action\n$body\n");
216            
217             #keep some stats
218             $self->{stats}->{requests}++;
219             $self->{stats}->{rtime} = time;
220            
221             #check if we are authenticated to google
222             if(!$self->IsAuthenticated()) {
223             $self->dprint( "Error authenticating\n");
224             return 0;
225             }
226              
227             #standard XML pre and post segments
228             my $pre = <<"EOL";
229            
230            
231             xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\">
232             $type<\/hs:type>
233             $self->{authtoken}
234             $self->{domain}
235             EOL
236              
237             my $post = '';
238              
239              
240             #create to request body
241             $body = $pre . $body . $post;
242             #properly encode it
243             $body = encode('UTF-8',$body);
244              
245             #save the request content
246             $self->{requestcontent} = $body;
247            
248             # Create an LWP object to make the HTTP POST request over
249             my($ua) = LWP::UserAgent->new;
250             if(!defined($ua)) {
251             $self->dprint("Cannot create LWP::UserAgent object: $!\n");
252             $self->{result}->{reason} = "Cannot create LWP::UserAgent object in request(): $!";
253             return $retval;
254             }
255            
256             #and create the request object where are we connecting to
257             my $url = GOOGLEBASEURL . $action;
258             $self->dprint("URL: $url\n");
259             my $req = HTTP::Request->new(POST => $url);
260             if(!defined($req)) {
261             $self->dprint("Cannot create HTTP::Request object: $!\n");
262             $self->{result}->{reason} = "Cannot create HTTP::Request object in request(): $!";
263             return $retval;
264             }
265            
266             #set some user agent variables
267             $ua->agent( GOOGLEAGENT );
268             $ua->from( '<' . $self->{admin}.'@'.$self->{domain} . '>');
269              
270             # Submit the request with values for
271             # accountType, Email and Passwd variables.
272             #$req->header('ContentType' => 'application/x-www-form-urlencoded');
273             $req->header('Content-Type' => 'application/xml');
274             $req->header('Accept' => 'application/xml');
275             $req->header('Content-Lenght' => length($body) );
276             $req->header('Connection' => 'Keep-Alive');
277             $req->header('Host' => GOOGLEHOST);
278             #assign the data to the request
279             $req->content($body);
280            
281             #execute the request
282             my $response = $ua->request($req);
283             #save reply page
284             $self->{replyheaders} = $response->headers->as_string;
285             $self->{replycontent} = $response->content;
286             #check result
287             if ($response->is_success) {
288             $self->{stats}->{success}++;
289             $self->dprint( "Success in post:\n");
290            
291             #delete all namespace elements to keep it simple (ie. remove "hs:")
292             #this avoids the need to use XML::NameSpace
293             my $xml = decode('UTF-8', $response->content);
294             $xml =~ s/hs\://g;
295             $self->dprint( $xml );
296            
297             #now go parse it using XML::Simple
298             $self->{result} = XMLin($xml,ForceArray => 0);
299             #include Data::Dumper above if you want to use this line:
300             #$self->dprint( Dumper($self->{result}) );
301              
302             #see if this was a successful call
303             if( defined($self->{result}->{status}) and $self->{result}->{status} eq SUCCESSCODE ) {
304             $self->dprint("Google API success!");
305             $retval = 1;
306             } else {
307             $self->dprint("Google API failure!");
308             if(defined($self->{result}->{reason})) {
309             $@ = "Google API failure: $self->{result}->{status} - $self->{result}->{reason}";
310             } else {
311             $@ = "Google API failure: reason not found!";
312             $self->{result}->{reason} = "Google API failure: reason not found!";
313             }
314             }
315             }
316             else {
317             $self->dprint( "Error in post: " . $response->status_line . "\n");
318             $self->{result}->{reason} = "Error in http post: " . $response->status_line;
319             }
320             #show full response for now
321             #$self->dprint( "Headers:\n" . $response->headers->as_string);
322             #foreach my $line (split/\n/, $response->content) {
323             # $self->dprint( "RECV'd: $line\n");
324             #}
325            
326             return $retval;
327             }
328              
329              
330             ######################################
331             ### these are the actual API calls ###
332             ### See the Google docs for more ###
333             ######################################
334              
335              
336             ### HOSTED ACCOUNT routines ###
337              
338             sub CreateAccountEmail
339             {
340             #get object reference
341             my $self = shift();
342              
343             $self->dprint( "CreateAccount called\n");
344              
345             #check remaining arguments
346             if(@_ < 4) {
347             $self->dprint( "CreateAccountEmail method requires at least 4 arguments!\n");
348             $self->{result}->{reason} = "CreateAccountEmail method requires at least 4 arguments!";
349             return 0;
350             }
351              
352             #get arguments
353             my $userName = shift();
354             my $firstName = shift();
355             my $lastName = shift();
356             my $password = shift();
357             my $quota = shift() if (@_); #this one is optional
358              
359             my $body = <<"EOL";
360            
361             $firstName
362             $lastName
363             $password
364             $userName
365             EOL
366              
367             if(defined($quota)) {
368             $body .= "\t\t$quota<\/hs:quota>\n";
369             }
370              
371             #add the final end-of-section tab
372             $body .= "\t<\/hs:CreateSection>\n";
373              
374              
375             return $self->Request('Account','Create/Account/Email',$body);
376              
377             }
378              
379             #NOTE: this API call may be discontinued!
380             sub CreateAccount
381             {
382             #get object reference
383             my $self = shift();
384              
385             $self->dprint( "CreateAccount called\n");
386              
387             #check remaining arguments
388             if(@_ != 4) {
389             $self->dprint( "CreateAccount method requires 4 arguments!\n");
390             $self->{result}->{reason} = "CreateAccount method requires 4 arguments!";
391             return 0;
392             }
393              
394             #get arguments
395             my $userName = shift();
396             my $firstName = shift();
397             my $lastName = shift();
398             my $password = shift();
399              
400             my $body = <<"EOL";
401            
402             $firstName
403             $lastName
404             $password
405             $userName
406            
407             EOL
408              
409             return $self->Request('Account','Create/Account',$body);
410              
411             }
412              
413             sub UpdateAccount
414             {
415             #get object reference
416             my $self = shift();
417              
418             $self->dprint( "UpdateAccount called\n");
419              
420             #check remaining arguments
421             if(@_ != 4) {
422             $self->dprint( "UpdateAccount method requires 4 arguments!\n");
423             $self->{result}->{reason} = "UpdateAccount method requires 4 arguments!";
424             return 0;
425             }
426              
427             #get arguments
428             my $userName = shift();
429             my $firstName = shift();
430             my $lastName = shift();
431             my $password = shift();
432              
433             #build request body
434             my $body = <<"EOL";
435             userName
436             $userName
437            
438             EOL
439              
440             if(defined($firstName)) {
441             $body .= "\t\t$firstName<\/hs:firstName>\n";
442             }
443             if(defined($lastName)) {
444             $body .= "\t\t$lastName<\/hs:lastName>\n";
445             }
446             if(defined($password)) {
447             $body .= "\t\t$password<\/hs:password>\n";
448             }
449              
450             #add the final end-of-section tab
451             $body .= "\t<\/hs:UpdateSection>\n";
452              
453              
454             return $self->Request('Account','Update/Account',$body);
455              
456             }
457              
458             sub UpdateAccountEmail
459             {
460             #get object reference
461             my $self = shift();
462              
463             $self->dprint( "UpdateAccountEmail called\n");
464              
465             #check remaining arguments
466             if(@_ != 1) {
467             $self->dprint( "UpdateAccount method requires 1 argument!\n");
468             $self->{result}->{reason} = "CreateAccount method requires 1 argument!";
469             return 0;
470             }
471              
472             #get arguments
473             my $userName = shift();
474              
475             my $body = <<"EOL";
476             userName
477             $userName
478            
479             1
480            
481             EOL
482              
483             return $self->Request('Account','Update/Account/Email',$body);
484              
485             }
486              
487             sub UpdateAccountStatus
488             {
489             #get object reference
490             my $self = shift();
491              
492             $self->dprint( "UpdateAccountStatus called\n");
493              
494             #check remaining arguments
495             if(@_ != 2) {
496             $self->dprint( "UpdateAccount method requires 2 argument!\n");
497             $self->{result}->{reason} = "CreateAccount method requires 2 arguments!";
498             return 0;
499             }
500              
501             #get arguments
502             my $userName = shift();
503             my $status = shift();
504              
505             if($status ne 'locked' and $status ne 'unlocked') {
506             $self->dprint( "Error: status invalid!\n");
507             $self->{result}->{reason} = 'Invalid status';
508             return 0;
509             }
510            
511             my $body = <<"EOL";
512             userName
513             $userName
514            
515             $status
516            
517             EOL
518              
519             return $self->Request('Account','Update/Account/Status',$body);
520              
521             }
522              
523             sub RetrieveAccount
524             {
525             #get object reference
526             my $self = shift();
527            
528             $self->dprint( "RetrieveAccount called\n");
529              
530             #check remaining arguments
531             if(@_ != 1) {
532             $self->dprint( "RetrieveAccount method requires 1 argument!\n");
533             $self->{result}->{reason} = "RetrieveAccount method requires 1 argument!";
534             return 0;
535             }
536              
537             #get argument
538             my $userName = shift();
539              
540             my $body = <<"EOL";
541             userName
542             $userName
543             EOL
544              
545             return $self->Request('Account','Retrieve/Account',$body);
546             }
547              
548              
549             sub DeleteAccount
550             {
551             #get object reference
552             my $self = shift();
553              
554             $self->dprint( "DeleteAccount called\n");
555              
556             #check remaining arguments
557             if(@_ != 1) {
558             $self->dprint( "DeleteAccount method requires 1 argument!\n");
559             $self->{result}->{reason} = "DeleteAccount method requires 1 argument!";
560             return 0;
561             }
562              
563             #get argument
564             my $userName = shift();
565              
566             my $body = <<"EOL";
567             userName
568             $userName
569             EOL
570              
571             return $self->Request('Account','Delete/Account',$body);
572             }
573              
574             sub RenameAccount
575             {
576             #This is derived from the Python sample code:
577             #-----
578             #Username change. Note that this feature must be explicitly
579             # enabled by the domain administrator, and is not enabled by
580             # default.
581             #
582             # Args:
583             # oldname: user to rename
584             # newname: new username to set for the user
585             # alias: if 1, create an alias of oldname for newname
586             #-----
587             #Ie. this may not work yet - JKR 20061204
588            
589             #get object reference
590             my $self = shift();
591              
592             $self->dprint( "RenameAccount called\n");
593              
594             #check remaining arguments
595             if(@_ != 3) {
596             $self->dprint( "RenameAccount method requires 3 arguments!\n");
597             $self->{result}->{reason} = "RenameAccount method requires 3 arguments!";
598             return 0;
599             }
600              
601             #get arguments
602             my $oldName = shift();
603             my $newName = shift();
604             my $alias = shift();
605             #check format of alias; default to 0
606             $alias = lc($alias);
607             if($alias ne '1') { $alias = '0'; }
608              
609             #build request format
610             my $body = <<"EOL";
611             userName
612             $oldName
613            
614             $newName
615             $alias
616            
617             EOL
618              
619             return $self->Request('Account','Update/Account/Username',$body);
620             }
621              
622              
623             ### ALIAS routines ###
624              
625             sub CreateAlias
626             {
627             #get object reference
628             my $self = shift();
629              
630             $self->dprint( "CreateAlias called\n");
631              
632             #check remaining arguments
633             if(@_ != 2) {
634             $self->dprint( "CreateAlias method requires 2 arguments!\n");
635             $self->{result}->{reason} = "CreateAlias method requires 2 arguments!";
636             return 0;
637             }
638              
639             #get argument
640             my $userName = shift();
641             my $alias = shift();
642            
643             #create the command format
644             my $body = <<"EOL";
645            
646             $userName
647             $alias
648            
649             EOL
650              
651             return $self->Request('Alias','Create/Alias',$body);
652             }
653              
654             sub RetrieveAlias
655             {
656             #get object reference
657             my $self = shift();
658            
659             $self->dprint( "RetrieveAlias called\n");
660              
661             #check remaining arguments
662             if(@_ != 1) {
663             $self->dprint( "RetrieveAlias method requires 1 argument!\n");
664             $self->{result}->{reason} = "RetrieveAlias method requires 1 argument!";
665             return 0;
666             }
667              
668             #get argument
669             my $userName = shift();
670              
671             my $body = <<"EOL";
672             aliasName
673             $userName
674             EOL
675              
676             return $self->Request('Alias','Retrieve/Alias',$body);
677             }
678              
679             sub DeleteAlias
680             {
681             #get object reference
682             my $self = shift();
683              
684             $self->dprint( "DeleteAlias called\n");
685              
686             #check remaining arguments
687             if(@_ != 1) {
688             $self->dprint( "DeleteAlias method requires 1 argument!\n");
689             $self->{result}->{reason} = "DeleteAlias method requires 1 argument!";
690             return 0;
691             }
692              
693             #get arguments
694             my $alias = shift();
695              
696             my $body = <<"EOL";
697             aliasName
698             $alias
699             EOL
700              
701             return $self->Request('Alias','Delete/Alias',$body);
702             }
703              
704              
705             ### Mailing List routines
706              
707              
708             sub CreateMailingList
709             {
710             #get object reference
711             my $self = shift();
712              
713             $self->dprint( "CreateMailingList called\n");
714              
715             #check remaining arguments
716             if(@_ != 1) {
717             $self->dprint( "CreateMailingList method requires 1 argument!\n");
718             $self->{result}->{reason} = "CreateMailingList method requires 1 argument!";
719             return 0;
720             }
721              
722             #get arguments
723             my $mailingListName = shift();
724              
725             my $body = <<"EOL";
726            
727             $mailingListName
728            
729             EOL
730              
731             return $self->Request('MailingList','Create/MailingList',$body);
732              
733             }
734              
735              
736             sub UpdateMailingList
737             {
738             #get object reference
739             my $self = shift();
740              
741             $self->dprint( "UpdateMailingList called\n");
742              
743             #check remaining arguments
744             if(@_ != 3) {
745             $self->dprint( "UpdateMailingList method requires 3 arguments!\n");
746             $self->{result}->{reason} = 'UpdateMailingList method requires 3 arguments!';
747             return 0;
748             }
749              
750             #get arguments
751             my $mailingListName = shift();
752             my $userName = shift();
753             my $listOperation = shift();
754              
755             my $body = <<"EOL";
756             mailingListName
757             $mailingListName
758            
759             $userName
760             $listOperation
761            
762             EOL
763              
764             return $self->Request('MailingList','Update/MailingList',$body);
765              
766             }
767              
768              
769              
770             sub RetrieveMailingList
771             {
772             #get object reference
773             my $self = shift();
774            
775             $self->dprint( "RetrieveMailingList called\n");
776              
777             #check remaining arguments
778             if(@_ != 1) {
779             $self->dprint( "RetrieveMailingList method requires 1 argument!\n");
780             $self->{result}->{reason} = 'RetrieveMailingList method requires 1 arguments!';
781             return 0;
782             }
783              
784             #get argument
785             my $mailingListName = shift();
786              
787             my $body = <<"EOL";
788             mailingListName
789             $mailingListName
790             EOL
791              
792             return $self->Request('MailingList','Retrieve/MailingList',$body);
793             }
794              
795              
796             sub DeleteMailingList
797             {
798             #get object reference
799             my $self = shift();
800              
801             $self->dprint( "DeleteMailingList called\n");
802              
803             #check remaining arguments
804             if(@_ != 1) {
805             $self->dprint( "DeleteMailingList method requires 1 argument!\n");
806             $self->{result}->{reason} = 'DeleteMailingList method requires 1 argument!';
807             return 0;
808             }
809              
810             #get argument
811             my $mailingListName = shift();
812              
813             my $body = <<"EOL";
814             mailingListName
815             $mailingListName
816             EOL
817              
818             return $self->Request('MailingList','Delete/MailingList',$body);
819             }
820              
821              
822             ################################################################
823             # below are various subroutines to access local 'private' data #
824             ################################################################
825              
826             #the content of the request from and reply from Google API engine
827             sub requestcontent
828             {
829             my $self = shift();
830              
831             return $self->{requestcontent};
832             }
833              
834             sub replyheaders
835             {
836             my $self = shift();
837              
838             return $self->{replyheaders};
839             }
840              
841             sub replycontent
842             {
843             my $self = shift();
844              
845             return $self->{replycontent};
846             }
847              
848            
849             #various access to local variables
850             sub debug
851             {
852             my $self = shift();
853              
854             $self-> { debug } = shift() if (@_);
855            
856             return $self->{debug};
857             }
858              
859             #change the admin account
860             sub admin
861             {
862             my $self = shift();
863              
864             if (@_)
865             {
866             $self-> { admin } = shift();
867             $self-> { refreshtoken } = 1;
868             }
869            
870             return $self->{admin};
871             }
872              
873             #password can only be set, not read!
874             sub password
875             {
876             my $self = shift();
877              
878              
879             if (@_)
880             {
881             $self-> { password } = shift();
882             #force authentication update on next request
883             $self-> { refreshtoken } = 1;
884             }
885            
886             return '';
887             }
888              
889             #the following can only be read!
890             sub authtime
891             {
892             my $self = shift();
893              
894             return $self->{authtime};
895             }
896              
897             #same for create time
898             sub ctime
899             {
900             my $self = shift();
901              
902             return $self->{stats}->{ctime};
903             }
904              
905             #and request time
906             sub rtime
907             {
908             my $self = shift();
909              
910             return $self->{stats}->{rtime};
911             }
912              
913             sub requests
914             {
915             my $self = shift();
916              
917             return $self->{stats}->{requests};
918             }
919              
920             sub logins
921             {
922             my $self = shift();
923              
924             return $self->{stats}->{logins};
925             }
926              
927             sub success
928             {
929             my $self = shift();
930              
931             return $self->{stats}->{success};
932             }
933              
934             sub version
935             {
936             my $self = shift();
937              
938             return $APIVersion;
939             }
940              
941             #several helper routines
942              
943             #print out debugging to STDERR if debug is set
944             sub dprint
945             {
946             my $self = shift();
947             my($text) = shift if (@_);
948             if( $self->{debug} and defined ($text) ) {
949             print STDERR $text . "\n";
950             }
951             }
952              
953             1;
954             __END__