File Coverage

blib/lib/Net/FTPTurboSync/PrgOpts.pm
Criterion Covered Total %
statement 3 210 1.4
branch 0 106 0.0
condition 0 9 0.0
subroutine 1 15 6.6
pod 0 14 0.0
total 4 354 1.1


line stmt bran cond sub pod time code
1             package Net::FTPTurboSync::PrgOpts;
2              
3 1     1   1018 use Net::Netrc;
  1         15173  
  1         2964  
4             # singleton :)
5             our $theOpts = undef;
6              
7             sub new {
8 0     0 0   my ($class, $argv ) = @_;
9 0           my $self = {
10             dbh=>undef,
11             newDB=>0,
12             dbpath=>"",
13             uildDB=>0,
14             nodelete=>0,
15             returncode=>0,
16             configfile=>$ENV{"HOME"}."/.turbo-ftp-sync",
17             # basics
18             localdir=>"",
19             remoteURL=>"",
20             ftpuser=>"anonymous",
21             ftppasswd=>"anonymos",
22             ftpserver=>"localhost",
23             ftpdir=>"",
24             maxerrors=> 3,
25             ftptimeout=>120,
26             # verbosity
27             doverbose=>1,
28             dodebug=>0,
29             doquiet=>0,
30             doinfoonly=>0,
31             infotext=>"",
32             docheckfirst=>0,
33             ignoremask => "",
34             followsymlinks=>0,
35             doflat=>0,
36             };
37 0           bless $self, $class;
38 0           $self->parseCommandLineParameters( $argv );
39 0           return $self;
40             }
41              
42             # return cfgfoptions
43             sub readConfigFile {
44 0     0 0   my ( $self ) = @_;
45 0           my @cfgfoptions=();
46 0 0         if ($self->{configfile} ne "") {
47 0 0         if (-r $self->{configfile}) {
48             #print "Reading config file.\n"; # For major problem debugging
49 0           open (CONFIGFILE,"<$self->{configfile}");
50 0           while () {
51 0           $_ =~ s/([ \n\r]*$|\.\.|#.*$)//gs;
52 0 0         if ($_ eq "") { next; }
  0            
53 0 0 0       if ( ($_ =~ /[^=]+=[^=]+/) || ($_ =~ /^-[a-zA-Z]+$/) ) { push @cfgfoptions, $_; }
  0            
54             }
55 0           close (CONFIGFILE);
56             } # else { print "Config file does not exist.\n"; } # For major problem debugging
57             } # else { print "No config file to read.\n"; } # For major problem debugging
58 0           return \@cfgfoptions;
59             }
60              
61             sub print_options() {
62 0     0 0   my ( $self ) = @_;
63 0           print "\nPrinting options:\n";
64             # meta
65 0           print "returncode = ", $self->{returncode} , "\n";
66 0           print "configfile = ", $self->{configfile} , "\n";
67             # basiscs
68 0           print "localdir = ", $self->{localdir} , "\n";
69             # FTP stuff
70 0           print "remoteURL = ", $self->{remoteURL} , "\n";
71 0           print "ftpuser = ", $self->{ftpuser} , "\n";
72 0           print "ftppasswd = ", $self->{ftppasswd} , "\n";
73 0           print "ftpserver = ", $self->{ftpserver} , "\n";
74 0           print "ftpdir = ", $self->{ftpdir} , "\n";
75             # verbsityosity
76 0           print "doverbose = ", $self->{doverbose} , "\n";
77 0           print "dodebug = ", $self->{dodebug} , "\n";
78 0           print "doquiet = ", $self->{doquiet} , "\n";
79             # print "db = ", $dbh , "\n";
80             #
81 0           print "doinfoonly = ", $self->{doinfoonly} , "\n";
82 0           print "\n";
83             }
84              
85             sub print_syntax() {
86 0     0 0   print "\n";
87 0           print "turbo-ftp-sync.pl $VERSION (2011-05-12)\n";
88 0           print "author is Daneel S. Yaitskov ( rtfm.rtfm.rtfm\@gmail.com )\n";
89 0           print "\n";
90 0           print " turbo-ftp-sync [ options ] [ localdir remoteURL ]\n";
91 0           print " options = [-dfgpqv] [ cfg|ftpuser|ftppasswd|ftpserver|ftpdir=value ... ] \n";
92 0           print " localdir local directory, defaults to \".\".\n";
93 0           print " remoteUrl full FTP URL, scheme\n";
94 0           print ' ftp://[ftpuser[:ftppasswd]@]ftpserver/ftpdir'."\n";
95 0           print " ftpdir is relative, so double / for absolute paths as well as /\n";
96 0           print " -c | -C like -i, but then prompts whether to actually do work\n";
97 0           print " -d | -D turns debug output (including verbose output) on\n";
98 0           print " -f | -F flat operation, no subdir recursion\n";
99 0           print " -h | -H prints out this help text\n";
100 0           print " -i | -I forces info mode, only telling what would be done\n";
101 0           print " -n | -N no deletion of obsolete files or directories\n";
102 0           print " -l | -L follow local symbolic links as if they were directories\n";
103 0           print " -q | -Q turns quiet operation on\n";
104 0           print " -b | -B build DB only - i.e don't upload data to remote host\n";
105 0           print " -v | -V turnes verbose output on\n";
106 0           print " maxerrors= if not 0 then program exit with nonzero code.\n";
107 0           print " cfg= read parameters and options from file defined by value.\n";
108 0           print " ftpserver= defines the FTP server, defaults to \"localhost\".\n";
109 0           print " ftpuser= defines the FTP user, defaults to \"ftp\".\n";
110 0           print " db= defines the file where info about uploaded files is stored.\n";
111 0           print " ftppasswd= defines the FTP password, defaults to \"anonymous\".\n";
112 0           print " ignoremask= defines a regexp to ignore certain files, like .svn"."\n";
113 0           print "\n";
114 0           print " Later mentioned options and parameters overwrite those mentioned earlier.\n";
115 0           print " Command line options and parameters overwrite those in the config file.\n";
116 0           print " Don't use '\"', although mentioned default values might motiviate you to.\n";
117 0           print "\n";
118 0           print " If ftpuser or ftppasswd resovle to ? (no matter through which options),\n";
119 0           print " turbo-ftp-sync.pl asks you for those interactively.\n";
120 0           print "\n";
121 0           print " PROGRAM CAN UPLOAD CHANGES ONLY IN ONE DIRECTION\n";
122 0           print " FROM YOUR MACHINE TO REMOTE MACHINE.\n";
123 0           print " ALSO PROGRAM CANNOT KNOW ABOUT CHANGES WERE MADE ON A REMOTE MACHINE.\n";
124 0           print "\n";
125 0           print " Demo usage: turbo-ftp-sync.pl db=db.db webroot ftp://yaitskov:secret\@ftp.vosi.biz//\n";
126 0           print "\n";
127             }
128              
129             sub parseParameters {
130 0     0 0   my ( $self, $curopt ) = @_;
131 0           $self->{noofopts}++;
132 0           my ($fname, $fvalue) = split /=/, $curopt, 2;
133 0 0         if ($fname eq "cfg") { return; }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
134             elsif ($fname eq "ftpdir") {
135 0           $self->{ftpdir} =$fvalue;
136 0 0         if ($self->{ftpdir} ne "/") { $self->{ftpdir}=~s/\/$//; }
  0            
137             }
138             elsif ($fname =~ m/ftppass(w(or)?d)?/i) {
139 0           $self->{ftppasswd}=$fvalue;
140             }
141             elsif ($fname eq "ftpserver") {
142 0           $self->{ftpserver} =$fvalue;
143             }
144             elsif ($fname eq "ftpuser") {
145 0           $self->{ftpuser} =$fvalue;
146             }elsif ( $fname eq "maxerrors" ){
147 0 0         if ( $fvalue =~ /^[0-9]{1,3}$/ ){
148 0           $self->{maxerrors} = $fvalue;
149             }else {
150 0           $self->{returncode} += 1;
151 0           print STDERR "maxerrors must non-negative integer but got: '$fvalue'\n" ;
152             }
153             }elsif ($fname eq "localdir") {
154 0           $self->{localdir} =$fvalue; $self->{localdir}=~s/\/$//;
  0            
155             }
156 0 0         elsif ($fname eq "timeout") { if ($fvalue>0) { $self->{ftptimeout} =$fvalue; } }
  0            
157 0           elsif ($fname eq "ignoremask") { $self->{ignoremask} = $fvalue; }
158             elsif ($fname eq "db" ){
159 0           $self->{dbpath} = $fvalue;
160             }
161             }
162             sub parseFtpParameter {
163 0     0 0   my ( $self, $curopt ) = @_;
164 0           $self->{noofopts}++;
165 0           $self->{remoteURL} = $curopt;
166 0           $self->parseRemoteURL();
167             }
168             sub parseRemoteURL() {
169 0     0 0   my ( $self ) = @_;
170 0 0         if ($self->{remoteURL} =~ /^ftp:\/\/(([^@\/\\\:]+)(:([^@\/\\\:]+))?@)?([a-zA-Z01-9\.\-]+)\/(.*)/) {
171             #print "DEBUG: parsing ".$remoteURL."\n";
172             #print "match 1 = ".$1."\n";
173             #print "match 2 = ".$2."\n";
174             #print "match 3 = ".$3."\n";
175             #print "match 4 = ".$4."\n";
176             #print "match 5 = ".$5."\n";
177             #print "match 6 = ".$6."\n";
178             #print "match 7 = ".$7."\n";
179 0 0         if (length($2) > 0) { $self->{ftpuser} = $2; }
  0            
180 0 0         if (length($4) > 0) { $self->{ftppasswd} = $4; }
  0            
181 0           $self->{ftpserver} = $5;
182 0           $self->{ftpdir} = $6;
183 0 0         if ($self->{ftpdir} ne "/") { $self->{ftpdir}=~s/\/$//; }
  0            
184             }
185             }
186              
187             sub parseOptions {
188 0     0 0   my ( $self, $curopt ) = @_;
189 0           my $i;
190 0           for ($i=1; $i < length($curopt); $i++) {
191 0           my $curoptchar = substr ($curopt, $i, 1);
192 0           $self->{noofopts}++;
193 0 0         if ($curoptchar =~ /[cC]/) { $self->{docheckfirst}=1; }
  0 0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
194 0           elsif ($curoptchar =~ /[dD]/) { $self->{dodebug}=1; $self->{doverbose}=3; $self->{doquiet}=0; }
  0            
  0            
195 0           elsif ($curoptchar =~ /[fF]/) { $self->{doflat}=1; }
196 0           elsif ($curoptchar =~ /[hH?]/) { $self->print_syntax(); exit 0; }
  0            
197 0           elsif ($curoptchar =~ /[iI]/) { $self->{doinfoonly}=1; }
198 0           elsif ($curoptchar =~ /[lL]/) { $self->{followsymlinks}=1; }
199 0           elsif ($curoptchar =~ /[qQ]/) { $self->{dodebug}=0; $self->{doverbose}=0; $self->{doquiet}=1; }
  0            
  0            
200 0           elsif ($curoptchar =~ /[vV]/) { $self->{doverbose}++; }
201 0           elsif ($curoptchar =~ /[nN]/) { $self->{nodelete}=1; }
202 0           elsif ($curoptchar =~ /[bB]/) { $self->{buildDB} = 1 ; }
203 0           else { print "ERROR: Unknown option: \"-".$curoptchar."\"\n"; $self->{returncode}+=1; }
  0            
204             }
205             }
206             sub parseLocalDir {
207 0     0 0   my ( $self, $curopt ) = @_;
208 0 0         if ($self->{localdir} eq "") {
209 0           $self->{noofopts}++;
210 0           $self->{localdir} = $curopt;
211             } else {
212 0           print "ERROR: Unknown parameter: \"".$curopt."\"\n"; $self->{returncode}+=1
  0            
213             }
214             }
215             # function has side effect; return nothing
216             # it changes variables of current package
217             sub parseOptionsAndParameters {
218 0     0 0   my ( $self, $cfgfoptions, $cloptions ) = @_;
219 0           $self->{noofopts}=0;
220 0           for my $curopt (@$cfgfoptions, @$cloptions) {
221 0 0         if ($curopt =~ /^-[a-zA-Z]/) {
    0          
    0          
222 0           $self->parseOptions( $curopt );
223             }
224             elsif ($curopt =~ /^ftp:\/\/(([^@\/\\\:]+)(:([^@\/\\\:]+))?@)?([a-zA-Z01-9\.\-]+)\/(.*)/) {
225 0           $self->parseFtpParameter ( $curopt );
226             }
227             elsif ($curopt =~ /^[a-z]+=.+/) {
228 0           $self->parseParameters ( $curopt );
229             }
230             else {
231 0           $self->parseLocalDir ( $curopt );
232             }
233             }
234 0 0         if (0 == $self->{noofopts}) { $self->print_syntax(); exit 0; }
  0            
  0            
235             }
236             sub parseCfg {
237 0     0 0   my ( $self, $argv ) = @_ ;
238 0           my @cloptions=();
239 0           for my $curopt (@ARGV) {
240 0 0         if ($curopt =~ /^cfg=/) {
241 0           $self->{configfile}="$'";
242 0 0         if (! -r $self->{configfile}) {
243 0           print "Config file does not exist: "
244             . $self->{configfile} . "\n";
245 0           $self->{returncode} += 1;
246             }
247             } else {
248 0           push @cloptions, $curopt;
249             }
250             }
251 0           return \@cloptions;
252             }
253             sub netRC {
254 0     0 0   my ( $self ) = @_;
255 0 0 0       if ( ($self->{ftpserver} ne "") and ($self->{ftppasswd} eq "anonymous") ) {
256 0 0         if ($self->{ftpuser} eq "ftp") {
257 0           my $netrcdata = Net::Netrc->lookup($self->{ftpserver});
258 0 0         if ( defined $netrcdata ) {
259 0           $self->{ftpuser} = $netrcdata->login;
260 0           $self->{ftppasswd} = $netrcdata->password;
261             }
262             } else {
263 0           my $netrcdata = Net::Netrc->lookup($self->{ftpserver},$self->{ftpuser});
264 0 0         if ( defined $netrcdata ) {
265 0           $self->{ftppasswd} = $netrcdata->password;
266             }
267             }
268             }
269             }
270             sub validateFtp {
271 0     0 0   my ( $self ) = @_;
272 0 0         if ($self->{ftpuser} eq "?") { print "User: "; $self->{ftpuser}=; chomp($self->{ftpuser}); }
  0            
  0            
  0            
273 0 0         if ($self->{ftppasswd} eq "?") { print "Password: "; $self->{ftppasswd}=; chomp($self->{ftppasswd}); }
  0            
  0            
  0            
274 0 0         if ($self->{ftpserver} eq "") { print "ERROR: No FTP server given.\n"; $self->{returncode}+=1; }
  0            
  0            
275 0 0         if ($self->{ftpdir} eq "") { print "ERROR: No FTP directory given.\n"; $self->{returncode}+=1; }
  0            
  0            
276 0 0         if ($self->{ftpuser} eq "") { print "ERROR: No FTP user given.\n"; $self->{returncode}+=1; }
  0            
  0            
277 0 0         if ($self->{ftppasswd} eq "") { print "ERROR: No FTP password given.\n"; $self->{returncode}+=1; }
  0            
  0            
278             }
279              
280             sub parseCommandLineParameters {
281 0     0 0   my ( $self, $argv ) = @_;
282 0           my $cloptions = $self->parseCfg ( $argv );
283 0           my $cfgfoptions = $self->readConfigFile ();
284 0           $self->parseOptionsAndParameters( $cfgfoptions, $cloptions );
285 0 0         if ( $self->{dbpath} eq "" ){
286 0           die "Required path to a file with the database (use parameter db=)";
287             }
288 0 0         if ( $self->{dodebug} ) { $self->print_options(); }
  0            
289 0 0 0       if ( ($self->{localdir} eq "" ) || (! -d $self->{localdir} ) ) {
290 0           print "ERROR: Local directory does not exist: '$self->{localdir}'\n";
291 0           $self->{returncode}+=1;
292             }
293 0           $self->{newDB} = ! -f $self->{dbpath};
294 0 0         if ( ! $self->{buildDB} ) {
295 0           $self->netRC();
296 0           $self->validateFtp();
297             }
298 0 0         if ($self->{returncode} > 0) {
299 0           die "Aborting due to missing or wrong options!"
300             . "Call turbo-ftp-sync -? for more information.\n";
301             }
302             }
303              
304             1;