1package ProFTPD::Tests::Modules::mod_ratio;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use File::Spec;
8use IO::Handle;
9
10use ProFTPD::TestSuite::FTP;
11use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite);
12
13$| = 1;
14
15my $order = 0;
16
17my $TESTS = {
18  ratio_bug3600 => {
19    order => ++$order,
20    test_class => [qw(bug forking)],
21  },
22
23  ratio_after_disconnect => {
24    order => ++$order,
25    test_class => [qw(bug forking)],
26  },
27
28  ratio_userratio_with_credit => {
29    order => ++$order,
30    test_class => [qw(bug forking)],
31  },
32
33};
34
35sub new {
36  return shift()->SUPER::new(@_);
37}
38
39sub list_tests {
40  return testsuite_get_runnable_tests($TESTS);
41}
42
43sub ratio_bug3600 {
44  my $self = shift;
45  my $tmpdir = $self->{tmpdir};
46
47  my $config_file = "$tmpdir/ratio.conf";
48  my $pid_file = File::Spec->rel2abs("$tmpdir/ratio.pid");
49  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ratio.scoreboard");
50
51  my $log_file = test_get_logfile();
52
53  my $auth_user_file = File::Spec->rel2abs("$tmpdir/ratio.passwd");
54  my $auth_group_file = File::Spec->rel2abs("$tmpdir/ratio.group");
55
56  my $user = 'proftpd';
57  my $passwd = 'test';
58  my $group = 'ftpd';
59  my $home_dir = File::Spec->rel2abs($tmpdir);
60  my $uid = 500;
61  my $gid = 500;
62
63  # Make sure that, if we're running as root, that the home directory has
64  # permissions/privs set for the account we create
65  if ($< == 0) {
66    unless (chmod(0755, $home_dir)) {
67      die("Can't set perms on $home_dir to 0755: $!");
68    }
69
70    unless (chown($uid, $gid, $home_dir)) {
71      die("Can't set owner of $home_dir to $uid/$gid: $!");
72    }
73  }
74
75  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
76    '/bin/bash');
77  auth_group_write($auth_group_file, $group, $gid, $user);
78
79  my $ratio_file = File::Spec->rel2abs("$home_dir/.ratio");
80  if (open(my $fh, "> $ratio_file")) {
81    unless (close($fh)) {
82      die("Can't write $ratio_file: $!");
83    }
84
85  } else {
86    die("Can't open $ratio_file: $!");
87  }
88
89  my $config = {
90    PidFile => $pid_file,
91    ScoreboardFile => $scoreboard_file,
92    SystemLog => $log_file,
93    TraceLog => $log_file,
94    Trace => 'response:10',
95
96    AuthUserFile => $auth_user_file,
97    AuthGroupFile => $auth_group_file,
98
99    IfModules => {
100      'mod_delay.c' => {
101        DelayEngine => 'off',
102      },
103
104      'mod_ratio.c' => {
105        Ratios => 'on',
106      },
107    },
108  };
109
110  my ($port, $config_user, $config_group) = config_write($config_file, $config);
111
112  # Open pipes, for use between the parent and child processes.  Specifically,
113  # the child will indicate when it's done with its test by writing a message
114  # to the parent.
115  my ($rfh, $wfh);
116  unless (pipe($rfh, $wfh)) {
117    die("Can't open pipe: $!");
118  }
119
120  my $ex;
121
122  # Fork child
123  $self->handle_sigchld();
124  defined(my $pid = fork()) or die("Can't fork: $!");
125  if ($pid) {
126    eval {
127      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
128      $client->login($user, $passwd);
129
130      my $resp_code = $client->response_code();
131      my $resp_msg = $client->response_msg(0);
132
133      my $expected;
134
135      $expected = 230;
136      $self->assert($expected == $resp_code,
137        test_msg("Expected response code $expected, got $resp_code"));
138
139      $expected = "Down: 0 Files (0mb)  Up: 0 Files (0mb)  10,000,000:1  CR: LEECH";
140      $self->assert($expected eq $resp_msg,
141        test_msg("Expected response message '$expected', got '$resp_msg'"));
142
143      ($resp_code, $resp_msg) = $client->syst();
144
145      $expected = 215;
146      $self->assert($expected == $resp_code,
147        test_msg("Expected response code $expected, got $resp_code"));
148
149      $expected = "UNIX Type: L8";
150      $self->assert($expected eq $resp_msg,
151        test_msg("Expected response message '$expected', got '$resp_msg'"));
152
153      $client->quit();
154    };
155
156    if ($@) {
157      $ex = $@;
158    }
159
160    $wfh->print("done\n");
161    $wfh->flush();
162
163  } else {
164    eval { server_wait($config_file, $rfh) };
165    if ($@) {
166      warn($@);
167      exit 1;
168    }
169
170    exit 0;
171  }
172
173  # Stop server
174  server_stop($pid_file);
175
176  $self->assert_child_ok($pid);
177
178  if ($ex) {
179    test_append_logfile($log_file, $ex);
180    unlink($log_file);
181
182    die($ex);
183  }
184
185  unlink($log_file);
186}
187
188sub ratio_after_disconnect {
189  my $self = shift;
190  my $tmpdir = $self->{tmpdir};
191
192  my $config_file = "$tmpdir/ratio.conf";
193  my $pid_file = File::Spec->rel2abs("$tmpdir/ratio.pid");
194  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ratio.scoreboard");
195
196  my $log_file = test_get_logfile();
197
198  my $auth_user_file = File::Spec->rel2abs("$tmpdir/ratio.passwd");
199  my $auth_group_file = File::Spec->rel2abs("$tmpdir/ratio.group");
200
201  my $user = 'proftpd';
202  my $passwd = 'test';
203  my $group = 'ftpd';
204  my $home_dir = File::Spec->rel2abs($tmpdir);
205  my $uid = 500;
206  my $gid = 500;
207
208  # Make sure that, if we're running as root, that the home directory has
209  # permissions/privs set for the account we create
210  if ($< == 0) {
211    unless (chmod(0755, $home_dir)) {
212      die("Can't set perms on $home_dir to 0755: $!");
213    }
214
215    unless (chown($uid, $gid, $home_dir)) {
216      die("Can't set owner of $home_dir to $uid/$gid: $!");
217    }
218  }
219
220  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
221    '/bin/bash');
222  auth_group_write($auth_group_file, $group, $gid, $user);
223
224  my $ratio_file = File::Spec->rel2abs("$home_dir/ratios.dat");
225  if (open(my $fh, "> $ratio_file")) {
226    unless (close($fh)) {
227      die("Can't write $ratio_file: $!");
228    }
229
230  } else {
231    die("Can't open $ratio_file: $!");
232  }
233
234  my $ratio_tmp_file = File::Spec->rel2abs("$home_dir/ratios.tmp");
235
236  my $config = {
237    PidFile => $pid_file,
238    ScoreboardFile => $scoreboard_file,
239    SystemLog => $log_file,
240    TraceLog => $log_file,
241    Trace => 'response:10',
242
243    AuthUserFile => $auth_user_file,
244    AuthGroupFile => $auth_group_file,
245
246    IfModules => {
247      'mod_delay.c' => {
248        DelayEngine => 'off',
249      },
250
251      'mod_ratio.c' => {
252        Ratios => 'on',
253        SaveRatios => 'on',
254        RatioFile => $ratio_file,
255        RatioTempFile => $ratio_tmp_file,
256        CwdRatioMsg => "Please Upload first",
257        FileRatioErrMsg => "Come on, gimme a lil more",
258        ByteRatioErrMsg => "COME ON NOW",
259      },
260    },
261  };
262
263  my ($port, $config_user, $config_group) = config_write($config_file, $config);
264
265  # Open pipes, for use between the parent and child processes.  Specifically,
266  # the child will indicate when it's done with its test by writing a message
267  # to the parent.
268  my ($rfh, $wfh);
269  unless (pipe($rfh, $wfh)) {
270    die("Can't open pipe: $!");
271  }
272
273  my $ex;
274
275  # Fork child
276  $self->handle_sigchld();
277  defined(my $pid = fork()) or die("Can't fork: $!");
278  if ($pid) {
279    eval {
280      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
281      $client->login($user, $passwd);
282
283      my $resp_code = $client->response_code();
284      my $resp_msg = $client->response_msg(0);
285
286      my $expected;
287
288      $expected = 230;
289      $self->assert($expected == $resp_code,
290        test_msg("Expected response code $expected, got $resp_code"));
291
292      $expected = "Down: 0 Files (0mb)  Up: 0 Files (0mb)  10,000,000:1  CR: LEECH";
293      $self->assert($expected eq $resp_msg,
294        test_msg("Expected response message '$expected', got '$resp_msg'"));
295
296      my $conn = $client->stor_raw("test.txt");
297      unless ($conn) {
298        die("STOR test.txt failed: " . $client->response_code() . " " .
299          $client->response_msg());
300      }
301
302      my $buf = "Hello, World!";
303      $conn->write($buf, length($buf), 5);
304      eval { $conn->close() };
305
306      $resp_code = $client->response_code();
307      $resp_msg = $client->response_msg(1);
308      $self->assert_transfer_ok($resp_code, $resp_msg);
309
310      $conn = $client->retr_raw("test.txt");
311      unless ($conn) {
312        die("RETR test.txt failed: " . $client->response_code() . " " .
313          $client->response_msg());
314      }
315
316      $conn->read($buf, 8192, 5);
317      eval { $conn->close() };
318
319      $resp_code = $client->response_code();
320      $resp_msg = $client->response_msg(1);
321      $self->assert_transfer_ok($resp_code, $resp_msg);
322
323      $client->quit();
324    };
325
326    if ($@) {
327      $ex = $@;
328    }
329
330    $wfh->print("done\n");
331    $wfh->flush();
332
333  } else {
334    eval { server_wait($config_file, $rfh) };
335    if ($@) {
336      warn($@);
337      exit 1;
338    }
339
340    exit 0;
341  }
342
343  # Stop server
344  server_stop($pid_file);
345
346  $self->assert_child_ok($pid);
347
348  if (open(my $fh, "< $ratio_file")) {
349    my $line = <$fh>;
350    chomp($line);
351    close($fh);
352
353    my $expected = "$user|2|0|1|0";
354    $self->assert($line eq $expected,
355      test_msg("Expected RatioFile content '$expected', got '$line'"));
356
357  } else {
358    die("Can't read $ratio_file: $!");
359  }
360
361  if ($ex) {
362    test_append_logfile($log_file, $ex);
363    unlink($log_file);
364
365    die($ex);
366  }
367
368  unlink($log_file);
369}
370
371# See: https://bugs.launchpad.net/ubuntu/+source/proftpd-dfsg/+bug/479963
372sub ratio_userratio_with_credit {
373  my $self = shift;
374  my $tmpdir = $self->{tmpdir};
375
376  my $config_file = "$tmpdir/ratio.conf";
377  my $pid_file = File::Spec->rel2abs("$tmpdir/ratio.pid");
378  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/ratio.scoreboard");
379
380  my $log_file = test_get_logfile();
381
382  my $auth_user_file = File::Spec->rel2abs("$tmpdir/ratio.passwd");
383  my $auth_group_file = File::Spec->rel2abs("$tmpdir/ratio.group");
384
385  my $user = 'proftpd';
386  my $passwd = 'test';
387  my $group = 'ftpd';
388  my $home_dir = File::Spec->rel2abs($tmpdir);
389  my $uid = 500;
390  my $gid = 500;
391
392  # Make sure that, if we're running as root, that the home directory has
393  # permissions/privs set for the account we create
394  if ($< == 0) {
395    unless (chmod(0755, $home_dir)) {
396      die("Can't set perms on $home_dir to 0755: $!");
397    }
398
399    unless (chown($uid, $gid, $home_dir)) {
400      die("Can't set owner of $home_dir to $uid/$gid: $!");
401    }
402  }
403
404  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
405    '/bin/bash');
406  auth_group_write($auth_group_file, $group, $gid, $user);
407
408  my $ratio_file = File::Spec->rel2abs("$home_dir/ratios.dat");
409  if (open(my $fh, "> $ratio_file")) {
410    print $fh "$user|7|102404|1|51218\n";
411
412    unless (close($fh)) {
413      die("Can't write $ratio_file: $!");
414    }
415
416  } else {
417    die("Can't open $ratio_file: $!");
418  }
419
420  my $ratio_tmp_file = File::Spec->rel2abs("$home_dir/ratios.tmp");
421  if (open(my $fh, "> $ratio_tmp_file")) {
422    print $fh "$user|7|102404|1|51218\n";
423
424    unless (close($fh)) {
425      die("Can't write $ratio_tmp_file: $!");
426    }
427
428  } else {
429    die("Can't open $ratio_tmp_file: $!");
430  }
431
432  my $config = {
433    PidFile => $pid_file,
434    ScoreboardFile => $scoreboard_file,
435    SystemLog => $log_file,
436    TraceLog => $log_file,
437    Trace => 'response:10',
438
439    AuthUserFile => $auth_user_file,
440    AuthGroupFile => $auth_group_file,
441
442    IfModules => {
443      'mod_delay.c' => {
444        DelayEngine => 'off',
445      },
446
447      'mod_ratio.c' => {
448        Ratios => 'on',
449        SaveRatios => 'on',
450        RatioFile => $ratio_file,
451        RatioTempFile => $ratio_tmp_file,
452        CwdRatioMsg => "Please Upload first",
453        FileRatioErrMsg => "Come on, gimme a lil more",
454        ByteRatioErrMsg => "COME ON NOW",
455        UserRatio => '* 0 0 5 2355200',
456      },
457    },
458  };
459
460  my ($port, $config_user, $config_group) = config_write($config_file, $config);
461
462  # Open pipes, for use between the parent and child processes.  Specifically,
463  # the child will indicate when it's done with its test by writing a message
464  # to the parent.
465  my ($rfh, $wfh);
466  unless (pipe($rfh, $wfh)) {
467    die("Can't open pipe: $!");
468  }
469
470  my $ex;
471
472  # Fork child
473  $self->handle_sigchld();
474  defined(my $pid = fork()) or die("Can't fork: $!");
475  if ($pid) {
476    eval {
477      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
478      $client->login($user, $passwd);
479
480      my $resp_code = $client->response_code();
481      my $resp_msg = $client->response_msg(0);
482
483      my $expected;
484
485      $expected = 230;
486      $self->assert($expected == $resp_code,
487        test_msg("Expected response code $expected, got $resp_code"));
488
489      $expected = "Down: 1 Files (0mb)  Up: 7 Files (0mb)   1:5B CR: 2300";
490      $self->assert($expected eq $resp_msg,
491        test_msg("Expected response message '$expected', got '$resp_msg'"));
492
493      my $conn = $client->stor_raw("test.txt");
494      unless ($conn) {
495        die("STOR test.txt failed: " . $client->response_code() . " " .
496          $client->response_msg());
497      }
498
499      my $buf = "Hello, World!";
500      $conn->write($buf, length($buf), 5);
501      eval { $conn->close() };
502
503      $resp_code = $client->response_code();
504      $resp_msg = $client->response_msg(1);
505      $self->assert_transfer_ok($resp_code, $resp_msg);
506
507      $conn = $client->retr_raw("test.txt");
508      unless ($conn) {
509        die("RETR test.txt failed: " . $client->response_code() . " " .
510          $client->response_msg());
511      }
512
513      $conn->read($buf, 8192, 5);
514      eval { $conn->close() };
515
516      $resp_code = $client->response_code();
517      $resp_msg = $client->response_msg(1);
518      $self->assert_transfer_ok($resp_code, $resp_msg);
519
520      $client->quit();
521    };
522
523    if ($@) {
524      $ex = $@;
525    }
526
527    $wfh->print("done\n");
528    $wfh->flush();
529
530  } else {
531    eval { server_wait($config_file, $rfh) };
532    if ($@) {
533      warn($@);
534      exit 1;
535    }
536
537    exit 0;
538  }
539
540  # Stop server
541  server_stop($pid_file);
542
543  $self->assert_child_ok($pid);
544
545  if (open(my $fh, "< $ratio_file")) {
546    my $line = <$fh>;
547    chomp($line);
548    close($fh);
549
550    my $expected = "$user|7|102404|1|51218";
551    $self->assert($line eq $expected,
552      test_msg("Expected RatioFile content '$expected', got '$line'"));
553
554  } else {
555    die("Can't read $ratio_file: $!");
556  }
557
558  if ($ex) {
559    test_append_logfile($log_file, $ex);
560    unlink($log_file);
561
562    die($ex);
563  }
564
565  unlink($log_file);
566}
567
5681;
569