File Coverage

blib/lib/Colloquy/Data.pm
Criterion Covered Total %
statement 21 123 17.0
branch 0 52 0.0
condition 0 11 0.0
subroutine 7 17 41.1
pod 2 6 33.3
total 30 209 14.3


line stmt bran cond sub pod time code
1             ############################################################
2             #
3             # $Id: Data.pm 526 2006-05-29 12:27:43Z nicolaw $
4             # Colloquy::Data - Read Colloquy 1.3 and 1.4 data files
5             #
6             # Copyright 2005,2006 Nicola Worthington
7             #
8             # Licensed under the Apache License, Version 2.0 (the "License");
9             # you may not use this file except in compliance with the License.
10             # You may obtain a copy of the License at
11             #
12             # http://www.apache.org/licenses/LICENSE-2.0
13             #
14             # Unless required by applicable law or agreed to in writing, software
15             # distributed under the License is distributed on an "AS IS" BASIS,
16             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17             # See the License for the specific language governing permissions and
18             # limitations under the License.
19             #
20             ############################################################
21              
22             package Colloquy::Data;
23             # vim:ts=4:sw=4:tw=78
24              
25 2     2   37399 use strict;
  2         6  
  2         73  
26 2     2   12 use Exporter;
  2         4  
  2         90  
27 2     2   9 use Fcntl ':mode';
  2         4  
  2         739  
28 2     2   11 use Carp qw(cluck croak);
  2         9  
  2         129  
29 2     2   2684 use Safe;
  2         124072  
  2         149  
30              
31 2     2   27 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $DEBUG);
  2         4  
  2         210  
32 2     2   12 use constant DEFAULT_DATADIR => '/usr/local/colloquy/data';
  2         3  
  2         4068  
