File Coverage

lib/SMB.pm
Criterion Covered Total %
statement 39 149 26.1
branch 11 88 12.5
condition 0 20 0.0
subroutine 10 17 58.8
pod 6 7 85.7
total 66 281 23.4


line stmt bran cond sub pod time code
1             # SMB-Perl library, Copyright (C) 2014 Mikhael Goikhman, migo@cpan.org
2             #
3             # This program is free software: you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation, either version 3 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program. If not, see .
15              
16             package SMB;
17              
18 4     4   26007 use strict;
  4         9  
  4         124  
19 4     4   19 use warnings;
  4         6  
  4         459  
20              
21             our $VERSION = 0.06;
22              
23             use constant {
24 4         9751 STATUS_SUCCESS => 0x00000000,
25             STATUS_PENDING => 0x00000103,
26             STATUS_NOTIFY_ENUM_DIR => 0x0000010c,
27             STATUS_SMB_BAD_TID => 0x00050002,
28             STATUS_OS2_INVALID_LEVEL => 0x007c0001,
29             STATUS_NO_MORE_FILES => 0x80000006,
30             STATUS_INVALID_PARAMETER => 0xc000000d,
31             STATUS_NO_SUCH_DEVICE => 0xc000000e,
32             STATUS_NO_SUCH_FILE => 0xc000000f,
33             STATUS_END_OF_FILE => 0xc0000011,
34             STATUS_MORE_PROCESSING_REQUIRED => 0xc0000016,
35             STATUS_NO_FREE_MEMORY => 0xc0000017,
36             STATUS_ACCESS_DENIED => 0xc0000022,
37             STATUS_BUFFER_TOO_SMALL => 0xc0000023,
38             STATUS_OBJECT_NAME_NOT_FOUND => 0xc0000034,
39             STATUS_OBJECT_NAME_COLLISION => 0xc0000035,
40             STATUS_OBJECT_PATH_NOT_FOUND => 0xc000003a,
41             STATUS_SHARING_VIOLATION => 0xc0000043,
42             STATUS_DELETE_PENDING => 0xc0000056,
43             STATUS_PRIVILEGE_NOT_HELD => 0xc0000061,
44             STATUS_LOGON_FAILURE => 0xc000006d,
45             STATUS_DISK_FULL => 0xc000007f,
46             STATUS_FILE_IS_A_DIRECTORY => 0xc00000ba,
47             STATUS_BAD_NETWORK_NAME => 0xc00000cc,
48             STATUS_DIRECTORY_NOT_EMPTY => 0xc0000101,
49             STATUS_NOT_A_DIRECTORY => 0xc0000103,
50             STATUS_CANCELLED => 0xc0000120,
51             STATUS_CANNOT_DELETE => 0xc0000121,
52             STATUS_FILE_CLOSED => 0xc0000128,
53             STATUS_INVALID_LEVEL => 0xc0000148,
54             STATUS_FS_DRIVER_REQUIRED => 0xc000019c,
55             STATUS_NOT_A_REPARSE_POINT => 0xc0000275,
56 4     4   43 };
  4         5  
57              
58             sub new ($%) {
59 28     28 1 49 my $class = shift;
60 28         146 my %options = @_;
61              
62 28 50       215 my $self = {
63             disable_log => $options{quiet} ? 1 : 0,
64             %options,
65             };
66              
67 28         148 bless $self, $class;
68             }
69              
70             sub log ($$@) {
71 7     7 1 11 my $self = shift;
72 7         15 my $is_err = shift;
73 7         11 my $format = shift;
74 7 50       30 return if $self->disable_log;
75 7 100       1755 print sprintf("%s $format\n", $is_err ? '!' : '*', @_);
76             }
77              
78 3     3 1 13 sub msg ($@) { shift()->log(0, @_) }
79 4     4 1 20 sub err ($@) { shift()->log(1, @_); return }
  4         11  
