File Coverage

blib/lib/Win32/IIS/Admin.pm
Criterion Covered Total %
statement 37 282 13.1
branch 1 104 0.9
condition 0 33 0.0
subroutine 12 27 44.4
pod 11 11 100.0
total 61 457 13.3


line stmt bran cond sub pod time code
1              
2             # $Id: Admin.pm,v 1.24 2008/11/07 00:46:29 Martin Exp $
3              
4             =head1 NAME
5              
6             Win32::IIS::Admin - Administer Internet Information Service on Windows
7              
8             =head1 SYNOPSIS
9              
10             use Win32::IIS::Admin;
11             my $oWIA = new Win32::IIS::Admin;
12             $oWIA->create_virtual_dir(-dir_name => 'cgi-bin',
13             -path => 'C:\wwwroot\cgi-bin',
14             -executable => 1);
15              
16             =head1 DESCRIPTION
17              
18             Enables you to do a few administration tasks on a IIS webserver.
19             Currently only works for IIS 5 (i.e. Windows 2000 Server).
20             Currently there are very few tasks it can do.
21             On non-Windows systems, the module can be loaded, but
22             new() always returns undef.
23              
24             =head1 METHODS
25              
26             =over
27              
28             =cut
29              
30             package Win32::IIS::Admin;
31              
32 4     4   131297 use strict;
  4         10  
  4         136  
33 4     4   23 use warnings;
  4         9  
  4         103  
34              
35 4     4   3419 use Data::Dumper;
  4         30753  
  4         243  
36 4     4   3526 use File::Spec::Functions;
  4         3689  
  4         358  
37 4     4   3495 use IO::String;
  4         18924  
  4         136  
38              
39 4     4   37 use constant DEBUG => 0;
  4         7  
  4         285  
40 4     4   20 use constant DEBUG_EXEC => 0;
  4         9  
  4         153  
41 4     4   20 use constant DEBUG_EXT => 0;
  4         8  
  4         148  
42 4     4   20 use constant DEBUG_FETCH => 0;
  4         7  
  4         139  
43 4     4   53 use constant DEBUG_PARSE => 0;
  4         32  
  4         183  
44 4     4   20 use constant DEBUG_SET => 0;
  4         5  
  4         16488  