33              
34             $VERSION = '1.15' || sprintf('%d', q$Revision: 526 $ =~ /(\d+)/g);
35             $DEBUG = $ENV{DEBUG} ? 1 : 0;
36             @ISA = qw(Exporter);
37             @EXPORT = ();
38             @EXPORT_OK = qw(&lists &users &caps &commify);
39             %EXPORT_TAGS = ( all => \@EXPORT_OK );
40              
41             sub users {
42 0     0 1   return _get_data(shift);
43             }
44              
45             sub lists {
46 0     0 1   my ($users,$lists) = _get_data(shift);
47 0           return ($lists,$users);
48             }
49              
50             sub caps {
51 0     0 0   (my $c = $_[0]) =~ s/_/ /g;
52 0           my @c = split(/\b/,$c);
53 0 0         foreach (@c) { if (/^([a-z])(.*)/) { $_ = uc($1).$2; } }
  0            
  0            
54 0           return join("",@c);
55             }
56              
57             sub commify {
58 0     0 0   local $_ = shift;
59 0           s/^\s+|\s+$//g;
60 0           1 while s/^([-+]?\d+)(\d{3})/$1,$2/;
61 0           return $_;
62             }
63              
64             sub _munge_user_lua {
65 0     0     local $_ = shift;
66 0           s/'/\\'/g;
67 0           s/"/'/g; #"'
68 0           s/(\s+[a-z0-9]+\s+=)(\s+['{\d+])/$1>$2/gi;
69 0           s/^return //;
70 0           return $_;
71             }
72              
73             sub _munge_list_lua {
74 0     0     local $_ = shift;
75 0           s/\s+\['(\S+?)'\]\s+=\s+{/ $1 => {/g;
76 0           s/'/\\'/g;
77 0           s/"/'/g; #"'
78 0           s/(\s+[a-z0-9]+\s+=)(\s+['{\d+])/$1>$2/gi;
79 0           s/(\s+members\s+=>\s+)\{(.+?)\}/$1 [ ( $2 ) ]/sgi;
80 0           s/^return //;
81 0           return $_;
82             }
83              
84             sub _read_file {
85 0     0     my $file = shift;
86 0 0         croak "No such file '$file'\n" unless -e $file;
87 0 0         croak "'$file' is not a plain file type\n" unless -f _;
88 0 0         croak "Insufficient permissions to read file '$file'\n" unless -r _;
89              
90 0           my $mode = (stat(_))[2];
91 0           my $group_write = ($mode & S_IWGRP) >> 3;
92 0           my $other_write = $mode & S_IWOTH;
93              
94             # Since this module started using Safe to parse the data files,
95             # this code is no longer as important as before. It's now only
96             # a warning.
97             # if ($^W && $group_write) {
98             # cluck "WARNING! $file is group writeable. This is potentially insecure!";
99             # }
100             #if ($other_write) {
101 0 0 0       if ($^W && $other_write) {
102             #croak "FATAL! $file is world writeable. This insecure file cannot be evaluated!";
103 0           cluck "WARNING! $file is world writeable. This is potentially insecure!";
104             }
105              
106 0 0         if (open(FH,"<$file")) {
107 0           local $/ = undef;
108 0           my $data = ;
109 0           close(FH);
110 0           return $data;
111             } else {
112 0           croak "Unable to open file handle FH for file '$file': $!";
113             # return undef;
114             }
115             }
116              
117             sub _get_data {
118 0   0 0     my $datadir = shift || DEFAULT_DATADIR;
119 0 0         my $users_lua = $datadir.'/users'.(-f $datadir.'/users.lua' ? '.lua' : '');
120 0 0         my $lists_lua = $datadir.'/lists'.(-f $datadir.'/lists.lua' ? '.lua' : '');
121              
122 0           my $users = {};
123 0 0         croak "Insufficient permissions to read $users_lua\n" unless -r $users_lua;
124              
125 0           my $c = new Safe;
126             # Minimum safe opcode set for building data structures lineseq, list and
127             # padany needed for perl 5.8.7
128 0           $c->permit_only(qw(rv2sv sassign aelem aelemfast helem anonlist anonhash
129             pushmark refgen const undef leaveeval lineseq list padany));
130              
131 0 0         if (-f $users_lua) {
    0          
132 0           my $coderef = _munge_user_lua( '$' . _read_file($users_lua) );
133 0           $users = $c->reval($coderef);
134             #eval $coderef;
135              
136             } elsif (-d $users_lua) {
137 0 0         if (opendir(DH,$users_lua)) {
138 0           for my $user (grep(!/^\./,readdir(DH))) {
139 0 0         next unless -f "$users_lua/$user";
140 0 0         unless (-r "$users_lua/$user") {
141 0           cluck "Insufficient permissions to read $users_lua/$user";
142 0           next;
143             }
144 0           my $coderef = _munge_user_lua( _read_file("$users_lua/$user") );
145 0 0 0       if (length($coderef) > 9 && $coderef =~ /^\s*(return )?{.+}\s*$/gsi) {
146             # if (length($coderef) > 9 && $coderef =~ /return {.+}/gsi) {
147 0           DUMP('$coderef',$coderef);
148 0           $users->{$user} = $c->reval($coderef);
149 0           DUMP('$users',$users);
150             #eval { $users->{$user} = eval $coderef; }
151             } else {
152 0           cluck "Caught known Colloquy data file corruption for user $user";
153             }
154             }
155 0           closedir(DH);
156             } else {
157 0           croak "Failed to open file handle DH for directory '$users_lua': $!";
158             }
159             }
160              
161 0           my $lists = {};
162 0 0         croak "Insufficient permissions to read $lists_lua\n" unless -r $lists_lua;
163              
164 0 0         if (-f $lists_lua) {
    0          
165 0           my $coderef = _munge_list_lua( '$' . _read_file($lists_lua) );
166 0           $lists = $c->reval($coderef);
167             #eval $coderef;
168              
169             } elsif (-d $lists_lua) {
170 0 0         if (opendir(DH,$lists_lua)) {
171 0           for my $list (grep(!/^\./,readdir(DH))) {
172 0 0         next unless -f "$lists_lua/$list";
173 0 0         unless (-r "$lists_lua/$list") {
174 0           cluck "Insufficient permissions to read $lists_lua/$list";
175 0           next;
176             }
177 0           my $coderef = _munge_list_lua( _read_file("$lists_lua/$list") );
178 0 0 0       if (length($coderef) > 9 && $coderef =~ /^\s*(return )?{.+}\s*$/gsi) {
179             # if (length($coderef) > 9 && $coderef =~ /return {.+}/gsi) {
180 0           DUMP('$coderef',$coderef);
181 0           $lists->{$list} = $c->reval($coderef);
182 0           DUMP('$lists',$lists);
183             #$lists->{$list} = eval $coderef;
184             } else {
185 0           cluck "Caught known Colloquy data file corruption for list $list";
186             }
187             }
188 0           closedir(DH);
189             } else {
190 0           croak "Failed to open file handle DH for directory '$lists_lua': $!";
191             }
192             }
193              
194 0           for my $list (keys %{$lists}) {
  0            
195 0           for my $member (@{$lists->{$list}->{members}}) {
  0            
196 0 0         $users->{$member}->{lists} = [] unless exists $users->{$member}->{lists};
197 0 0         $lists->{$list}->{users} = [] unless exists $lists->{$list}->{users};
198 0           push @{$users->{$member}->{lists}},$list;
  0            
199 0           push @{$lists->{$list}->{users}},$member;
  0            
200             }
201             }
202              
203 0           return ($users,$lists);
204             }
205              
206             sub TRACE {
207 0 0   0 0   return unless $DEBUG;
208 0           warn(shift());
209             }
210              
211             sub DUMP {
212 0 0   0 0   return unless $DEBUG;
213 0           eval {
214 0           require Data::Dumper;
215 0           warn(shift().': '.Data::Dumper::Dumper(shift()));
216             }
217             }
218              
219             1;
220              
221             =pod
222              
223             =head1 NAME
224              
225             Colloquy::Data - Read Colloquy 1.3 and 1.4 data files
226              
227             =head1 SYNOPSIS
228              
229             use Data::Dumper;
230             use Colloquy::Data qw(:all);
231            
232             my $colloquy_datadir = "/home/system/colloquy/data";
233            
234             #my ($users_hashref,$lists_hashref) = users($colloquy_datadir);
235             my ($lists_hashref,$users_hashref) = lists($colloquy_datadir);
236            
237             print "Users: ".Dumper($users);
238             print "Lists: ".Dumper($lists);
239              
240             =head1 DESCRIPTION
241              
242             This module munges the users.lua and lists.lua (Colloquy 1.3x) files
243             in to executable perl code which is then evaluated. Colloquy 1.4 uses
244             a seperate LUA file for each user and list, which are located in the
245             users and lists directories in the Colloquy data directory. These files
246             are read one by one and evaluated in the same way.
247              
248             This module compiles and execute the Colloquy data files in restricted
249             compartments using the L module. Even so, this module should be
250             used with caution if you cannot gaurentee the integrity of the user and
251             list LUA files. The module will issue a warning complaining about world
252             writable permissions if $^W warnings.
253              
254             =head1 EXPORTS
255              
256             =head2 users
257              
258             my ($users_hashref,$lists_hashref) = users($colloquy_datadir);
259              
260             Returns users and lists hash references, in that order.
261              
262             =head2 lists
263              
264             my ($lists_hashref,$users_hashref) = lists($colloquy_datadir);
265              
266             Returns lists and users hash references, in that order.
267              
268             =head1 SEE ALSO
269              
270             L, L
271              
272             =head1 VERSION
273              
274             $Id: Data.pm 526 2006-05-29 12:27:43Z nicolaw $
275              
276             =head1 AUTHOR
277              
278             Nicola Worthington
279              
280             L
281              
282             =head1 COPYRIGHT
283              
284             Copyright 2005,2006 Nicola Worthington.
285              
286             This software is licensed under The Apache Software License, Version 2.0.
287              
288             L
289              
290             =cut
291              
292             __END__