80              
81             my $MAX_DUMP_BYTES = 8 * 1024;
82             my $dump_line_format = "%03x | 00 53 54 52 49 4E 47 aa aa aa aa aa aa aa | _STRING. ...... |\n";
83              
84             sub mem ($$;$) {
85 0     0 1 0 my $self = shift;
86 0         0 my $data = shift;
87 0   0     0 my $label = shift || "Data dump";
88 0 0       0 return if $self->disable_log;
89              
90 0 0       0 if (!defined $data) {
91 0         0 $self->msg("$label (undef)");
92 0         0 return;
93             }
94              
95 0         0 my $len = length($data);
96 0 0       0 $self->msg(sprintf("%s (%lu bytes%s):", $label, $len, $len > $MAX_DUMP_BYTES ? ", shorten" : ""), @_);
97 0 0       0 $len = $MAX_DUMP_BYTES if $len > $MAX_DUMP_BYTES;
98              
99 0         0 for (my $n = 0; $n < ($len + 15) / 16; $n++) {
100 0         0 for (my $i = 0; $i < 16; $i++) {
101 0         0 my $valid = $n * 16 + $i < $len;
102 0 0       0 my $b = $valid ? ord(substr($data, $n * 16 + $i, 1)) : undef;
103 0 0       0 substr($dump_line_format, 7 + $i * 3 + ($i >= 8), 2) = $valid ? sprintf("%02x", $b) : " ";
104 0 0 0     0 substr($dump_line_format, 58 + $i + ($i >= 8), 1) = $valid ? $b == 0 ? '_' : $b <= 32 || $b >= 127 || $b == 37 ? '.' : chr($b) : ' ';
    0          
    0          
105             }
106 0         0 printf $dump_line_format, $n;
107             }
108             }
109              
110             sub parse_share_uri ($$) {
111 0     0 0 0 my $self = shift;
112 0         0 my $share_uri = shift;
113              
114 0 0       0 unless ($share_uri) {
115 0         0 $self->err("No share uri supplied");
116 0         0 return;
117             }
118 0 0       0 unless ($share_uri =~ m~^([/\\])\1([\w.]+(?::\d+)?)\1([^/\\]+)(?:$|\1)~) {
119 0         0 $self->err("Invalid share uri ($share_uri)");
120 0         0 return;
121             }
122              
123 0 0       0 return wantarray ? ($2, $3) : $share_uri;
124             }
125              
126             our %dump_seen;
127             our $dump_is_newline = 1;
128             our $dump_level_limit = 7;
129             our $dump_array_limit = 20;
130             our $dump_string_limit = 50;
131              
132             sub _dump_prefix ($) {
133 0     0   0 my $level = shift;
134              
135 0 0       0 return "" unless $dump_is_newline;
136 0         0 $dump_is_newline = 0;
137              
138 0         0 return " " x (4 * $level);
139             }
140              
141             sub _dump_eol () {
142 0     0   0 $dump_is_newline = 1;
143              
144 0         0 return "\n";
145             }
146              
147             sub _dump_string ($) {
148 0     0   0 my $value = shift;
149              
150 0         0 my $len = length($value);
151 0 0       0 if ($len > $dump_string_limit) {
152 0         0 my $llen = length($len);
153 0         0 substr($value, $dump_string_limit - 3 - $llen) =
154             "..+" . ($len - $dump_string_limit + 3 + $llen);
155             }
156              
157 0         0 $value =~ s/([\\"])/\\$1/g;
158 0         0 $value =~ s/([^\\" -\x7e])/sprintf("\\x%02x", ord($1))/ge;
  0         0  
159              
160 0         0 return $value;
161             }
162              
163             sub _dump_value ($) {
164 0     0   0 my $value = shift;
165 0   0     0 my $level = shift || 0;
166 0   0     0 my $inline = shift || 0;
167              
168 0 0       0 return '' if $level >= $dump_level_limit;
169              
170 0         0 my $type = ref($value);
171 0         0 my $dump = _dump_prefix($level);
172 0   0     0 my $is_seen = $type && $dump_seen{$value};
173 0 0       0 $dump_seen{$value} = 1 if $type;
174              
175 0 0       0 if (! $type) {
    0          
    0          
    0          
    0          
    0          
    0          
176 0 0 0     0 $dump .= defined $value
    0          
177             ? $value =~ /^-?\d+$/ ||$inline == 2 && $value =~ /^-?\w+$/
178             ? $value : '"' . _dump_string($value) . '"'
179             : 'undef';
180             } elsif ($type eq 'ARRAY') {
181 0 0       0 if ($is_seen) {
182 0         0 $dump .= "ARRAY (seen)";
183             } else {
184 0         0 $dump .= "[" . _dump_eol();
185 0 0       0 my @array = @$value > $dump_array_limit ? (@$value)[0 .. $dump_array_limit - 2] : @$value;
186 0         0 my $prev_elem = '';
187 0         0 foreach (@array) {
188             # compress equal consecutive elements
189 0         0 my $elem = &_dump_value($_, $level + 1, 1);
190 0 0       0 if ($elem eq $prev_elem) {
191 0   0     0 $dump =~ s/^(\s+)(?:\()?(.*?)(?:\) x (\d+))?,$(\n)\z/my $c = ($3 || 1) + 1; "$1($2) x $c," . _dump_eol()/me;
  0         0  
  0         0  
192 0         0 next;
193             }
194 0         0 $dump .= _dump_prefix($level + 1);
195 0         0 $dump .= $prev_elem = $elem;
196 0         0 $dump .= "," . _dump_eol();
197             }
198 0 0       0 if (@$value > $dump_array_limit) {
199 0         0 $dump .= _dump_prefix($level + 1);
200 0         0 $dump .= "...[+" . (@$value - $dump_array_limit + 1) . "]," . _dump_eol();
201             }
202 0         0 $dump .= _dump_prefix($level) . "]";
203             }
204             } elsif ($type eq 'HASH') {
205 0 0       0 if ($is_seen) {
206 0         0 $dump .= "HASH (seen)";
207             } else {
208 0         0 $dump .= "{" . _dump_eol();
209 0         0 my $idx = 0;
210 0         0 my @keys = sort keys %$value;
211 0         0 my $size = @keys;
212 0         0 foreach my $key (@keys) {
213 0         0 my $val = $value->{$key};
214 0 0 0     0 last if ++$idx == $dump_array_limit && $size > $dump_array_limit;
215 0         0 $dump .= _dump_prefix($level + 1);
216 0         0 $dump .= &_dump_value($key, $level + 1, 2);
217 0         0 $dump .= " => ";
218 0         0 $dump .= &_dump_value($val, $level + 1, 1);
219 0         0 $dump .= "," . _dump_eol();
220             }
221 0 0       0 if ($size > $dump_array_limit) {
222 0         0 $dump .= _dump_prefix($level + 1);
223 0         0 $dump .= "...[+" . ($size - $dump_array_limit + 1) . "]," . _dump_eol();
224             }
225 0         0 $dump .= _dump_prefix($level) . "}";
226             }
227             } elsif ($type eq 'REF') {
228 0         0 $dump .= "REF";
229             } elsif ($type eq 'CODE') {
230 0         0 $dump .= "CODE";
231             } elsif ($type eq 'GLOB') {
232 0         0 $dump .= "GLOB";
233             } elsif ($type eq 'SCALAR') {
234 0         0 $dump .= "\\";
235 0         0 $dump .= &_dump_value($$value, $level + 1, 1);
236             } else {
237 0         0 $dump .= "$type ";
238 0         0 my $native_type;
239 0         0 foreach ('SCALAR', 'ARRAY', 'HASH', 'CODE', 'GLOB') {
240 0 0       0 $native_type = $_ if $value->isa($_);
241             }
242 0 0       0 die "Non-standard perl ref type to dump in $value\n" unless $native_type;
243              
244 0         0 $dump_seen{$value} = 0;
245 0         0 bless($value, $native_type);
246 0         0 $dump .= &_dump_value($value, $level, 1);
247 0         0 bless($value, $type);
248             }
249              
250 0 0       0 $dump .= _dump_eol() unless $inline;
251              
252 0         0 return $dump;
253              
254             }
255              
256             sub dump ($;$) {
257 0     0 1 0 my $self = shift;
258 0         0 my $level = 0;
259              
260 0         0 my $dump = _dump_value($self);
261              
262 0         0 %dump_seen = ();
263              
264 0         0 return $dump;
265             }
266              
267             our $AUTOLOAD;
268              
269             sub AUTOLOAD ($;@) {
270 35     35   2132 my $self = shift;
271 35         56 my @params = @_;
272              
273 35         55 my $method = $AUTOLOAD;
274 35         168 $method =~ s/.*://g;
275              
276 35 50       95 return if $method eq 'DESTROY'; # ignore DESTROY messages
277              
278 35 50       87 die "Calling method $method for non-object '$self'\n"
279             unless ref($self);
280              
281 35 50       109 if (exists $self->{$method}) {
282             # define this accessor method explicitely if not yet
283 4     4   29 no strict 'refs';
  4         6  
  4         756  
284 35         133 *{$AUTOLOAD} = sub {
285 331     331   10847 my $self = shift;
286 331 50       728 warn "Skipping extraneous params (@_) on access of field '$method' in $self\n"
287             if @_ > 1;
288 331 100       626 $self->{$method} = shift if @_;
289 331         3426 return $self->{$method};
290 35 50       441 } unless $self->can($AUTOLOAD);
291              
292 35         57 return *{$AUTOLOAD}->($self, @params);
  35         109  
293             }
294              
295 0           die "Unknown method or field '$method' in $self\n";
296             }
297              
298             1;
299              
300             __END__