45              
46             our
47             $VERSION = do { my @r = (q$Revision: 1.24 $ =~ /\d+/g); sprintf "%d."."%03d" x $#r, @r };
48              
49             =item new
50              
51             Returns a new Win32::IIS::Admin object, or undef if there is any problem
52             (such as, IIS is not installed on the local machine!).
53              
54             =cut
55              
56             sub new
57             {
58 4     4 1 681 my ($class, %parameters) = @_;
59 4 50       27 if ($^O ne 'MSWin32')
60             {
61 4         6 DEBUG && print STDERR " DDD this is not windows\n";
62 4         17 return undef;
63             } # if
64             # Find out where IIS is installed.
65             # Find the cscript executable:
66 0           my (@asTry, $sCscript);
67 0           push @asTry, catfile($ENV{windir}, 'system32', 'cscript.exe');
68 0           foreach my $sTry (@asTry)
69             {
70 0 0         if (-f $sTry)
71             {
72 0           $sCscript = $sTry;
73 0           last;
74             } # if
75             } # foreach
76 0           DEBUG && print STDERR " DDD cscript is ==$sCscript==\n";
77 0 0         if ($sCscript eq '')
78             {
79 0           warn "can not find executable cscript\n";
80 0           return undef;
81             } # if
82             # Get a list of logical drives:
83 0           eval q{use Win32API::File qw( :DRIVE_ )};
84 0 0         if ($@)
85             {
86 0           DEBUG && warn " EEE can not use Win32API::File because $@\n";
87 0           return undef;
88             } # if
89 0           my @asDrive = Win32API::File::getLogicalDrives();
90 0           DEBUG && print STDERR " DDD logical drives are: @asDrive\n";
91             # See which ones are hard drives:
92 0           my @asHD;
93 0           foreach my $sDrive (@asDrive)
94             {
95 0           my $sType = Win32API::File::GetDriveType($sDrive);
96 0 0         push @asHD, $sDrive if ($sType eq eval'DRIVE_FIXED');
97             } # foreach
98 0           DEBUG && print STDERR " DDD hard drives are: @asHD\n";
99             # Find the adsutil.vbs script:
100 0           my $sAdsutil = '';
101 0           @asTry = ();
102             # This is the default location, according to microsoft.com:
103 0           push @asTry, catdir($ENV{windir}, qw( System32 Inetsrv AdminSamples ));
104             # This is where I find it on my old IIS installation:
105 0           push @asTry, map { catdir($_, qw( inetpub AdminScripts )) } @asHD;
  0            
106 0           @asTry = map { catfile($_, 'adsutil.vbs') } @asTry;
  0            
107 0           foreach my $sTry (@asTry)
108             {
109 0 0         if (-f $sTry)
110             {
111 0           $sAdsutil = $sTry;
112 0           last;
113             } # if
114             } # foreach
115 0           DEBUG && print STDERR " DDD adsutil is ==$sAdsutil==\n";
116 0 0         if ($sAdsutil eq '')
117             {
118 0           warn "can not find adsutil.vbs\n";
119 0           return undef;
120             } # if
121             # Now we have all the info we need to get started:
122 0           my %hash = (
123             adsutil => $sAdsutil,
124             cscript => $sCscript,
125             );
126 0   0       my $self = bless (\%hash, ref ($class) || $class);
127 0           return $self;
128             } # new
129              
130              
131             # Not published.
132              
133             sub _config_set_value
134             {
135 0     0     my $self = shift;
136 0           local $" = ',';
137 0           DEBUG_SET && print STDERR " DDD _config_set_value(@_)\n";
138             # Required arg1 = section:
139 0   0       my $sSection = shift || '';
140 0 0         return unless ($sSection ne '');
141             # Required arg2 = parameter name:
142 0   0       my $sParameter = shift || '';
143 0 0         return unless ($sParameter ne '');
144             # Remaining arg(s) will be taken as the value(s) for this parameter.
145 0 0         return unless @_;
146 0           my $sRes = $self->_execute_script('adsutil', 'SET', "$sSection/$sParameter", map { qq/"$_"/ } @_);
  0            
147 0 0         if ($sRes =~ m!ERROR TRYING TO GET THE SCHEMA!i)
148             {
149             # Unknown parameter name:
150 0           $self->_add_error($sRes);
151 0           return;
152             } # if
153 0 0         if ($sRes =~ m!ERROR TRYING TO GET THE OBJECT!i)
154             {
155             # Section does not exist:
156 0           $self->_add_error($sRes);
157 0           return;
158             } # if
159 0 0         if ($sRes =~ m!ERROR TRYING TO SET THE PROPERTY!i)
160             {
161             # Type mismatch
162 0           $self->_add_error($sRes);
163 0           return;
164             } # if
165             # Assume success at this point:
166 0           return '';
167             } # _config_set_value
168              
169              
170             # Not published.
171              
172             sub _config_get_value
173             {
174 0     0     my $self = shift;
175 0           local $" = ',';
176 0           DEBUG_FETCH && print STDERR " DDD _config_get_value(@_)\n";
177             # Required arg1 = section:
178 0   0       my $sSection = shift || '';
179 0 0         return unless ($sSection ne '');
180             # Required arg2 = parameter name:
181 0   0       my $sParameter = shift || '';
182 0 0         return unless ($sParameter ne '');
183 0           my $sRes = $self->_execute_script('adsutil', 'GET', "$sSection/$sParameter");
184 0 0         if ($sRes =~ m!ERROR TRYING TO GET!i)
185             {
186 0           $self->_add_error($sRes);
187 0           return;
188             } # if
189 0           my $oIS = IO::String->new($sRes);
190 0           my $sLine = <$oIS>;
191 0 0         if ($sLine =~ m!\A(\S+)\s+:\s+\((\S+)\)\s*(.+)\Z!)
192             {
193 0           my ($sProperty, $sType, $sValue) = ($1, $2, $3);
194 0           my @asValue;
195 0 0         if ($sType eq 'STRING')
    0          
    0          
    0          
    0          
    0          
196             {
197             # Protect backslashes, in case this value is a dir/file path:
198 0           $sValue =~ s!\\!/!g;
199 0           $sValue = eval $sValue;
200 0           return $sValue;
201             } # if STRING
202             elsif ($sType eq 'INTEGER')
203             {
204 0           $sValue = eval $sValue;
205 0           return $sValue;
206             } # if INTEGER
207             elsif ($sType eq 'EXPANDSZ')
208             {
209             # Protect backslashes, this value is a dir/file path:
210 0           $sValue =~ s!\\!/!g;
211 0           $sValue = eval $sValue;
212 0           $sValue =~ s!%([^%]+)%!$ENV{$1}!g;
213 0           return $sValue;
214             } # if INTEGER
215             elsif ($sType eq 'BOOLEAN')
216             {
217 0           $sValue = ($sValue eq 'True');
218 0           return $sValue;
219             }
220             elsif ($sType eq 'LIST')
221             {
222 0           my @asValue = ();
223 0 0         if ($sValue =~ m!(\d+)\sItems!)
224             {
225 0           my $iCount = 0 + $1;
226             ITEM_OF_LIST:
227 0           for (1..$iCount)
228             {
229 0           my $sSubline = <$oIS>;
230 0 0         if ($sSubline =~ m!\A\s+\042([^"]+)\042!) #
231             {
232 0           push @asValue, $1;
233             } # if
234             else
235             {
236 0           print STDERR " WWW list item does not look like string, in line ==$sLine==\n";
237             }
238             } # for ITEM_OF_LIST
239             } # if
240             else
241             {
242 0           print STDERR " WWW found LIST type but not item count at line ==$sLine==\n";
243 0           next LINE_OF_CONFIG;
244             }
245 0           return \@asValue;
246             } # if LIST
247             elsif ($sType eq 'MimeMapList')
248             {
249 0           my %hash;
250 0           while ($sValue =~ m!"(\S+)"!g)
251             {
252 0           my ($sExt, $sType) = split(',', $1);
253 0           $hash{$sExt} = $sType;
254             } # while
255 0           return \%hash;
256             }
257             else
258             {
259 0           print STDERR " EEE unknown type =$sType=\n";
260             }
261             } # if PropertyName : (TYPE) value
262             else
263             {
264 0           DEBUG_PARSE && print STDERR " WWW unparsable line ==$sLine==\n";
265             }
266 0           return;
267             } # _config_get_value
268              
269              
270             =item iis_version
271              
272             Returns the version of IIS found on this machine,
273             in a decimal number format like "6.0".
274              
275             =cut
276              
277             sub iis_version
278             {
279 0     0 1   my $self = shift;
280 0 0         if (! defined $self->{_iss_version_})
281             {
282 0           my $iMajor = $self->_config_get_value('/W3SVC/Info',
283             'MajorIIsVersionNumber');
284 0           my $iMinor = $self->_config_get_value('/W3SVC/Info',
285             'MinorIIsVersionNumber');
286 0           $self->{_iss_version_} = "$iMajor.$iMinor";
287             } # if
288 0           return $self->{_iss_version_};
289             } # iis_version
290              
291              
292             =item get_timeout
293              
294             Returns the IIS timeout value.
295              
296             =cut
297              
298             sub get_timeout
299             {
300 0     0 1   my $self = shift;
301 0           $self->_config_get_value('/W3SVC', 'CGITimeout');
302             } # set_timeout
303              
304              
305             =item set_timeout
306              
307             Given an integer,
308             sets the IIS timeout to that value.
309             Does no checking on the value passed in, so use carefully!
310              
311             =cut
312              
313             sub set_timeout
314             {
315 0     0 1   my $self = shift;
316             # Required arg1 = an integer:
317 0           my $iArg = shift() + 0;
318 0           $self->_config_set_value('/W3SVC', 'CGITimeout', $iArg);
319             } # set_timeout
320              
321              
322             =item path_of_virtual_dir
323              
324             Given the name of a virtual directory (or 'ROOT'),
325             returns the absolute full path of where the physical files are located.
326             Returns undef if there is no virtual directory matching the name given.
327              
328             =cut
329              
330             sub path_of_virtual_dir
331             {
332 0     0 1   my $self = shift;
333 0   0       my $sDir = shift || '';
334 0 0         if ($sDir eq '')
335             {
336 0           $self->_add_error(qq(Argument is required on path_of_virtual_dir.));
337 0           return;
338             } # if
339             # We cravenly refuse to modify anything but the default #1 webserver:
340 0           my $sWebsite = 1;
341 0 0         if ($sDir eq 'ROOT')
342             {
343 0           goto ROOT;
344             } # if
345 0           my $sVersion = $self->iis_version;
346 0 0         if ("6.0" le $sVersion)
347             {
348 0           my $sSection = join('/', 'W3SVC', $sWebsite);
349 0   0       my $sRes .= $self->_execute_script('iisvdir', '/query', $sSection) || '';
350 0 0         if ($sRes =~ m!Error!)
351             {
352 0           $self->_add_error($sRes);
353 0           return;
354             } # if
355 0           DEBUG_FETCH && print STDERR " DDD iisvdir returned:", $sRes;
356 0           my $oIS = IO::String->new($sRes);
357             FIND_DIVIDER_LINE:
358 0           while (my $sLine = <$oIS>)
359             {
360 0 0         last if ($sLine =~ m!={22}!);
361             } # while FIND_DIVIDER_LINE
362             VIR_DIR_LINE:
363 0           while (my $sLine = <$oIS>)
364             {
365 0           chomp $sLine;
366 0           my ($sVirDir, $sPath) = split(/ +/, $sLine);
367 0           DEBUG_FETCH && print STDERR " DDD found virdir=$sVirDir==>$sPath\n";
368             # Question: do we want to match the vir-dir name
369             # case-INsensitively?
370 0 0         if ($sVirDir =~ m!\A/?$sDir\Z!)
371             {
372 0           return $sPath;
373             } # if
374             } # while VIR_DIR_LINE
375 0           return '';
376             } # if
377             ROOT:
378             # If we get here, we must be using IIS 5.0:
379 0           my $sSection = join('/', '', 'W3SVC', $sWebsite, 'ROOT');
380 0 0         if ($sDir !~ m!\AROOT\Z!i)
381             {
382 0           $sSection .= "/$sDir";
383             } # if
384 0   0       my $sPath = $self->_config_get_value($sSection, 'Path') || '';
385 0           return $sPath;
386             } # path_of_virtual_dir
387              
388              
389             =item create_virtual_dir
390              
391             Given the following named arguments, create a virtual directory on the
392             default #1 server on the local machine's IIS instance.
393              
394             =over
395              
396             =item -dir_name => 'virtual'
397              
398             This is the virtual directory name as it will appear to your browsers.
399              
400             =item -path => 'C:/local/path'
401              
402             This is the full path the the actual location of the data files.
403              
404             =item -executable => 1
405              
406             Give this argument if your virtual directory holds executable programs.
407             Default is 0 (NOT executable).
408              
409             =back
410              
411             =cut
412              
413             sub create_virtual_dir
414             {
415 0     0 1   my $self = shift;
416 0           my %hArgs = @_;
417 0   0       $hArgs{-dir_name} ||= '';
418 0 0         if ($hArgs{-dir_name} eq '')
419             {
420 0           $self->_add_error(qq(Argument -dir_name is required on create_virtual_dir.));
421 0           return;
422             } # if
423 0   0       $hArgs{-path} ||= '';
424 0 0         if ($hArgs{-path} eq '')
425             {
426 0           $self->_add_error(qq(Argument -path is required on create_virtual_dir.));
427 0           return;
428             } # if
429 0   0       $hArgs{-executable} ||= 0;
430             # print STDERR Dumper(\%hArgs);
431             # We cravenly refuse to modify anything but the default #1 webserver:
432 0           my $sWebsite = 1;
433             # First, see if a virtual directory with the same name is already
434             # exists:
435 0           my $sPath = $self->path_of_virtual_dir($hArgs{-dir_name});
436 0           my $sRes = '';
437 0 0         if ($sPath ne '')
438             {
439             # There is already a virtual directory with that name. Create a
440             # sensible error message:
441 0 0         if ($sPath ne $hArgs{-path})
442             {
443 0           $self->_add_error(qq(There is already a virtual directory named '$hArgs{-dir_name}', but it points to $sPath));
444 0           return;
445             } # if
446 0           $self->_add_error(qq(There is already a virtual directory named '$hArgs{-dir_name}' pointing to $sPath));
447             # Fall through and (try to) set the access rules.
448             } # if
449             else
450             {
451             # Virtual dir not there, create it:
452 0           my @asArgs = ('mkwebdir',
453             qq(-v "$hArgs{-dir_name}","$hArgs{-path}"),
454             qq(-w $sWebsite),
455             # qq(-c $sComputer),
456             );
457 0 0         if ('6.0' le $self->iis_version)
458             {
459 0           @asArgs = ('iisvdir', '/create', "W3SVC/$sWebsite",
460             $hArgs{-dir_name}, $hArgs{-path});
461             } # if
462 0   0       $sRes .= $self->_execute_script(@asArgs) || '';
463 0 0         if ($sRes =~ m!Error!)
464             {
465 0           $self->_add_error($sRes);
466 0           return;
467             } # if
468             } # else
469             # Whether the dir was already defined or not, try to set permissions
470             # as requested:
471 0 0         if ($hArgs{-executable})
472             {
473 0           my $sSection = join('/', '', 'W3SVC', $sWebsite, 'Root', $hArgs{-dir_name});
474 0 0         if ('6.0' le $self->iis_version)
475             {
476 0           $sRes .= $self->_config_set_value($sSection, "AccessExecute", 'True');
477             # These seem to get turned on by default, but we'll make them
478             # explicit anyway:
479 0           $sRes .= $self->_config_set_value($sSection, "AccessScript", 'True');
480 0           $sRes .= $self->_config_set_value($sSection, "AccessRead", 'True');
481             }
482             else
483             {
484             # For some reason, the argument to chaccess has no leading slash
485             # (some other scripts require leading slash):
486 0           $sSection =~ s!\A/!!;
487             # Set accesses for execution:
488 0           $sRes .= $self->_execute_script('chaccess',
489             -a => $sSection,
490             qw( +execute +read +script ),
491             );
492             } # else
493             } # if
494 0           return $sRes;
495             } # create_virtual_dir
496              
497              
498             =item add_extension_restriction
499              
500             Given the following named arguments,
501             adds an "extension restriction" to
502             the default #1 server on the local machine's IIS instance.
503             Only works on IIS version 6.0.
504             Note: no checking is done on the arguments,
505             so it is possible to add bogus/duplicate/conflicting/illegal values to your IIS configuration.
506             For more information, see
507             http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/79652e88-e713-4aa5-a88c-8e2bd6a2955e.mspx?mfr=true
508              
509             =over
510              
511             =item -allow => <0, 1>
512              
513             Send 0 if this is a "deny" rule; send 1 if this is an "allow" rule.
514             The default is 0, deny.
515              
516             =item -path =>
517              
518             The full path to the executable or extension.
519             This argument is required.
520              
521             =item -groupid =>
522              
523             "A non-localizable string used to identify groups of extensions."
524             Default is empty string.
525              
526             =item -description =>
527              
528             "A localizable description of the extension."
529             Default is empty string.
530              
531             =back
532              
533             =cut
534              
535             sub add_extension_restriction
536             {
537 0     0 1   my $self = shift;
538             # print STDERR " DDD add_extension_restriction()\n";
539 0 0         if ($self->iis_version < 6.0)
540             {
541 0           return;
542             } # if
543             # Set defaults, and get arguments:
544 0           my %hArgs = (
545             -allow => 0,
546             -groupid => '',
547             -description => '',
548             @_,
549             # At present, this argument is not alterable:
550             -deletable => 1,
551             );
552             # Verify all argument values:
553 0 0         $hArgs{-allow} = 0 if ($hArgs{-allow} ne '1');
554 0 0         if (! exists $hArgs{-path})
555             {
556 0           $self->add_error("add_extension_restriction() called without required argument -path");
557 0           return;
558             } # if
559             # Construct the new Registry value:
560 0           my $s = join(',', @hArgs{qw( -allow -path -deletable -groupid -description )});
561             # print STDERR " DDD s=$s=\n";
562 0           my $ra = $self->_config_get_value('/W3SVC', 'WebSvcExtRestrictionList');
563             # print STDERR " DDD before, list is ", Dumper($ra);
564 0           push @{$ra}, $s;
  0            
565 0           $self->_config_set_value('/W3SVC', 'WebSvcExtRestrictionList', @{$ra});
  0            
566             } # add_extension_restriction
567              
568              
569             =item remove_extension_restriction
570              
571             Given the full path of an existing "extension restriction" in
572             the default #1 server on the local machine's IIS instance,
573             removes that restriction.
574             If more than one restriction refers to the same path,
575             they will all be removed.
576             Only works on IIS version 6.0.
577              
578             =cut
579              
580             sub remove_extension_restriction
581             {
582 0     0 1   my $self = shift;
583             # Required arg1 = path element:
584 0   0       my $sPath = shift || '';
585 0           DEBUG_EXT && print STDERR " DDD remove_extension_restriction($sPath)\n";
586 0           $self->_remove_extension_restriction_by_elem($sPath, 1);
587             } # remove_extension_restriction
588              
589              
590             =item remove_extension_restriction_group
591              
592             Given the group ID of an existing "extension restriction" in
593             the default #1 server on the local machine's IIS instance,
594             removes all restrictions of that group.
595             Only works on IIS version 6.0.
596              
597             =cut
598              
599             sub remove_extension_restriction_group
600             {
601 0     0 1   my $self = shift;
602             # Required arg1 = path element:
603 0   0       my $sValue = shift || '';
604 0           DEBUG_EXT && print STDERR " DDD remove_extension_restriction_group($sValue)\n";
605 0           $self->_remove_extension_restriction_by_elem($sValue, 3);
606             } # remove_extension_restriction_group
607              
608              
609             sub _remove_extension_restriction_by_elem
610             {
611 0     0     my $self = shift;
612             # Required arg1 = path element:
613 0   0       my $sValue = shift || '';
614             # Required arg2 = element number:
615 0           my $iElem = shift;
616             # Verify all argument values:
617 0 0         return if ! defined($iElem);
618 0 0         return if ($iElem < 0);
619 0 0         return if (4 < $iElem);
620 0 0         if ($sValue eq '')
621             {
622 0           return;
623             } # if
624 0 0         if ($self->iis_version < 6.0)
625             {
626 0           return;
627             } # if
628 0           my $rasOrig = $self->_config_get_value('/W3SVC', 'WebSvcExtRestrictionList');
629 0           DEBUG_EXT && print STDERR " DDD before, list is ", Dumper($rasOrig);
630 0           my @asNew;
631 0           foreach my $s (@$rasOrig)
632             {
633 0           my @asElem = split(',', $s);
634 0 0 0       if (($asElem[$iElem] || '') eq $sValue)
635             {
636 0           DEBUG_EXT && print STDERR " DDD found one to remove\n";
637             }
638             else
639             {
640 0           push @asNew, $s;
641             }
642             } # foreach
643 0           DEBUG_EXT && print STDERR " DDD after, list is ", Dumper(\@asNew);
644 0           $self->_config_set_value('/W3SVC', 'WebSvcExtRestrictionList', @asNew);
645             } # _remove_extension_restriction_by_elem
646              
647              
648             =item restart_iis
649              
650             Restarts the IIS service on the local machine.
651             Assumes that IISReset.exe is in your path.
652              
653             =cut
654              
655             sub restart_iis
656             {
657 0     0 1   my $self = shift;
658             # Assume that IISReset is in the path:
659 0           my $sProg = 'IISReset';
660 0           my $iRes = system(qq'$sProg /RESTART');
661 0 0         if ($iRes)
662             {
663             # print STDERR "$sProg failed: $!"; # for debugging
664 0           $self->add_error("$sProg failed: $!");
665             } # if
666             } # restart_iis
667              
668              
669             =item errors
670              
671             Method not implemented.
672             In the current version, error messages are printed to STDERR as they occur.
673              
674             =cut
675              
676             sub errors
677 0     0 1   {
678             } # errors
679              
680             sub _add_error
681             {
682 0     0     my $self = shift;
683 0           print STDERR "@_\n";
684             } # add_error
685              
686             sub _execute_script
687             {
688 0     0     my $self = shift;
689 0           my $sVBS = shift;
690             # Figure out exactly which script the caller wants to execute.
691             # Cscript needs the full path:
692 0           my $sScriptFname;
693 0 0         if (defined $self->{$sVBS})
694             {
695             # User requested a script which we have already located.
696 0           $sScriptFname = $self->{$sVBS};
697             }
698             else
699             {
700             # adsutil.vbs is the only script we bother to physically locate;
701             # all other scripts are next to cscript itself:
702 0           $sScriptFname = $self->{cscript};
703 0           $sScriptFname =~ s!cscript\.exe!$sVBS.vbs!i;
704             }
705 0           my $sCmd = join(' ', $self->{cscript}, '-nologo', $sScriptFname, @_);
706 0           DEBUG_EXEC && print STDERR " DDD exec ==$sCmd==\n";
707 0           my $sRes = qx/$sCmd/;
708 0           print STDERR " DDD result ===$sRes===\n" if (1 < DEBUG_EXEC);
709 0           return $sRes;
710             } # _execute_script
711              
712             =back
713              
714             =head1 BUGS
715              
716             To report a bug, please use L.
717              
718             =head1 AUTHOR
719              
720             Martin Thurn C
721              
722             =head1 COPYRIGHT
723              
724             This program is free software; you can redistribute
725             it and/or modify it under the same terms as Perl itself.
726              
727             =cut
728              
729             1;
730              
731             __END__