File Coverage

blib/lib/XAS/Lib/WS/Transfer.pm
Criterion Covered Total %
statement 3 109 2.7
branch 0 6 0.0
condition n/a
subroutine 1 15 6.6
pod 7 7 100.0
total 11 137 8.0


line stmt bran cond sub pod time code
1             package XAS::Lib::WS::Transfer;
2              
3             our $VERSION = '0.01';
4              
5             use XAS::Class
6 1         6 version => $VERSION,
7             base => 'XAS::Lib::WS::RemoteShell',
8             codecs => 'base64 unicode',
9             utils => ':validation dotid',
10             constants => 'SCALAR',
11 1     1   1787 ;
  1         1  
12              
13             # ----------------------------------------------------------------------
14             # Public Methods
15             # ----------------------------------------------------------------------
16              
17             sub get {
18 0     0 1   my $self = shift;
19 0           my ($remote, $local) = validate_params(\@_, [
20             { type => SCALAR },
21             { type => SCALAR },
22             ]);
23              
24             # this assumes that the remote WS-Manage server is Microsoft based
25             # otherwise you would use something sensible like scp
26              
27 0           my $fh;
28 0           my $code = $self->_code_get_powershell($remote);
29 0           my $invoke = "powershell -noprofile -encodedcommand $code";
30              
31 0           $self->command($invoke);
32 0           $self->receive();
33 0           $self->check_exitcode();
34            
35 0 0         if (open($fh, '>', $local)) {
36              
37 0           print $fh decode_base64($self->stdout);
38 0           close $fh;
39              
40             } else {
41              
42 0           $self->throw_msg(
43             dotid($self->class) . '.put.badfile',
44             'file_create',
45             $local, $!
46             );
47              
48             }
49              
50 0           return $self->exitcode;
51              
52             }
53              
54             sub put {
55 0     0 1   my $self = shift;
56 0           my ($local, $remote) = validate_params(\@_, [
57             { type => SCALAR },
58             { type => SCALAR },
59             ]);
60              
61             # this assumes that the remote WS-Manage server is Microsoft based
62             # otherwise you would use something sensible like scp
63              
64 0           my $fh;
65 0           my $size = 30 * 57;
66 0           my $invoke = 'powershell -noprofile -encodedcommand %s';
67              
68 0 0         if (open($fh, '<', $local)) {
69              
70 0           while (read($fh, my $buf, $size)) {
71              
72 0           my $data = encode_base64($buf, '');
73 0           my $code = $self->_code_put_powershell($remote, $data);
74 0           my $cmd = sprintf($invoke, $code);
75              
76 0           $self->command($cmd);
77 0           $self->receive();
78 0           $self->check_exitcode();
79              
80             }
81              
82 0           close $fh;
83              
84             } else {
85              
86 0           $self->throw_msg(
87             dotid($self->class) . '.put.badfile',
88             'file_create',
89             $local, $!
90             );
91              
92             }
93              
94 0           return $self->exitcode;
95              
96             }
97              
98             sub exists {
99 0     0 1   my $self = shift;
100 0           my ($path) = validate_params(\@_, [1]);
101              
102 0           my $code = $self->_code_exists_powershell($path);
103 0           my $invoke = "powershell -noprofile -encodedcommand $code";
104              
105 0           $self->command($invoke);
106 0           $self->receive();
107 0           $self->check_exitcode();
108              
109 0 0         return $self->exitcode ? 0 : 1;
110              
111             }
112              
113             sub mkdir {
114 0     0 1   my $self = shift;
115 0           my ($path) = validate_params(\@_, [1]);
116              
117 0           my $code = $self->_code_mkdir_powershell($path);
118 0           my $invoke = "powershell -noprofile -encodedcommand $code";
119              
120 0           $self->command($invoke);
121 0           $self->receive();
122 0           $self->check_exitcode();
123              
124 0           return $self->exitcode;
125              
126             }
127              
128             sub rmdir {
129 0     0 1   my $self = shift;
130 0           my ($path) = validate_params(\@_, [1]);
131              
132 0           my $code = $self->_code_rmdir_powershell($path);
133 0           my $invoke = "powershell -noprofile -encodedcommand $code";
134              
135 0           $self->command($invoke);
136 0           $self->receive();
137 0           $self->check_exitcode();
138              
139 0           return $self->exitcode;
140              
141             }
142              
143             sub del {
144 0     0 1   my $self = shift;
145 0           my ($path) = validate_params(\@_, [1]);
146              
147 0           my $code = $self->_code_del_powershell($path);
148 0           my $invoke = "powershell -noprofile -encodedcommand $code";
149              
150 0           $self->command($invoke);
151 0           $self->receive();
152 0           $self->check_exitcode();
153              
154 0           return $self->exitcode;
155              
156             }
157              
158             sub dir {
159 0     0 1   my $self = shift;
160 0           my ($path) = validate_params(\@_, [1]);
161              
162 0           my $code = $self->_code_dir_powershell($path);
163 0           my $invoke = "powershell -noprofile -encodedcommand $code";
164              
165 0           $self->command($invoke);
166 0           $self->receive();
167 0           $self->check_exitcode();
168              
169 0           return $self->stdout;
170              
171             }
172              
173             # ----------------------------------------------------------------------
174             # Private Methods
175             # ----------------------------------------------------------------------
176              
177             # ----------------------------------------------------------------------
178             # Powershell Boilerplate - yeah heredoc...
179             #
180             # some powershell code borrowed from
181             # https://github.com/WinRb/winrm-fs/tree/master/lib/winrm-fs/scripts
182             # ----------------------------------------------------------------------
183              
184             sub _code_put_powershell {
185 0     0     my $self = shift;
186 0           my $filename = shift;
187 0           my $data = shift;
188              
189 0           my $code = <<'CODE';
190             $ProgressPreference='SilentlyContinue'
191             try {
192             $data = '__DATA__'
193             $bytes = [System.Convert]::FromBase64String($data)
194             $file = [System.IO.File]::Open('__FILENAME__', 'Append')
195             $file.Write($bytes, 0, $bytes.Length)
196             $file.Close()
197             exit 0
198             } catch {
199             Write-Error -Message $_.Exception.Message
200             exit 1
201             }
202             CODE
203              
204 0           $code =~ s/__FILENAME__/$filename/;
205 0           $code =~ s/__DATA__/$data/;
206              
207 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
208              
209             }
210              
211             sub _code_get_powershell {
212 0     0     my $self = shift;
213 0           my $filename = shift;
214              
215 0           my $code = <<'CODE';
216             $ProgressPreference='SilentlyContinue'
217             $p = $ExecutionContext.SessionState.Path
218             $path = $p.GetUnresolvedProviderPathFromPSPath('__FILENAME__')
219             if (Test-Path $path -PathType Leaf) {
220             $bytes = [System.convert]::ToBase64String([System.IO.File]::ReadAllBytes($path))
221             Write-Host $bytes
222             exit 0
223             }
224             Write-Error -Message 'File not found'
225             exit 1
226             CODE
227              
228 0           $code =~ s/__FILENAME__/$filename/;
229              
230 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
231              
232             }
233              
234             sub _code_exists_powershell {
235 0     0     my $self = shift;
236 0           my $path = shift;
237              
238 0           my $code = <<'CODE';
239             $ProgressPreference='SilentlyContinue'
240             $p = $ExecutionContext.SessionState.Path
241             $path = $p.GetUnresolvedProviderPathFromPSPath('__PATH__')
242             if (Test-Path $path) {
243             exit 0
244             } else {
245             Write-Error -Message '__PATH__ not found'
246             exit 1
247             }
248             CODE
249              
250 0           $code =~ s/__PATH__/$path/g;
251              
252 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
253              
254             }
255              
256             sub _code_mkdir_powershell {
257 0     0     my $self = shift;
258 0           my $path = shift;
259              
260 0           my $code = <<'CODE';
261             $ProgressPreference='SilentlyContinue'
262             $p = $ExecutionContext.SessionState.Path
263             $path = $p.GetUnresolvedProviderPathFromPSPath('__PATH__')
264             if (!(Test-Path $path)) {
265             New-Item -ItemType Directory -Force -Path $path | Out-Null
266             exit 0
267             }
268             Write-Error -Message '__PATH__ not found'
269             exit 1
270             CODE
271              
272 0           $code =~ s/__PATH__/$path/g;
273              
274 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
275              
276             }
277              
278             sub _code_rmdir_powershell {
279 0     0     my $self = shift;
280 0           my $path = shift;
281              
282 0           my $code = <<'CODE';
283             $ProgressPreference='SilentlyContinue'
284             $p = $ExecutionContext.SessionState.Path
285             $path = $p.GetUnresolvedProviderPathFromPSPath('__PATH__')
286             if (Test-Path $path) {
287             Remove-Item $path -Force
288             exit 0
289             }
290             Write-Error -Message '__PATH__ not found'
291             exit 1
292             CODE
293              
294 0           $code =~ s/__PATH__/$path/g;
295              
296 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
297              
298             }
299              
300             sub _code_del_powershell {
301 0     0     my $self = shift;
302 0           my $path = shift;
303              
304 0           my $code = <<'CODE';
305             $ProgressPreference='SilentlyContinue'
306             $p = $ExecutionContext.SessionState.Path
307             $path = $p.GetUnresolvedProviderPathFromPSPath('__PATH__')
308             if (Test-Path $path) {
309             Remove-Item $path -Force
310             exit 0
311             }
312             Write-Error -Message '__PATH__ not found'
313             exit 1
314             CODE
315              
316 0           $code =~ s/__PATH__/$path/g;
317              
318 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
319              
320             }
321              
322             sub _code_dir_powershell {
323 0     0     my $self = shift;
324 0           my $path = shift;
325              
326 0           my $code = <<'CODE';
327             $ProgressPreference='SilentlyContinue'
328             $p = $ExecutionContext.SessionState.Path
329             $path = $p.GetUnresolvedProviderPathFromPSPath('__PATH__')
330             if (Test-Path $path) {
331             Get-ChildItem -Path __PATH__
332             exit 0
333             }
334             Write-Error -Message '__PATH__ not found'
335             exit 1
336             CODE
337              
338 0           $code =~ s/__PATH__/$path/g;
339              
340 0           return encode_base64(encode_unicode('UTF-16LE', $code), '');
341              
342             }
343              
344             1;
345              
346             __END__
347              
348             =head1 NAME
349              
350             XAS::Lib::WS::Transfer - A class to transfer files with WS-Manage
351              
352             =head1 SYNOPSIS
353              
354             use Try::Tiny;
355             use XAS::Lib::WS::Transfer;
356              
357             my $trans = XAs::Lib::WS::Transfer->new(
358             -username => 'Administrator',
359             -password => 'secret',
360             -url => 'http://windowserver:5985/wsman',
361             -auth_method => 'basic',
362             -keep_alive => 1,
363             );
364              
365             try {
366              
367             if ($trans->create) {
368              
369             if ($trans->exists('test.txt')) {
370              
371             $trans->del('test.txt');
372              
373             }
374              
375             $trans->put('junk.txt', 'test.txt');
376              
377             my $output = $trans->dir('.');
378             printf("%s\n", $output);
379              
380             $trans->destroy;
381              
382             }
383              
384             } catch {
385              
386             my $ex = $_;
387             $trans->destroy;
388             die $ex;
389              
390             };
391              
392             =head1 DESCRIPTION
393              
394             This package implements a crude method of performing file operations
395             with a Windows based WS-Manage server. These methods should be wrapped
396             in an exception handling block to trap errors. If not, resources will not
397             be freed on the remote server. You have been warned.
398              
399             =head1 METHODS
400              
401             =head2 new
402              
403             This module inherits from L<XAS::Lib::WS::RemoteShell|XAS::Lib::WS::RemoteShell> and
404             takes the same parameters.
405              
406             =head2 get($remote, $local)
407              
408             Retrieve a file from the remote server. This is very memory intensive
409             operation as the file is converted to base64 and dumped to stdout on the
410             remote end. This blob is then buffered on the local side and converted back
411             to a binary blob before being written out to disk. This method can be used
412             to transfer binary files.
413              
414             =over 4
415              
416             =item B<$local>
417              
418             The name of the local file. Paths are not checked and any existing file
419             will be over written.
420              
421             =item B<$remote>
422              
423             The name of the remote file.
424              
425             =back
426              
427             =head2 put($local, $remote)
428              
429             This method will put a file on the remote server. This is an extremely slow
430             operation. The local file is block read and the buffer is converted to
431             base64. This buffer is then stored within a script that will be executed to
432             convert the blob back into a binary stream. This stream is then appended to
433             the remote file. Not recommended for large files. This method can be used to
434             transfer binary files.
435              
436             =over 4
437              
438             =item B<$local>
439              
440             The name of the local file.
441              
442             =item B<$remote>
443              
444             The name of the remote file. Paths are not checked and any existing file
445             will be appended too.
446              
447             =back
448              
449             =head2 exists($path)
450              
451             This method checks to see if the remote path exists. Returns true if it does.
452              
453             =over 4
454              
455             =item B<$path>
456              
457             The name of the path.
458              
459             =back
460              
461             =head2 del($filename)
462              
463             This method will delete a remote file. Returns true if successfull.
464              
465             =over 4
466              
467             =item B<$filename>
468              
469             The name of the file to delete.
470              
471             =back
472              
473             =head2 mkdir($path)
474              
475             This method will create a directory on the remote server. Intermediate
476             directories are also created. Returns true if successful.
477              
478             =over 4
479              
480             =item B<$path>
481              
482             The path for the directory.
483              
484             =back
485              
486             =head2 rmdir($path)
487              
488             This method will remove a directory for the the remote server. Returns
489             true if successful.
490              
491             =over 4
492              
493             =item B<$path>
494              
495             The name of the directory to remove.
496              
497             =back
498              
499             =head2 dir($path)
500              
501             This method will return a listing of a directory on the remote server.
502             No effort to format the listing is made. This is the raw output.
503              
504             =over 4
505              
506             =item B<$path>
507              
508             The name of the directory to perform the listing on.
509              
510             =back
511              
512             =head1 SEE ALSO
513              
514             =over 4
515              
516             =item L<XAS::Lib::WS::Base|XAS::Lib::WS::Base>
517              
518             =item L<XAS::Lib::WS::RemoteShell|XAS::Lib::WS::RemoteShell>
519              
520             =item L<XAS|XAS>
521              
522             =back
523              
524             =head1 AUTHOR
525              
526             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
527              
528             =head1 COPYRIGHT AND LICENSE
529              
530             Copyright (c) 2012-2016 Kevin L. Esteb
531              
532             This is free software; you can redistribute it and/or modify it under
533             the terms of the Artistic License 2.0. For details, see the full text
534             of the license at http://www.perlfoundation.org/artistic_license_2_0.
535              
536             =cut