1package ProFTPD::Tests::Modules::mod_sql_sqlite;
2
3use lib qw(t/lib);
4use base qw(ProFTPD::TestSuite::Child);
5use strict;
6
7use Carp;
8use File::Path qw(mkpath);
9use File::Spec;
10use IO::Handle;
11use Sys::HostAddr;
12
13use ProFTPD::TestSuite::FTP;
14use ProFTPD::TestSuite::Utils qw(:auth :config :running :test :testsuite);
15
16$| = 1;
17
18my $order = 0;
19
20my $TESTS = {
21  sql_bug2045 => {
22    order => ++$order,
23    test_class => [qw(bug forking)],
24  },
25
26  sql_bug2922 => {
27    order => ++$order,
28    test_class => [qw(bug forking rootprivs)],
29  },
30
31  sql_bug3116 => {
32    order => ++$order,
33    test_class => [qw(bug forking)],
34  },
35
36  sql_bug3124 => {
37    order => ++$order,
38    test_class => [qw(bug forking)],
39  },
40
41  sql_sqlite_bug3126 => {
42    order => ++$order,
43    test_class => [qw(bug forking)],
44  },
45
46  sql_user_where_clause_ok => {
47    order => ++$order,
48    test_class => [qw(forking)],
49  },
50
51  sql_user_where_clause_with_vars_ok => {
52    order => ++$order,
53    test_class => [qw(forking)],
54  },
55
56  sql_group_where_clause_ok => {
57    order => ++$order,
58    test_class => [qw(forking)],
59  },
60
61  sql_bug3149 => {
62    order => ++$order,
63    test_class => [qw(bug forking)],
64  },
65
66  sql_sqllog => {
67    order => ++$order,
68    test_class => [qw(forking)],
69  },
70
71  sql_sqlite_sqllog_with_chroot => {
72    order => ++$order,
73    test_class => [qw(forking rootprivs)],
74  },
75
76  sql_user_info_different_table_names => {
77    order => ++$order,
78    test_class => [qw(forking)],
79  },
80
81  sql_custom_user_info => {
82    order => ++$order,
83    test_class => [qw(forking)],
84  },
85
86  sql_custom_user_info_null_ids => {
87    order => ++$order,
88    test_class => [qw(forking)],
89  },
90
91  sql_userset_bug2434 => {
92    order => ++$order,
93    test_class => [qw(bug forking rootprivs)],
94  },
95
96  sql_usersetfast_bug2434 => {
97    order => ++$order,
98    test_class => [qw(bug forking rootprivs)],
99  },
100
101  sql_custom_group_info_bug3043 => {
102    order => ++$order,
103    test_class => [qw(bug forking)],
104  },
105
106  sql_groupset_bug3043 => {
107    order => ++$order,
108    test_class => [qw(bug forking rootprivs)],
109  },
110
111  sql_groupsetfast_bug3043 => {
112    order => ++$order,
113    test_class => [qw(bug forking rootprivs)],
114  },
115
116  sql_sqllog_var_w => {
117    order => ++$order,
118    test_class => [qw(forking)],
119  },
120
121  sql_sqllog_var_w_chrooted => {
122    order => ++$order,
123    test_class => [qw(forking rootprivs)],
124  },
125
126  sql_openssl_auth_type_md5 => {
127    order => ++$order,
128    test_class => [qw(feature_openssl forking)],
129  },
130
131  sql_openssl_auth_type_sha1 => {
132    order => ++$order,
133    test_class => [qw(feature_openssl forking)],
134  },
135
136  sql_sqllog_var_S => {
137    order => ++$order,
138    test_class => [qw(forking)],
139  },
140
141  sql_sqllog_var_S_err => {
142    order => ++$order,
143    test_class => [qw(forking)],
144  },
145
146  sql_negative_cache_bug3282 => {
147    order => ++$order,
148    test_class => [qw(bug forking)],
149  },
150
151  sql_sqllog_var_T_rnfr => {
152    order => ++$order,
153    test_class => [qw(bug forking)],
154  },
155
156  sql_sqllog_var_T_retr => {
157    order => ++$order,
158    test_class => [qw(bug forking)],
159  },
160
161  sql_sqllog_var_transfer_millisecs_retr_bug4218 => {
162    order => ++$order,
163    test_class => [qw(bug forking)],
164  },
165
166  sql_sqllog_var_R_retr_bug4218 => {
167    order => ++$order,
168    test_class => [qw(bug forking)],
169  },
170
171  sql_sqllog_exit => {
172    order => ++$order,
173    test_class => [qw(forking)],
174  },
175
176  sql_sqllog_exit_var_remote_port_bug4296 => {
177    order => ++$order,
178    test_class => [qw(bug forking)],
179  },
180
181  sql_sqllog_var_d_bug3395 => {
182    order => ++$order,
183    test_class => [qw(bug forking)],
184  },
185
186  sql_sqllog_var_d_chroot_bug3395 => {
187    order => ++$order,
188    test_class => [qw(bug forking rootprivs)],
189  },
190
191  sql_sqllog_var_uid_gid_bug3390 => {
192    order => ++$order,
193    test_class => [qw(bug forking)],
194  },
195
196  sql_sqlite_auth_type_backend_bug3511 => {
197    order => ++$order,
198    test_class => [qw(bug forking)],
199  },
200
201  sql_sqllog_pass_ok_var_s_bug3528 => {
202    order => ++$order,
203    test_class => [qw(bug forking)],
204  },
205
206  sql_sqllog_pass_failed_var_s_bug3528 => {
207    order => ++$order,
208    test_class => [qw(bug forking)],
209  },
210
211  sql_sqllog_pass_failed_bug4301 => {
212    order => ++$order,
213    test_class => [qw(bug forking)],
214  },
215
216  sql_sqlshowinfo_pass_bug3423 => {
217    order => ++$order,
218    test_class => [qw(bug forking)],
219  },
220
221  sql_sqlshowinfo_list_bug3423 => {
222    order => ++$order,
223    test_class => [qw(bug forking)],
224  },
225
226  sql_multiple_users_shared_uid_gid => {
227    order => ++$order,
228    test_class => [qw(bug forking)],
229  },
230
231  sql_resolve_tag_bug3536 => {
232    order => ++$order,
233    test_class => [qw(bug forking inprogress)],
234  },
235
236  sql_sqllog_vars_I_O_bug3554 => {
237    order => ++$order,
238    test_class => [qw(bug forking)],
239  },
240
241  sql_sqllog_note_var_unique_id_bug3572 => {
242    order => ++$order,
243    test_class => [qw(bug forking mod_unique_id)],
244  },
245
246  sql_sqllog_note_var_rewrite_bug3572 => {
247    order => ++$order,
248    test_class => [qw(bug forking mod_rewrite)],
249  },
250
251  sql_sqllog_note_sql_user_info => {
252    order => ++$order,
253    test_class => [qw(forking)],
254  },
255
256  sql_sqllog_var_E => {
257    order => ++$order,
258    test_class => [qw(forking)],
259  },
260
261  sql_named_conn_bug3262 => {
262    order => ++$order,
263    test_class => [qw(bug forking)],
264  },
265
266  sql_named_conn_sqllog_exit_bug3645 => {
267    order => ++$order,
268    test_class => [qw(bug forking rootprivs)],
269  },
270
271  sql_sqllog_vars_H_L_matching_server_bug3620 => {
272    order => ++$order,
273    test_class => [qw(bug forking)],
274  },
275
276  sql_sqllog_vars_H_L_default_server_bug3620 => {
277    order => ++$order,
278    test_class => [qw(bug forking)],
279  },
280
281  sql_sqllog_exit_ifuser => {
282    order => ++$order,
283    test_class => [qw(forking mod_ifsession)],
284  },
285
286  sql_sqllog_exit_ifgroup => {
287    order => ++$order,
288    test_class => [qw(forking mod_ifsession)],
289  },
290
291  sql_sqllog_multi_pass_ifclass_bug4025 => {
292    order => ++$order,
293    test_class => [qw(forking mod_ifsession)],
294  },
295
296  sql_opt_no_disconnect_on_error_with_extlog_bug3633 => {
297    order => ++$order,
298    test_class => [qw(bug forking)],
299  },
300
301  sql_sqllog_ignore_errors_bad_table_bug3692 => {
302    order => ++$order,
303    test_class => [qw(bug forking)],
304  },
305
306  sql_sqllog_ignore_errors_bad_db_bug3692 => {
307    order => ++$order,
308    test_class => [qw(bug forking)],
309  },
310
311  sql_sqllog_var_xfer_status_nonxfer => {
312    order => ++$order,
313    test_class => [qw(forking)],
314  },
315
316  sql_sqllog_var_xfer_status_success_download => {
317    order => ++$order,
318    test_class => [qw(forking)],
319  },
320
321  sql_sqllog_var_xfer_status_success_upload => {
322    order => ++$order,
323    test_class => [qw(forking)],
324  },
325
326  sql_sqllog_var_xfer_status_cancelled => {
327    order => ++$order,
328    test_class => [qw(forking)],
329  },
330
331  sql_sqllog_var_xfer_status_failed => {
332    order => ++$order,
333    test_class => [qw(forking)],
334  },
335
336  sql_sqllog_var_xfer_status_timeout => {
337    order => ++$order,
338    test_class => [qw(forking)],
339  },
340
341  sql_sqllog_var_xfer_status_filtered => {
342    order => ++$order,
343    test_class => [qw(forking)],
344  },
345
346  sql_sqllog_var_xfer_failure_none => {
347    order => ++$order,
348    test_class => [qw(forking)],
349  },
350
351  sql_sqllog_var_xfer_failure_reason => {
352    order => ++$order,
353    test_class => [qw(forking)],
354  },
355
356  sql_sqlshowinfo_pass_failed_bug3782 => {
357    order => ++$order,
358    test_class => [qw(bug forking)],
359  },
360
361  sql_sqllog_preauth_var_U_bug3822 => {
362    order => ++$order,
363    test_class => [qw(bug forking)],
364  },
365
366  sql_sqllog_preauth_var_u_bug3822 => {
367    order => ++$order,
368    test_class => [qw(bug forking)],
369  },
370
371  sql_sqlite_maxhostsperuser => {
372    order => ++$order,
373    test_class => [qw(forking mod_unique_id)],
374  },
375
376  sql_sqllog_var_micros_ts_bug3889 => {
377    order => ++$order,
378    test_class => [qw(bug forking)],
379  },
380
381  sql_sqllog_var_millis_ts_bug3889 => {
382    order => ++$order,
383    test_class => [qw(bug forking)],
384  },
385
386  sql_sqllog_var_iso8601_ts_bug3889 => {
387    order => ++$order,
388    test_class => [qw(bug forking)],
389  },
390
391  sql_sqllog_var_note_ftps_without_tls_issue1232 => {
392    order => ++$order,
393    test_class => [qw(bug forking)],
394  },
395
396  sql_sqllog_var_note_ftps_with_tls_issue1232 => {
397    order => ++$order,
398    test_class => [qw(bug forking mod_tls)],
399  },
400
401  sql_sqllogonevent_bug3893 => {
402    order => ++$order,
403    test_class => [qw(bug forking)],
404  },
405
406  sql_userprimarykey_bug3864 => {
407    order => ++$order,
408    test_class => [qw(bug forking)],
409  },
410
411  sql_userprimarykey_custom_bug3864 => {
412    order => ++$order,
413    test_class => [qw(bug forking)],
414  },
415
416  sql_groupprimarykey_bug3864 => {
417    order => ++$order,
418    test_class => [qw(bug forking)],
419  },
420
421  sql_groupprimarykey_custom_bug3864 => {
422    order => ++$order,
423    test_class => [qw(bug forking)],
424  },
425
426  sql_sqllog_var_basename_bug3987 => {
427    order => ++$order,
428    test_class => [qw(bug forking)],
429  },
430
431  sql_user_info_defaulthomedir_bug4083 => {
432    order => ++$order,
433    test_class => [qw(bug forking)],
434  },
435
436  sql_user_info_null_uid_gid_columns => {
437    order => ++$order,
438    test_class => [qw(forking)],
439  },
440
441  sql_64_bit_uid_bug4164 => {
442    order => ++$order,
443    test_class => [qw(bug forking)],
444  },
445
446  sql_sqllog_var_remote_port => {
447    order => ++$order,
448    test_class => [qw(forking)],
449  },
450
451  sql_userowner_issue346 => {
452    order => ++$order,
453    test_class => [qw(forking rootprivs)],
454  },
455
456  sql_sqlite_log_db_enoent_issue654 => {
457    order => ++$order,
458    test_class => [qw(forking bug)],
459  },
460
461  sql_namebased_vhost_using_host_issue882 => {
462    order => ++$order,
463    test_class => [qw(forking bug)],
464  },
465
466  sql_namebased_vhost_using_sni_issue882 => {
467    order => ++$order,
468    test_class => [qw(forking bug mod_tls)],
469  },
470};
471
472sub new {
473  return shift()->SUPER::new(@_);
474}
475
476sub list_tests {
477  return testsuite_get_runnable_tests($TESTS);
478}
479
480sub build_db {
481  my $cmd = shift;
482  my $db_script = shift;
483  my $check_exit_status = shift;
484  $check_exit_status = 0 unless defined $check_exit_status;
485
486  if ($ENV{TEST_VERBOSE}) {
487    print STDERR "Executing sqlite3: $cmd\n";
488  }
489
490  my @output = `$cmd`;
491  my $exit_status = $?;
492
493  if ($ENV{TEST_VERBOSE}) {
494    print STDERR "Output: ", join('', @output), "\n";
495  }
496
497  if ($check_exit_status) {
498    if ($? != 0) {
499      croak("'$cmd' failed");
500    }
501  }
502
503  unlink($db_script);
504  return 1;
505}
506
507sub sql_bug2045 {
508  my $self = shift;
509  my $tmpdir = $self->{tmpdir};
510  my $setup = test_setup($tmpdir, 'sqlite');
511
512  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
513
514  # Build up sqlite3 command to create users, groups tables and populate them
515  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
516
517  if (open(my $fh, "> $db_script")) {
518    print $fh <<EOS;
519CREATE TABLE users (
520  userid TEXT,
521  passwd TEXT,
522  uid INTEGER,
523  gid INTEGER,
524  homedir TEXT,
525  shell TEXT,
526  lastdir TEXT
527);
528INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash');
529
530CREATE TABLE groups (
531  groupname TEXT,
532  gid INTEGER,
533  members TEXT
534);
535INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
536EOS
537
538    unless (close($fh)) {
539      die("Can't write $db_script: $!");
540    }
541
542  } else {
543    die("Can't open $db_script: $!");
544  }
545
546  my $cmd = "sqlite3 $db_file < $db_script";
547  build_db($cmd, $db_script);
548
549  # Make sure that, if we're running as root, the database file has
550  # the permissions/privs set for use by proftpd
551  if ($< == 0) {
552    unless (chmod(0666, $db_file)) {
553      die("Can't set perms on $db_file to 0666: $!");
554    }
555  }
556
557  my $config = {
558    PidFile => $setup->{pid_file},
559    ScoreboardFile => $setup->{scoreboard_file},
560    SystemLog => $setup->{log_file},
561    TraceLog => $setup->{log_file},
562    Trace => 'sql:20',
563
564    IfModules => {
565      'mod_delay.c' => {
566        DelayEngine => 'off',
567      },
568
569      'mod_sql.c' => {
570        SQLAuthTypes => 'plaintext',
571        SQLBackend => 'sqlite3',
572        SQLConnectInfo => $db_file,
573        SQLLogFile => $setup->{log_file},
574        SQLNamedQuery => 'lastdir SELECT "lastdir FROM users WHERE userid = \'%u\'"',
575        SQLShowInfo => 'PASS "230" "\"%{lastdir}\" was the last directory"',
576      },
577    },
578  };
579
580  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
581    $config);
582
583  # Open pipes, for use between the parent and child processes.  Specifically,
584  # the child will indicate when it's done with its test by writing a message
585  # to the parent.
586  my ($rfh, $wfh);
587  unless (pipe($rfh, $wfh)) {
588    die("Can't open pipe: $!");
589  }
590
591  my $ex;
592
593  # Fork child
594  $self->handle_sigchld();
595  defined(my $pid = fork()) or die("Can't fork: $!");
596  if ($pid) {
597    eval {
598      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
599      $client->login($setup->{user}, $setup->{passwd});
600
601      my $resp_msgs = $client->response_msgs();
602      my $nmsgs = scalar(@$resp_msgs);
603
604      my $expected = 1;
605      $self->assert($expected == $nmsgs, "Expected $expected, got $nmsgs");
606
607      $expected = "User proftpd logged in";
608      $self->assert($expected eq $resp_msgs->[0],
609        "Expected '$expected', got '$resp_msgs->[0]'");
610    };
611    if ($@) {
612      $ex = $@;
613    }
614
615    $wfh->print("done\n");
616    $wfh->flush();
617
618  } else {
619    eval { server_wait($setup->{config_file}, $rfh) };
620    if ($@) {
621      warn($@);
622      exit 1;
623    }
624
625    exit 0;
626  }
627
628  # Stop server
629  server_stop($setup->{pid_file});
630  $self->assert_child_ok($pid);
631
632  test_cleanup($setup->{log_file}, $ex);
633}
634
635sub sql_bug2922 {
636  my $self = shift;
637  my $tmpdir = $self->{tmpdir};
638
639  my $config_file = "$tmpdir/sqlite.conf";
640  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
641  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
642
643  my $log_file = test_get_logfile();
644
645  # Bug#2922 occurred because mod_sql would "authenticate" the plaintext
646  # password (if configured to do so) of a user whose info did NOT come from
647  # mod_sql.
648
649  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
650
651  # Build up sqlite3 command to create users, groups tables and populate them
652  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
653
654  if (open(my $fh, "> $db_script")) {
655    print $fh <<EOS;
656CREATE TABLE users (
657  userid TEXT,
658  passwd TEXT,
659  uid INTEGER,
660  gid INTEGER,
661  homedir TEXT,
662  shell TEXT
663);
664
665CREATE TABLE groups (
666  groupname TEXT,
667  gid INTEGER,
668  members TEXT
669);
670EOS
671
672    unless (close($fh)) {
673      die("Can't write $db_script: $!");
674    }
675
676  } else {
677    die("Can't open $db_script: $!");
678  }
679
680  my $cmd = "sqlite3 $db_file < $db_script";
681  build_db($cmd, $db_script);
682
683  # Make sure that, if we're running as root, the database file has
684  # the permissions/privs set for use by proftpd
685  if ($< == 0) {
686    unless (chmod(0666, $db_file)) {
687      die("Can't set perms on $db_file to 0666: $!");
688    }
689  }
690
691  my $config = {
692    PidFile => $pid_file,
693    ScoreboardFile => $scoreboard_file,
694    SystemLog => $log_file,
695
696    MaxLoginAttempts => 100,
697
698    IfModules => {
699      'mod_delay.c' => {
700        DelayEngine => 'off',
701      },
702
703      'mod_sql.c' => {
704        SQLAuthTypes => 'plaintext',
705        SQLBackend => 'sqlite3',
706        SQLConnectInfo => $db_file,
707        SQLLogFile => $log_file,
708      },
709    },
710  };
711
712  my ($port, $config_user, $config_group) = config_write($config_file, $config);
713
714  # Open pipes, for use between the parent and child processes.  Specifically,
715  # the child will indicate when it's done with its test by writing a message
716  # to the parent.
717  my ($rfh, $wfh);
718  unless (pipe($rfh, $wfh)) {
719    die("Can't open pipe: $!");
720  }
721
722  my $ex;
723
724  # Fork child
725  $self->handle_sigchld();
726  defined(my $pid = fork()) or die("Can't fork: $!");
727  if ($pid) {
728    eval {
729      # Bug#2922 would allow a login which should NOT happen.  So if
730      # *any* of the following logins succeed, we have a problem.
731
732      my $accounts = [
733        { user => 'bin', passwd => '*' },
734        { user => 'bin', passwd => 'x' },
735        { user => 'daemon', passwd => '*' },
736        { user => 'daemon', passwd => 'x' },
737        { user => 'mysql', passwd => '*' },
738        { user => 'mysql', passwd => 'x' },
739      ];
740
741      foreach my $account (@$accounts) {
742        my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
743        my $user = $account->{user};
744        my $passwd = $account->{passwd};
745
746        eval { $client->login($user, $passwd) };
747        unless ($@) {
748          die("Logged in unexpectedly");
749        }
750      }
751    };
752
753    if ($@) {
754      $ex = $@;
755    }
756
757    $wfh->print("done\n");
758    $wfh->flush();
759
760  } else {
761    eval { server_wait($config_file, $rfh, 30) };
762    if ($@) {
763      warn($@);
764      exit 1;
765    }
766
767    exit 0;
768  }
769
770  # Stop server
771  server_stop($pid_file);
772
773  $self->assert_child_ok($pid);
774
775  if ($ex) {
776    test_append_logfile($log_file, $ex);
777    unlink($log_file);
778
779    die($ex);
780  }
781
782  unlink($log_file);
783}
784
785sub sql_bug3116 {
786  my $self = shift;
787  my $tmpdir = $self->{tmpdir};
788
789  my $config_file = "$tmpdir/sqlite.conf";
790  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
791  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
792
793  my $log_file = test_get_logfile();
794
795  # Bug#3116 occurred because mod_sql was treating percent signs in user
796  # (and group) names as variables to be substituted.
797
798  my $user = 'proftpd%proftpd.org';
799  my $passwd = 'test';
800  my $group = 'ftpd';
801  my $home_dir = File::Spec->rel2abs($tmpdir);
802
803  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
804
805  # Build up sqlite3 command to create users, groups tables and populate them
806  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
807
808  if (open(my $fh, "> $db_script")) {
809    print $fh <<EOS;
810CREATE TABLE users (
811  userid TEXT,
812  passwd TEXT,
813  uid INTEGER,
814  gid INTEGER,
815  homedir TEXT,
816  shell TEXT
817);
818INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', 500, 500, '$home_dir', '/bin/bash');
819
820CREATE TABLE groups (
821  groupname TEXT,
822  gid INTEGER,
823  members TEXT
824);
825INSERT INTO groups (groupname, gid, members) VALUES ('$group', 500, '$user');
826INSERT INTO groups (groupname, gid, members) VALUES ('ftpadm', 501, '$user');
827EOS
828
829    unless (close($fh)) {
830      die("Can't write $db_script: $!");
831    }
832
833  } else {
834    die("Can't open $db_script: $!");
835  }
836
837  my $cmd = "sqlite3 -echo $db_file < $db_script 2>&1";
838  build_db($cmd, $db_script);
839
840  # Make sure that, if we're running as root, the database file has
841  # the permissions/privs set for use by proftpd
842  if ($< == 0) {
843    unless (chmod(0666, $db_file)) {
844      die("Can't set perms on $db_file to 0666: $!");
845    }
846  }
847
848  my $config = {
849    PidFile => $pid_file,
850    ScoreboardFile => $scoreboard_file,
851    SystemLog => $log_file,
852
853    IfModules => {
854      'mod_delay.c' => {
855        DelayEngine => 'off',
856      },
857
858      'mod_sql.c' => {
859        SQLAuthTypes => 'plaintext',
860        SQLBackend => 'sqlite3',
861        SQLConnectInfo => $db_file,
862        SQLLogFile => $log_file,
863      },
864    },
865  };
866
867  my ($port, $config_user, $config_group) = config_write($config_file, $config);
868
869  # Open pipes, for use between the parent and child processes.  Specifically,
870  # the child will indicate when it's done with its test by writing a message
871  # to the parent.
872  my ($rfh, $wfh);
873  unless (pipe($rfh, $wfh)) {
874    die("Can't open pipe: $!");
875  }
876
877  my $ex;
878
879  # Fork child
880  $self->handle_sigchld();
881  defined(my $pid = fork()) or die("Can't fork: $!");
882  if ($pid) {
883    eval {
884      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
885
886      # Bug#3116 would prevent a login from occurring, so we only need to
887      # attempt a login.
888      $client->login($user, $passwd, 5);
889    };
890
891    if ($@) {
892      $ex = $@;
893    }
894
895    $wfh->print("done\n");
896    $wfh->flush();
897
898  } else {
899    eval { server_wait($config_file, $rfh) };
900    if ($@) {
901      warn($@);
902      exit 1;
903    }
904
905    exit 0;
906  }
907
908  # Stop server
909  server_stop($pid_file);
910
911  $self->assert_child_ok($pid);
912
913  if ($ex) {
914    test_append_logfile($log_file, $ex);
915    unlink($log_file);
916
917    die($ex);
918  }
919
920  unlink($log_file);
921}
922
923sub sql_bug3124 {
924  my $self = shift;
925  my $tmpdir = $self->{tmpdir};
926
927  my $config_file = "$tmpdir/sqlite.conf";
928  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
929  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
930
931  my $log_file = test_get_logfile();
932
933  my $user = 'proftpd';
934  my $passwd = 'test';
935  my $group = 'ftpd';
936  my $home_dir = File::Spec->rel2abs($tmpdir);
937
938  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
939
940  # Build up sqlite3 command to create users, groups tables and populate them
941  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
942
943  if (open(my $fh, "> $db_script")) {
944    print $fh <<EOS;
945CREATE TABLE users (
946  userid TEXT,
947  passwd TEXT,
948  uid INTEGER,
949  gid INTEGER,
950  homedir TEXT,
951  shell TEXT
952);
953INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', 500, 500, '$home_dir', '/bin/bash');
954
955CREATE TABLE groups (
956  groupname TEXT,
957  gid INTEGER,
958  members TEXT
959);
960EOS
961
962    unless (close($fh)) {
963      die("Can't write $db_script: $!");
964    }
965
966  } else {
967    die("Can't open $db_script: $!");
968  }
969
970  my $cmd = "sqlite3 $db_file < $db_script";
971  build_db($cmd, $db_script);
972
973  # Make sure that, if we're running as root, the database file has
974  # the permissions/privs set for use by proftpd
975  if ($< == 0) {
976    unless (chmod(0666, $db_file)) {
977      die("Can't set perms on $db_file to 0666: $!");
978    }
979  }
980
981  # Bug#3124 occurred when SQLNegativeCache was on, and there was no group
982  # info; it would cause a segfault.
983
984  my $config = {
985    PidFile => $pid_file,
986    ScoreboardFile => $scoreboard_file,
987    SystemLog => $log_file,
988
989    IfModules => {
990      'mod_delay.c' => {
991        DelayEngine => 'off',
992      },
993
994      'mod_sql.c' => {
995        SQLAuthTypes => 'plaintext',
996        SQLBackend => 'sqlite3',
997        SQLConnectInfo => $db_file,
998        SQLLogFile => $log_file,
999        SQLNegativeCache => 'on',
1000      },
1001    },
1002  };
1003
1004  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1005
1006  # Open pipes, for use between the parent and child processes.  Specifically,
1007  # the child will indicate when it's done with its test by writing a message
1008  # to the parent.
1009  my ($rfh, $wfh);
1010  unless (pipe($rfh, $wfh)) {
1011    die("Can't open pipe: $!");
1012  }
1013
1014  my $ex;
1015
1016  # Fork child
1017  $self->handle_sigchld();
1018  defined(my $pid = fork()) or die("Can't fork: $!");
1019  if ($pid) {
1020    eval {
1021      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1022
1023      # Bug#3124 would prevent a login from occurring, so we only need to
1024      # attempt a login.
1025      $client->login($user, $passwd, 5);
1026    };
1027
1028    if ($@) {
1029      $ex = $@;
1030    }
1031
1032    $wfh->print("done\n");
1033    $wfh->flush();
1034
1035  } else {
1036    eval { server_wait($config_file, $rfh) };
1037    if ($@) {
1038      warn($@);
1039      exit 1;
1040    }
1041
1042    exit 0;
1043  }
1044
1045  # Stop server
1046  server_stop($pid_file);
1047
1048  $self->assert_child_ok($pid);
1049
1050  if ($ex) {
1051    test_append_logfile($log_file, $ex);
1052    unlink($log_file);
1053
1054    die($ex);
1055  }
1056
1057  unlink($log_file);
1058}
1059
1060sub sql_sqlite_bug3126 {
1061  my $self = shift;
1062  my $tmpdir = $self->{tmpdir};
1063
1064  my $config_file = "$tmpdir/sqlite.conf";
1065  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
1066  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
1067
1068  my $log_file = test_get_logfile();
1069
1070  my $user = 'proftpd';
1071  my $passwd = 'test';
1072  my $group = 'ftpd';
1073  my $home_dir = File::Spec->rel2abs($tmpdir);
1074
1075  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1076
1077  # Build up sqlite3 command to create users, groups tables and populate them
1078  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1079
1080  if (open(my $fh, "> $db_script")) {
1081    print $fh <<EOS;
1082CREATE TABLE users (
1083  userid TEXT,
1084  passwd TEXT,
1085  uid INTEGER,
1086  gid INTEGER,
1087  homedir TEXT,
1088  shell TEXT
1089);
1090INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', 500, 500, '$home_dir', '/bin/bash');
1091
1092CREATE TABLE groups (
1093  groupname TEXT,
1094  gid INTEGER,
1095  members TEXT
1096);
1097INSERT INTO groups (groupname, gid, members) VALUES ('$group', 500, '$user');
1098INSERT INTO groups (groupname, gid, members) VALUES ('ftpadm', 501, '$user');
1099EOS
1100
1101    unless (close($fh)) {
1102      die("Can't write $db_script: $!");
1103    }
1104
1105  } else {
1106    die("Can't open $db_script: $!");
1107  }
1108
1109  my $cmd = "sqlite3 $db_file < $db_script";
1110  build_db($cmd, $db_script);
1111
1112  # Make sure that, if we're running as root, the database file has
1113  # the permissions/privs set for use by proftpd
1114  if ($< == 0) {
1115    unless (chmod(0666, $db_file)) {
1116      die("Can't set perms on $db_file to 0666: $!");
1117    }
1118  }
1119
1120  my $config = {
1121    PidFile => $pid_file,
1122    ScoreboardFile => $scoreboard_file,
1123    SystemLog => $log_file,
1124
1125    IfModules => {
1126      'mod_delay.c' => {
1127        DelayEngine => 'off',
1128      },
1129
1130      'mod_sql.c' => {
1131        SQLAuthTypes => 'plaintext',
1132        SQLBackend => 'sqlite3',
1133        SQLConnectInfo => $db_file,
1134        SQLLogFile => $log_file,
1135      },
1136    },
1137  };
1138
1139  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1140
1141  # Open pipes, for use between the parent and child processes.  Specifically,
1142  # the child will indicate when it's done with its test by writing a message
1143  # to the parent.
1144  my ($rfh, $wfh);
1145  unless (pipe($rfh, $wfh)) {
1146    die("Can't open pipe: $!");
1147  }
1148
1149  my $ex;
1150
1151  # Fork child
1152  $self->handle_sigchld();
1153  defined(my $pid = fork()) or die("Can't fork: $!");
1154  if ($pid) {
1155    eval {
1156      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1157
1158      # Bug#3126 would prevent a login from occurring, so we only need to
1159      # attempt a login.
1160      $client->login($user, $passwd, 5);
1161    };
1162
1163    if ($@) {
1164      $ex = $@;
1165    }
1166
1167    $wfh->print("done\n");
1168    $wfh->flush();
1169
1170  } else {
1171    eval { server_wait($config_file, $rfh) };
1172    if ($@) {
1173      warn($@);
1174      exit 1;
1175    }
1176
1177    exit 0;
1178  }
1179
1180  # Stop server
1181  server_stop($pid_file);
1182
1183  $self->assert_child_ok($pid);
1184
1185  if ($ex) {
1186    test_append_logfile($log_file, $ex);
1187    unlink($log_file);
1188
1189    die($ex);
1190  }
1191
1192  unlink($log_file);
1193}
1194
1195sub sql_user_where_clause_ok {
1196  my $self = shift;
1197  my $tmpdir = $self->{tmpdir};
1198
1199  my $config_file = "$tmpdir/sqlite.conf";
1200  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
1201  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
1202
1203  my $log_file = test_get_logfile();
1204
1205  my $user1 = 'proftpd';
1206  my $user2 = 'proftpd2';
1207  my $passwd = 'test';
1208  my $group = 'ftpd';
1209  my $home_dir = File::Spec->rel2abs($tmpdir);
1210
1211  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1212
1213  # Build up sqlite3 command to create users, groups tables and populate them
1214  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1215
1216  if (open(my $fh, "> $db_script")) {
1217    print $fh <<EOS;
1218CREATE TABLE users (
1219  userid TEXT,
1220  passwd TEXT,
1221  uid INTEGER,
1222  gid INTEGER,
1223  homedir TEXT,
1224  shell TEXT,
1225  allowed TEXT
1226);
1227INSERT INTO users (userid, passwd, uid, gid, homedir, shell, allowed) VALUES ('$user1', '$passwd', 500, 500, '$home_dir', '/bin/bash', 'false');
1228INSERT INTO users (userid, passwd, uid, gid, homedir, shell, allowed) VALUES ('$user2', '$passwd', 500, 500, '$home_dir', '/bin/bash', 'true');
1229
1230CREATE TABLE groups (
1231  groupname TEXT,
1232  gid INTEGER,
1233  members TEXT
1234);
1235INSERT INTO groups (groupname, gid, members) VALUES ('$group', 500, '$user1,$user2');
1236EOS
1237
1238    unless (close($fh)) {
1239      die("Can't write $db_script: $!");
1240    }
1241
1242  } else {
1243    die("Can't open $db_script: $!");
1244  }
1245
1246  my $cmd = "sqlite3 $db_file < $db_script";
1247  build_db($cmd, $db_script);
1248
1249  # Make sure that, if we're running as root, the database file has
1250  # the permissions/privs set for use by proftpd
1251  if ($< == 0) {
1252    unless (chmod(0666, $db_file)) {
1253      die("Can't set perms on $db_file to 0666: $!");
1254    }
1255  }
1256
1257  my $config = {
1258    PidFile => $pid_file,
1259    ScoreboardFile => $scoreboard_file,
1260    SystemLog => $log_file,
1261
1262    IfModules => {
1263      'mod_delay.c' => {
1264        DelayEngine => 'off',
1265      },
1266
1267      'mod_sql.c' => {
1268        SQLAuthTypes => 'plaintext',
1269        SQLBackend => 'sqlite3',
1270        SQLConnectInfo => $db_file,
1271        SQLLogFile => $log_file,
1272        SQLUserWhereClause => '"allowed = \'true\'"',
1273      },
1274    },
1275  };
1276
1277  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1278
1279  # Open pipes, for use between the parent and child processes.  Specifically,
1280  # the child will indicate when it's done with its test by writing a message
1281  # to the parent.
1282  my ($rfh, $wfh);
1283  unless (pipe($rfh, $wfh)) {
1284    die("Can't open pipe: $!");
1285  }
1286
1287  my $ex;
1288
1289  # Fork child
1290  $self->handle_sigchld();
1291  defined(my $pid = fork()) or die("Can't fork: $!");
1292  if ($pid) {
1293    eval {
1294      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1295
1296      # This account should not be allowed to login based on the
1297      # SQLUserWhereClause...
1298      eval { $client->login($user1, $passwd) };
1299      unless ($@) {
1300        die("Login for user '$user1' succeeded unexpectedly");
1301      }
1302
1303      # ...but this one should succeed.
1304      $client->login($user2, $passwd);
1305    };
1306
1307    if ($@) {
1308      $ex = $@;
1309    }
1310
1311    $wfh->print("done\n");
1312    $wfh->flush();
1313
1314  } else {
1315    eval { server_wait($config_file, $rfh) };
1316    if ($@) {
1317      warn($@);
1318      exit 1;
1319    }
1320
1321    exit 0;
1322  }
1323
1324  # Stop server
1325  server_stop($pid_file);
1326
1327  $self->assert_child_ok($pid);
1328
1329  if ($ex) {
1330    test_append_logfile($log_file, $ex);
1331    unlink($log_file);
1332
1333    die($ex);
1334  }
1335
1336  unlink($log_file);
1337}
1338
1339sub sql_user_where_clause_with_vars_ok {
1340  my $self = shift;
1341  my $tmpdir = $self->{tmpdir};
1342  my $setup = test_setup($tmpdir, 'sqlite');
1343
1344  my $user2 = 'proftpd2';
1345
1346  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1347
1348  # Build up sqlite3 command to create users, groups tables and populate them
1349  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1350
1351  if (open(my $fh, "> $db_script")) {
1352    print $fh <<EOS;
1353CREATE TABLE users (
1354  userid TEXT,
1355  passwd TEXT,
1356  uid INTEGER,
1357  gid INTEGER,
1358  homedir TEXT,
1359  shell TEXT,
1360  allowed_ip TEXT
1361);
1362INSERT INTO users (userid, passwd, uid, gid, homedir, shell, allowed_ip) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', '1.2.3.4');
1363INSERT INTO users (userid, passwd, uid, gid, homedir, shell, allowed_ip) VALUES ('$user2', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', '127.0.0.1');
1364
1365CREATE TABLE groups (
1366  groupname TEXT,
1367  gid INTEGER,
1368  members TEXT
1369);
1370INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user},$user2');
1371EOS
1372
1373    unless (close($fh)) {
1374      die("Can't write $db_script: $!");
1375    }
1376
1377  } else {
1378    die("Can't open $db_script: $!");
1379  }
1380
1381  my $cmd = "sqlite3 $db_file < $db_script";
1382  build_db($cmd, $db_script);
1383
1384  # Make sure that, if we're running as root, the database file has
1385  # the permissions/privs set for use by proftpd
1386  if ($< == 0) {
1387    unless (chmod(0666, $db_file)) {
1388      die("Can't set perms on $db_file to 0666: $!");
1389    }
1390  }
1391
1392  my $config = {
1393    PidFile => $setup->{pid_file},
1394    ScoreboardFile => $setup->{scoreboard_file},
1395    SystemLog => $setup->{log_file},
1396    TraceLog => $setup->{log_file},
1397    Trace => 'jot:20 sql:20',
1398
1399    IfModules => {
1400      'mod_delay.c' => {
1401        DelayEngine => 'off',
1402      },
1403
1404      'mod_sql.c' => {
1405        SQLAuthTypes => 'plaintext',
1406        SQLBackend => 'sqlite3',
1407        SQLConnectInfo => $db_file,
1408        SQLLogFile => $setup->{log_file},
1409        SQLUserWhereClause => '"allowed_ip = \'%L\'"',
1410      },
1411    },
1412  };
1413
1414  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1415    $config);
1416
1417  # Open pipes, for use between the parent and child processes.  Specifically,
1418  # the child will indicate when it's done with its test by writing a message
1419  # to the parent.
1420  my ($rfh, $wfh);
1421  unless (pipe($rfh, $wfh)) {
1422    die("Can't open pipe: $!");
1423  }
1424
1425  my $ex;
1426
1427  # Fork child
1428  $self->handle_sigchld();
1429  defined(my $pid = fork()) or die("Can't fork: $!");
1430  if ($pid) {
1431    eval {
1432      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1433
1434      # This account should not be allowed to login based on the
1435      # SQLUserWhereClause...
1436      eval { $client->login($setup->{user}, $setup->{passwd}) };
1437      unless ($@) {
1438        die("Login for user '$setup->{user}' succeeded unexpectedly");
1439      }
1440
1441      # ...but this one should succeed.
1442      $client->login($user2, $setup->{passwd});
1443      $client->quit();
1444    };
1445    if ($@) {
1446      $ex = $@;
1447    }
1448
1449    $wfh->print("done\n");
1450    $wfh->flush();
1451
1452  } else {
1453    eval { server_wait($setup->{config_file}, $rfh) };
1454    if ($@) {
1455      warn($@);
1456      exit 1;
1457    }
1458
1459    exit 0;
1460  }
1461
1462  # Stop server
1463  server_stop($setup->{pid_file});
1464  $self->assert_child_ok($pid);
1465
1466  test_cleanup($setup->{log_file}, $ex);
1467}
1468
1469sub sql_group_where_clause_ok {
1470  my $self = shift;
1471  my $tmpdir = $self->{tmpdir};
1472
1473  my $config_file = "$tmpdir/sqlite.conf";
1474  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
1475  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
1476
1477  my $log_file = test_get_logfile();
1478
1479  my $user = 'proftpd';
1480  my $passwd = 'test';
1481  my $group = 'ftpd';
1482  my $home_dir = File::Spec->rel2abs($tmpdir);
1483  my $uid = 500;
1484  my $gid= 500;
1485
1486  my $acl_group = 'whitelist';
1487
1488  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1489
1490  # Build up sqlite3 command to create users, groups tables and populate them
1491  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1492
1493  if (open(my $fh, "> $db_script")) {
1494    print $fh <<EOS;
1495CREATE TABLE users (
1496  userid TEXT,
1497  passwd TEXT,
1498  uid INTEGER,
1499  gid INTEGER,
1500  homedir TEXT,
1501  shell TEXT
1502);
1503INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
1504
1505CREATE TABLE groups (
1506  groupname TEXT,
1507  gid INTEGER,
1508  members TEXT,
1509  allowed TEXT
1510);
1511INSERT INTO groups (groupname, gid, members, allowed) VALUES ('$group', $gid, '$user', 'false');
1512INSERT INTO groups (groupname, gid, members, allowed) VALUES ('$acl_group', $gid, '$user', 'true');
1513EOS
1514
1515    unless (close($fh)) {
1516      die("Can't write $db_script: $!");
1517    }
1518
1519  } else {
1520    die("Can't open $db_script: $!");
1521  }
1522
1523  my $cmd = "sqlite3 $db_file < $db_script";
1524  build_db($cmd, $db_script);
1525
1526  # Make sure that, if we're running as root, the database file has
1527  # the permissions/privs set for use by proftpd
1528  if ($< == 0) {
1529    unless (chmod(0666, $db_file)) {
1530      die("Can't set perms on $db_file to 0666: $!");
1531    }
1532  }
1533
1534  my $config = {
1535    PidFile => $pid_file,
1536    ScoreboardFile => $scoreboard_file,
1537    SystemLog => $log_file,
1538
1539    IfModules => {
1540      'mod_delay.c' => {
1541        DelayEngine => 'off',
1542      },
1543
1544      'mod_sql.c' => {
1545        AuthOrder => 'mod_sql.c',
1546
1547        SQLAuthenticate => 'users groups',
1548        SQLAuthTypes => 'plaintext',
1549        SQLBackend => 'sqlite3',
1550        SQLConnectInfo => $db_file,
1551        SQLLogFile => $log_file,
1552        SQLGroupWhereClause => '"allowed = \'true\'"',
1553        SQLOptions => 'useNormalizedGroupSchema',
1554      },
1555    },
1556  };
1557
1558  my ($port, $config_user, $config_group) = config_write($config_file, $config);
1559
1560  if (open(my $fh, ">> $config_file")) {
1561    print $fh <<EOC;
1562
1563<Limit LOGIN>
1564  AllowGroup $acl_group
1565  DenyAll
1566</Limit>
1567EOC
1568    unless (close($fh)) {
1569      die("Can't write $config_file: $!");
1570    }
1571
1572  } else {
1573    die("Can't open $config_file: $!");
1574  }
1575
1576  # Open pipes, for use between the parent and child processes.  Specifically,
1577  # the child will indicate when it's done with its test by writing a message
1578  # to the parent.
1579  my ($rfh, $wfh);
1580  unless (pipe($rfh, $wfh)) {
1581    die("Can't open pipe: $!");
1582  }
1583
1584  my $ex;
1585
1586  # Fork child
1587  $self->handle_sigchld();
1588  defined(my $pid = fork()) or die("Can't fork: $!");
1589  if ($pid) {
1590    eval {
1591      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1592      $client->login($user, $passwd);
1593      $client->quit();
1594    };
1595
1596    if ($@) {
1597      $ex = $@;
1598    }
1599
1600    $wfh->print("done\n");
1601    $wfh->flush();
1602
1603  } else {
1604    eval { server_wait($config_file, $rfh) };
1605    if ($@) {
1606      warn($@);
1607      exit 1;
1608    }
1609
1610    exit 0;
1611  }
1612
1613  # Stop server
1614  server_stop($pid_file);
1615
1616  $self->assert_child_ok($pid);
1617
1618  if ($ex) {
1619    test_append_logfile($log_file, $ex);
1620    unlink($log_file);
1621
1622    die($ex);
1623  }
1624
1625  unlink($log_file);
1626}
1627
1628sub sql_bug3149 {
1629  my $self = shift;
1630  my $tmpdir = $self->{tmpdir};
1631  my $setup = test_setup($tmpdir, 'sqlite');
1632
1633  my $user2 = 'proftpd2';
1634
1635  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1636
1637  # Build up sqlite3 command to create users, groups tables and populate them
1638  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1639
1640  if (open(my $fh, "> $db_script")) {
1641    print $fh <<EOS;
1642CREATE TABLE users (
1643  userid TEXT,
1644  passwd TEXT,
1645  uid INTEGER,
1646  gid INTEGER,
1647  homedir TEXT,
1648  shell TEXT,
1649  host TEXT
1650);
1651INSERT INTO users (userid, passwd, uid, gid, homedir, shell, host) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', 'remotehost');
1652INSERT INTO users (userid, passwd, uid, gid, homedir, shell, host) VALUES ('$user2', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', '127.0.0.1');
1653
1654CREATE TABLE groups (
1655  groupname TEXT,
1656  gid INTEGER,
1657  members TEXT
1658);
1659INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user},$user2');
1660EOS
1661
1662    unless (close($fh)) {
1663      die("Can't write $db_script: $!");
1664    }
1665
1666  } else {
1667    die("Can't open $db_script: $!");
1668  }
1669
1670  my $cmd = "sqlite3 $db_file < $db_script";
1671  build_db($cmd, $db_script);
1672
1673  # Make sure that, if we're running as root, the database file has
1674  # the permissions/privs set for use by proftpd
1675  if ($< == 0) {
1676    unless (chmod(0666, $db_file)) {
1677      die("Can't set perms on $db_file to 0666: $!");
1678    }
1679  }
1680
1681  my $config = {
1682    PidFile => $setup->{pid_file},
1683    ScoreboardFile => $setup->{scoreboard_file},
1684    SystemLog => $setup->{log_file},
1685    TraceLog => $setup->{log_file},
1686    Trace => 'jot:20 sql:20',
1687
1688    IfModules => {
1689      'mod_delay.c' => {
1690        DelayEngine => 'off',
1691      },
1692
1693      # Bug#3149 occurred because mod_sql's resolve_short_tag() function
1694      # was not able to resolve %V properly (it was deferencing a bad pointer).
1695      'mod_sql.c' => {
1696        SQLAuthTypes => 'plaintext',
1697        SQLBackend => 'sqlite3',
1698        SQLConnectInfo => $db_file,
1699        SQLLogFile => $setup->{log_file},
1700        SQLUserWhereClause => '"host=\'%V\'"',
1701      },
1702    },
1703  };
1704
1705  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1706    $config);
1707
1708  # Open pipes, for use between the parent and child processes.  Specifically,
1709  # the child will indicate when it's done with its test by writing a message
1710  # to the parent.
1711  my ($rfh, $wfh);
1712  unless (pipe($rfh, $wfh)) {
1713    die("Can't open pipe: $!");
1714  }
1715
1716  my $ex;
1717
1718  # Fork child
1719  $self->handle_sigchld();
1720  defined(my $pid = fork()) or die("Can't fork: $!");
1721  if ($pid) {
1722    eval {
1723      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1724
1725      # This account should not be allowed to login based on the
1726      # SQLUserWhereClause...
1727      eval { $client->login($setup->{user}, $setup->{passwd}) };
1728      unless ($@) {
1729        die("Login for user '$setup->{user}' succeeded unexpectedly");
1730      }
1731
1732      # ...but this one should succeed.
1733      $client->login($user2, $setup->{passwd});
1734      $client->quit();
1735    };
1736    if ($@) {
1737      $ex = $@;
1738    }
1739
1740    $wfh->print("done\n");
1741    $wfh->flush();
1742
1743  } else {
1744    eval { server_wait($setup->{config_file}, $rfh) };
1745    if ($@) {
1746      warn($@);
1747      exit 1;
1748    }
1749
1750    exit 0;
1751  }
1752
1753  # Stop server
1754  server_stop($setup->{pid_file});
1755  $self->assert_child_ok($pid);
1756
1757  test_cleanup($setup->{log_file}, $ex);
1758}
1759
1760sub get_sessions {
1761  my $db_file = shift;
1762  my $where = shift;
1763
1764  my $sql = "SELECT user, ip_addr, timestamp FROM ftpsessions";
1765  if ($where) {
1766    $sql .= " WHERE $where";
1767  }
1768
1769  my $cmd = "sqlite3 $db_file \"$sql\"";
1770
1771  if ($ENV{TEST_VERBOSE}) {
1772    print STDERR "Executing sqlite3: $cmd\n";
1773  }
1774
1775  my $res = join('', `$cmd`);
1776  chomp($res);
1777
1778  # The default sqlite3 delimiter is '|'
1779  return split(/\|/, $res);
1780}
1781
1782sub sql_sqllog {
1783  my $self = shift;
1784  my $tmpdir = $self->{tmpdir};
1785  my $setup = test_setup($tmpdir, 'sqlite');
1786
1787  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1788
1789  # Build up sqlite3 command to create users, groups tables and populate them
1790  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1791
1792  if (open(my $fh, "> $db_script")) {
1793    print $fh <<EOS;
1794CREATE TABLE ftpsessions (
1795  user TEXT,
1796  ip_addr TEXT,
1797  timestamp TEXT
1798);
1799EOS
1800
1801    unless (close($fh)) {
1802      die("Can't write $db_script: $!");
1803    }
1804
1805  } else {
1806    die("Can't open $db_script: $!");
1807  }
1808
1809  my $cmd = "sqlite3 $db_file < $db_script";
1810  build_db($cmd, $db_script);
1811
1812  # Make sure that, if we're running as root, the database file has
1813  # the permissions/privs set for use by proftpd
1814  if ($< == 0) {
1815    unless (chmod(0666, $db_file)) {
1816      die("Can't set perms on $db_file to 0666: $!");
1817    }
1818  }
1819
1820  my $config = {
1821    PidFile => $setup->{pid_file},
1822    ScoreboardFile => $setup->{scoreboard_file},
1823    SystemLog => $setup->{log_file},
1824    TraceLog => $setup->{log_file},
1825    Trace => 'jot:20 sql:20',
1826
1827    AuthUserFile => $setup->{auth_user_file},
1828    AuthGroupFile => $setup->{auth_group_file},
1829
1830    IfModules => {
1831      'mod_delay.c' => {
1832        DelayEngine => 'off',
1833      },
1834
1835      'mod_sql.c' => {
1836        SQLEngine => 'log',
1837        SQLBackend => 'sqlite3',
1838        SQLConnectInfo => $db_file,
1839        SQLLogFile => $setup->{log_file},
1840        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
1841        SQLLog => 'PASS session_start',
1842      },
1843    },
1844  };
1845
1846  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
1847    $config);
1848
1849  # Open pipes, for use between the parent and child processes.  Specifically,
1850  # the child will indicate when it's done with its test by writing a message
1851  # to the parent.
1852  my ($rfh, $wfh);
1853  unless (pipe($rfh, $wfh)) {
1854    die("Can't open pipe: $!");
1855  }
1856
1857  my $ex;
1858
1859  # Fork child
1860  $self->handle_sigchld();
1861  defined(my $pid = fork()) or die("Can't fork: $!");
1862  if ($pid) {
1863    eval {
1864      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
1865      $client->login($setup->{user}, $setup->{passwd});
1866      $client->quit();
1867    };
1868    if ($@) {
1869      $ex = $@;
1870    }
1871
1872    $wfh->print("done\n");
1873    $wfh->flush();
1874
1875  } else {
1876    eval { server_wait($setup->{config_file}, $rfh) };
1877    if ($@) {
1878      warn($@);
1879      exit 1;
1880    }
1881
1882    exit 0;
1883  }
1884
1885  # Stop server
1886  server_stop($setup->{pid_file});
1887  $self->assert_child_ok($pid);
1888
1889  if ($ex) {
1890    test_cleanup($setup->{log_file}, $ex);
1891  }
1892
1893  eval {
1894    my ($login, $ip_addr, $timestamp) = get_sessions($db_file,
1895      "user = \'$setup->{user}\'");
1896
1897    my $expected = $setup->{user};
1898    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
1899
1900    $expected = '127.0.0.1';
1901    $self->assert($expected eq $ip_addr,
1902      "Expected '$expected', got '$ip_addr'");
1903
1904    $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}';
1905    $self->assert(qr/$expected/, $timestamp,
1906      "Expected '$expected', got '$timestamp'");
1907  };
1908  if ($@) {
1909    $ex = $@;
1910  }
1911
1912  test_cleanup($setup->{log_file}, $ex);
1913}
1914
1915sub sql_sqlite_sqllog_with_chroot {
1916  my $self = shift;
1917  my $tmpdir = $self->{tmpdir};
1918
1919  my $config_file = "$tmpdir/sqlite.conf";
1920  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
1921  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
1922
1923  my $log_file = test_get_logfile();
1924
1925  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
1926  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
1927
1928  my $user = 'proftpd';
1929  my $passwd = 'test';
1930  my $group = 'ftpd';
1931  my $home_dir = File::Spec->rel2abs($tmpdir);
1932  my $uid = 500;
1933  my $gid = 500;
1934
1935  # Make sure that, if we're running as root, that the home directory has
1936  # permissions/privs set for the account we create
1937  if ($< == 0) {
1938    unless (chmod(0755, $home_dir)) {
1939      die("Can't set perms on $home_dir to 0755: $!");
1940    }
1941
1942    unless (chown($uid, $gid, $home_dir)) {
1943      die("Can't set owner of $home_dir to $uid/$gid: $!");
1944    }
1945  }
1946
1947  my $tmp_dir = File::Spec->rel2abs("$tmpdir/tmp");
1948  mkpath($tmp_dir);
1949  if ($< == 0) {
1950    unless (chmod(0777, $tmp_dir)) {
1951      die("Can't set perms on $tmp_dir to 0777: $!");
1952    }
1953  }
1954
1955  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
1956    '/bin/bash');
1957  auth_group_write($auth_group_file, '$group', $gid, $user);
1958
1959  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
1960
1961  # Build up sqlite3 command to create users, groups tables and populate them
1962  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
1963
1964  if (open(my $fh, "> $db_script")) {
1965    print $fh <<EOS;
1966CREATE TABLE ftpsessions (
1967  user TEXT,
1968  ip_addr TEXT,
1969  timestamp TEXT
1970);
1971EOS
1972
1973    unless (close($fh)) {
1974      die("Can't write $db_script: $!");
1975    }
1976
1977  } else {
1978    die("Can't open $db_script: $!");
1979  }
1980
1981  my $cmd = "sqlite3 $db_file < $db_script";
1982  build_db($cmd, $db_script);
1983
1984  # Make sure that, if we're running as root, the database file has
1985  # the permissions/privs set for use by proftpd
1986  if ($< == 0) {
1987    unless (chmod(0666, $db_file)) {
1988      die("Can't set perms on $db_file to 0666: $!");
1989    }
1990  }
1991
1992  my $config = {
1993    PidFile => $pid_file,
1994    ScoreboardFile => $scoreboard_file,
1995    SystemLog => $log_file,
1996
1997    AuthUserFile => $auth_user_file,
1998    AuthGroupFile => $auth_group_file,
1999    DefaultRoot => '~',
2000
2001    IfModules => {
2002      'mod_delay.c' => {
2003        DelayEngine => 'off',
2004      },
2005
2006      'mod_sql.c' => {
2007        SQLEngine => 'log',
2008        SQLBackend => 'sqlite3',
2009        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
2010        SQLLogFile => $log_file,
2011        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
2012        SQLLog => 'PASS session_start',
2013      },
2014    },
2015  };
2016
2017  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2018
2019  # Open pipes, for use between the parent and child processes.  Specifically,
2020  # the child will indicate when it's done with its test by writing a message
2021  # to the parent.
2022  my ($rfh, $wfh);
2023  unless (pipe($rfh, $wfh)) {
2024    die("Can't open pipe: $!");
2025  }
2026
2027  my $ex;
2028
2029  # Fork child
2030  $self->handle_sigchld();
2031  defined(my $pid = fork()) or die("Can't fork: $!");
2032  if ($pid) {
2033    eval {
2034      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2035
2036      $client->login($user, $passwd);
2037    };
2038
2039    if ($@) {
2040      $ex = $@;
2041    }
2042
2043    $wfh->print("done\n");
2044    $wfh->flush();
2045
2046  } else {
2047    eval { server_wait($config_file, $rfh) };
2048    if ($@) {
2049      warn($@);
2050      exit 1;
2051    }
2052
2053    exit 0;
2054  }
2055
2056  # Stop server
2057  server_stop($pid_file);
2058
2059  $self->assert_child_ok($pid);
2060
2061  if ($ex) {
2062    test_append_logfile($log_file, $ex);
2063    unlink($log_file);
2064
2065    die($ex);
2066  }
2067
2068  my ($login, $ip_addr, $timestamp) = get_sessions($db_file,
2069    "user = \'$user\'");
2070
2071  my $expected;
2072
2073  $expected = $user;
2074  $self->assert($expected eq $login,
2075    test_msg("Expected '$expected', got '$login'"));
2076
2077  $expected = '127.0.0.1';
2078  $self->assert($expected eq $ip_addr,
2079    test_msg("Expected '$expected', got '$ip_addr'"));
2080
2081  $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}';
2082  $self->assert(qr/$expected/, $timestamp,
2083    test_msg("Expected '$expected', got '$timestamp'"));
2084
2085  unlink($log_file);
2086}
2087
2088sub sql_user_info_different_table_names {
2089  my $self = shift;
2090  my $tmpdir = $self->{tmpdir};
2091
2092  my $config_file = "$tmpdir/sqlite.conf";
2093  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2094  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2095
2096  my $log_file = test_get_logfile();
2097
2098  my $user = 'proftpd';
2099  my $passwd = 'test';
2100  my $group = 'ftpd';
2101  my $home_dir = File::Spec->rel2abs($tmpdir);
2102  my $uid = 500;
2103  my $gid = 500;
2104
2105  # Make sure that, if we're running as root, that the home directory has
2106  # permissions/privs set for the account we create
2107  if ($< == 0) {
2108    unless (chmod(0755, $home_dir)) {
2109      die("Can't set perms on $home_dir to 0755: $!");
2110    }
2111
2112    unless (chown($uid, $gid, $home_dir)) {
2113      die("Can't set owner of $home_dir to $uid/$gid: $!");
2114    }
2115  }
2116
2117  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2118
2119  # Build up sqlite3 command to create users, groups tables and populate them
2120  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2121
2122  if (open(my $fh, "> $db_script")) {
2123    print $fh <<EOS;
2124CREATE TABLE ftpusers (
2125  userid TEXT,
2126  passwd TEXT,
2127  uid INTEGER,
2128  gid INTEGER,
2129  homedir TEXT,
2130  shell TEXT
2131);
2132INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
2133
2134CREATE TABLE ftpgroups (
2135  groupname TEXT,
2136  gid INTEGER,
2137  members TEXT
2138);
2139INSERT INTO ftpgroups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2140EOS
2141
2142    unless (close($fh)) {
2143      die("Can't write $db_script: $!");
2144    }
2145
2146  } else {
2147    die("Can't open $db_script: $!");
2148  }
2149
2150  my $cmd = "sqlite3 $db_file < $db_script";
2151  build_db($cmd, $db_script);
2152
2153  # Make sure that, if we're running as root, the database file has
2154  # the permissions/privs set for use by proftpd
2155  if ($< == 0) {
2156    unless (chmod(0666, $db_file)) {
2157      die("Can't set perms on $db_file to 0666: $!");
2158    }
2159  }
2160
2161  my $config = {
2162    PidFile => $pid_file,
2163    ScoreboardFile => $scoreboard_file,
2164    SystemLog => $log_file,
2165
2166    IfModules => {
2167      'mod_delay.c' => {
2168        DelayEngine => 'off',
2169      },
2170
2171      'mod_sql.c' => [
2172        'SQLAuthTypes plaintext',
2173        'SQLBackend sqlite3',
2174        "SQLConnectInfo $db_file",
2175        "SQLLogFile $log_file",
2176        'SQLUserInfo ftpusers userid passwd uid gid homedir shell',
2177        'SQLGroupInfo ftpgroups groupname gid members',
2178      ],
2179    },
2180
2181  };
2182
2183  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2184
2185  # Open pipes, for use between the parent and child processes.  Specifically,
2186  # the child will indicate when it's done with its test by writing a message
2187  # to the parent.
2188  my ($rfh, $wfh);
2189  unless (pipe($rfh, $wfh)) {
2190    die("Can't open pipe: $!");
2191  }
2192
2193  my $ex;
2194
2195  # Fork child
2196  $self->handle_sigchld();
2197  defined(my $pid = fork()) or die("Can't fork: $!");
2198  if ($pid) {
2199    eval {
2200      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2201      $client->login($user, $passwd);
2202
2203      my $resp_msgs = $client->response_msgs();
2204      my $nmsgs = scalar(@$resp_msgs);
2205
2206      my $expected;
2207
2208      $expected = 1;
2209      $self->assert($expected == $nmsgs,
2210        test_msg("Expected $expected, got $nmsgs"));
2211
2212      $expected = "User proftpd logged in";
2213      $self->assert($expected eq $resp_msgs->[0],
2214        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
2215
2216    };
2217
2218    if ($@) {
2219      $ex = $@;
2220    }
2221
2222    $wfh->print("done\n");
2223    $wfh->flush();
2224
2225  } else {
2226    eval { server_wait($config_file, $rfh) };
2227    if ($@) {
2228      warn($@);
2229      exit 1;
2230    }
2231
2232    exit 0;
2233  }
2234
2235  # Stop server
2236  server_stop($pid_file);
2237
2238  $self->assert_child_ok($pid);
2239
2240  if ($ex) {
2241    test_append_logfile($log_file, $ex);
2242    unlink($log_file);
2243
2244    die($ex);
2245  }
2246
2247  unlink($log_file);
2248}
2249
2250sub sql_custom_user_info {
2251  my $self = shift;
2252  my $tmpdir = $self->{tmpdir};
2253
2254  my $config_file = "$tmpdir/sqlite.conf";
2255  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2256  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2257
2258  my $log_file = test_get_logfile();
2259
2260  my $user = 'proftpd';
2261  my $passwd = 'test';
2262  my $group = 'ftpd';
2263  my $home_dir = File::Spec->rel2abs($tmpdir);
2264  my $uid = 500;
2265  my $gid = 500;
2266
2267  # Make sure that, if we're running as root, that the home directory has
2268  # permissions/privs set for the account we create
2269  if ($< == 0) {
2270    unless (chmod(0755, $home_dir)) {
2271      die("Can't set perms on $home_dir to 0755: $!");
2272    }
2273
2274    unless (chown($uid, $gid, $home_dir)) {
2275      die("Can't set owner of $home_dir to $uid/$gid: $!");
2276    }
2277  }
2278
2279  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2280
2281  # Build up sqlite3 command to create users, groups tables and populate them
2282  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2283
2284  if (open(my $fh, "> $db_script")) {
2285    print $fh <<EOS;
2286CREATE TABLE ftpusers (
2287  userid TEXT,
2288  passwd TEXT,
2289  uid INTEGER,
2290  gid INTEGER,
2291  homedir TEXT,
2292  shell TEXT,
2293  lastdir TEXT
2294);
2295INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
2296
2297CREATE TABLE groups (
2298  groupname TEXT,
2299  gid INTEGER,
2300  members TEXT
2301);
2302INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2303EOS
2304
2305    unless (close($fh)) {
2306      die("Can't write $db_script: $!");
2307    }
2308
2309  } else {
2310    die("Can't open $db_script: $!");
2311  }
2312
2313  my $cmd = "sqlite3 $db_file < $db_script";
2314  build_db($cmd, $db_script);
2315
2316  # Make sure that, if we're running as root, the database file has
2317  # the permissions/privs set for use by proftpd
2318  if ($< == 0) {
2319    unless (chmod(0666, $db_file)) {
2320      die("Can't set perms on $db_file to 0666: $!");
2321    }
2322  }
2323
2324  my $config = {
2325    PidFile => $pid_file,
2326    ScoreboardFile => $scoreboard_file,
2327    SystemLog => $log_file,
2328
2329    IfModules => {
2330      'mod_delay.c' => {
2331        DelayEngine => 'off',
2332      },
2333
2334      'mod_sql.c' => [
2335        'SQLAuthTypes plaintext',
2336        'SQLBackend sqlite3',
2337        "SQLConnectInfo $db_file",
2338        "SQLLogFile $log_file",
2339        'SQLNamedQuery get-user-by-name SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE userid = \'%U\'"',
2340        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE uid = %{0}"',
2341         'SQLNamedQuery get-user-names SELECT "userid FROM ftpusers"',
2342         'SQLNamedQuery get-all-users SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers"',
2343        'SQLUserInfo custom:/get-user-by-name/get-user-by-id/get-user-names/get-all-users',
2344      ],
2345    },
2346
2347
2348  };
2349
2350  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2351
2352  # Open pipes, for use between the parent and child processes.  Specifically,
2353  # the child will indicate when it's done with its test by writing a message
2354  # to the parent.
2355  my ($rfh, $wfh);
2356  unless (pipe($rfh, $wfh)) {
2357    die("Can't open pipe: $!");
2358  }
2359
2360  my $ex;
2361
2362  # Fork child
2363  $self->handle_sigchld();
2364  defined(my $pid = fork()) or die("Can't fork: $!");
2365  if ($pid) {
2366    eval {
2367      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2368
2369      $client->login($user, $passwd);
2370
2371      my $resp_msgs = $client->response_msgs();
2372      my $nmsgs = scalar(@$resp_msgs);
2373
2374      my $expected;
2375
2376      $expected = 1;
2377      $self->assert($expected == $nmsgs,
2378        test_msg("Expected $expected, got $nmsgs"));
2379
2380      $expected = "User proftpd logged in";
2381      $self->assert($expected eq $resp_msgs->[0],
2382        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
2383
2384    };
2385
2386    if ($@) {
2387      $ex = $@;
2388    }
2389
2390    $wfh->print("done\n");
2391    $wfh->flush();
2392
2393  } else {
2394    eval { server_wait($config_file, $rfh) };
2395    if ($@) {
2396      warn($@);
2397      exit 1;
2398    }
2399
2400    exit 0;
2401  }
2402
2403  # Stop server
2404  server_stop($pid_file);
2405
2406  $self->assert_child_ok($pid);
2407
2408  if ($ex) {
2409    test_append_logfile($log_file, $ex);
2410    unlink($log_file);
2411
2412    die($ex);
2413  }
2414
2415  unlink($log_file);
2416}
2417
2418sub sql_custom_user_info_null_ids {
2419  my $self = shift;
2420  my $tmpdir = $self->{tmpdir};
2421
2422  my $config_file = "$tmpdir/sqlite.conf";
2423  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2424  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2425
2426  my $log_file = test_get_logfile();
2427
2428  my $user = 'proftpd';
2429  my $passwd = 'test';
2430  my $group = 'ftpd';
2431  my $home_dir = File::Spec->rel2abs($tmpdir);
2432  my $uid = 500;
2433  my $gid = 500;
2434
2435  # Make sure that, if we're running as root, that the home directory has
2436  # permissions/privs set for the account we create
2437  if ($< == 0) {
2438    unless (chmod(0755, $home_dir)) {
2439      die("Can't set perms on $home_dir to 0755: $!");
2440    }
2441
2442    unless (chown($uid, $gid, $home_dir)) {
2443      die("Can't set owner of $home_dir to $uid/$gid: $!");
2444    }
2445  }
2446
2447  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2448
2449  # Build up sqlite3 command to create users, groups tables and populate them
2450  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2451
2452  if (open(my $fh, "> $db_script")) {
2453    # Note: by inserting/using NULL for the uid and gid columns, we will
2454    # force mod_sql to use the SQLDefaultUID/SQLDefaultGID values (rather than
2455    # e.g. segfaulting).
2456
2457    print $fh <<EOS;
2458CREATE TABLE ftpusers (
2459  userid TEXT,
2460  passwd TEXT,
2461  uid INTEGER,
2462  gid INTEGER,
2463  homedir TEXT,
2464  shell TEXT,
2465  lastdir TEXT
2466);
2467INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', NULL, NULL, '$home_dir', '/bin/bash');
2468
2469CREATE TABLE groups (
2470  groupname TEXT,
2471  gid INTEGER,
2472  members TEXT
2473);
2474INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2475EOS
2476
2477    unless (close($fh)) {
2478      die("Can't write $db_script: $!");
2479    }
2480
2481  } else {
2482    die("Can't open $db_script: $!");
2483  }
2484
2485  my $cmd = "sqlite3 $db_file < $db_script";
2486  build_db($cmd, $db_script);
2487
2488  # Make sure that, if we're running as root, the database file has
2489  # the permissions/privs set for use by proftpd
2490  if ($< == 0) {
2491    unless (chmod(0666, $db_file)) {
2492      die("Can't set perms on $db_file to 0666: $!");
2493    }
2494  }
2495
2496  my $config = {
2497    PidFile => $pid_file,
2498    ScoreboardFile => $scoreboard_file,
2499    SystemLog => $log_file,
2500
2501    IfModules => {
2502      'mod_delay.c' => {
2503        DelayEngine => 'off',
2504      },
2505
2506      'mod_sql.c' => [
2507        'SQLAuthTypes plaintext',
2508        'SQLBackend sqlite3',
2509        "SQLConnectInfo $db_file",
2510        "SQLLogFile $log_file",
2511        'SQLNamedQuery get-user-by-name SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE userid = \'%U\'"',
2512        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE uid = %{0}"',
2513         'SQLNamedQuery get-user-names SELECT "userid FROM ftpusers"',
2514         'SQLNamedQuery get-all-users SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers"',
2515        'SQLUserInfo custom:/get-user-by-name/get-user-by-id/get-user-names/get-all-users',
2516      ],
2517    },
2518
2519
2520  };
2521
2522  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2523
2524  # Open pipes, for use between the parent and child processes.  Specifically,
2525  # the child will indicate when it's done with its test by writing a message
2526  # to the parent.
2527  my ($rfh, $wfh);
2528  unless (pipe($rfh, $wfh)) {
2529    die("Can't open pipe: $!");
2530  }
2531
2532  my $ex;
2533
2534  # Fork child
2535  $self->handle_sigchld();
2536  defined(my $pid = fork()) or die("Can't fork: $!");
2537  if ($pid) {
2538    eval {
2539      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2540      $client->login($user, $passwd);
2541
2542      my $resp_msgs = $client->response_msgs();
2543      my $nmsgs = scalar(@$resp_msgs);
2544
2545      my $expected;
2546
2547      $expected = 1;
2548      $self->assert($expected == $nmsgs,
2549        test_msg("Expected $expected, got $nmsgs"));
2550
2551      $expected = "User proftpd logged in";
2552      $self->assert($expected eq $resp_msgs->[0],
2553        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
2554
2555    };
2556
2557    if ($@) {
2558      $ex = $@;
2559    }
2560
2561    $wfh->print("done\n");
2562    $wfh->flush();
2563
2564  } else {
2565    eval { server_wait($config_file, $rfh) };
2566    if ($@) {
2567      warn($@);
2568      exit 1;
2569    }
2570
2571    exit 0;
2572  }
2573
2574  # Stop server
2575  server_stop($pid_file);
2576
2577  $self->assert_child_ok($pid);
2578
2579  if ($ex) {
2580    test_append_logfile($log_file, $ex);
2581    unlink($log_file);
2582
2583    die($ex);
2584  }
2585
2586  unlink($log_file);
2587}
2588
2589sub sql_userset_bug2434 {
2590  my $self = shift;
2591  my $tmpdir = $self->{tmpdir};
2592
2593  my $config_file = "$tmpdir/sqlite.conf";
2594  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2595  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2596
2597  my $log_file = test_get_logfile();
2598
2599  my $user = 'proftpd';
2600  my $passwd = 'test';
2601  my $group = 'ftpd';
2602  my $home_dir = File::Spec->rel2abs($tmpdir);
2603  my $uid = 500;
2604  my $gid = 500;
2605
2606  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2607
2608  # Build up sqlite3 command to create users, groups tables and populate them
2609  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2610
2611  if (open(my $fh, "> $db_script")) {
2612    print $fh <<EOS;
2613CREATE TABLE ftpusers (
2614  userid TEXT,
2615  passwd TEXT,
2616  uid INTEGER,
2617  gid INTEGER,
2618  homedir TEXT,
2619  shell TEXT,
2620  lastdir TEXT
2621);
2622INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('foo', '$passwd', 500, 500, '$home_dir', '/bin/bash');
2623INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('bar', '$passwd', 500, 500, '$home_dir', '/bin/bash');
2624INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
2625
2626CREATE TABLE groups (
2627  groupname TEXT,
2628  gid INTEGER,
2629  members TEXT
2630);
2631INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2632EOS
2633
2634    unless (close($fh)) {
2635      die("Can't write $db_script: $!");
2636    }
2637
2638  } else {
2639    die("Can't open $db_script: $!");
2640  }
2641
2642  my $cmd = "sqlite3 $db_file < $db_script";
2643  build_db($cmd, $db_script);
2644
2645  # Make sure that, if we're running as root, the database file has
2646  # the permissions/privs set for use by proftpd
2647  if ($< == 0) {
2648    unless (chmod(0666, $db_file)) {
2649      die("Can't set perms on $db_file to 0666: $!");
2650    }
2651  }
2652
2653  my $config = {
2654    PidFile => $pid_file,
2655    ScoreboardFile => $scoreboard_file,
2656    SystemLog => $log_file,
2657
2658    # This bug requires that a chroot occur in order to be tickled.
2659    DefaultRoot => '~',
2660
2661    IfModules => {
2662      'mod_delay.c' => {
2663        DelayEngine => 'off',
2664      },
2665
2666      'mod_sql.c' => [
2667        'SQLAuthenticate users userset groups',
2668        'SQLAuthTypes plaintext',
2669        'SQLBackend sqlite3',
2670        "SQLConnectInfo $db_file",
2671        "SQLLogFile $log_file",
2672        'SQLNamedQuery get-all-users SELECT "userid FROM ftpusers"',
2673        'SQLNamedQuery get-user-by-name SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE userid = \'%U\'"',
2674        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE uid = %{0}"',
2675        'SQLUserInfo custom:/get-user-by-name/get-user-by-id/get-all-users',
2676      ],
2677    },
2678  };
2679
2680  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2681
2682  # Open pipes, for use between the parent and child processes.  Specifically,
2683  # the child will indicate when it's done with its test by writing a message
2684  # to the parent.
2685  my ($rfh, $wfh);
2686  unless (pipe($rfh, $wfh)) {
2687    die("Can't open pipe: $!");
2688  }
2689
2690  my $ex;
2691
2692  # Fork child
2693  $self->handle_sigchld();
2694  defined(my $pid = fork()) or die("Can't fork: $!");
2695  if ($pid) {
2696    eval {
2697      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2698
2699      $client->login($user, $passwd);
2700
2701      my $resp_msgs = $client->response_msgs();
2702      my $nmsgs = scalar(@$resp_msgs);
2703
2704      my $expected;
2705
2706      $expected = 1;
2707      $self->assert($expected == $nmsgs,
2708        test_msg("Expected $expected, got $nmsgs"));
2709
2710      $expected = "User proftpd logged in";
2711      $self->assert($expected eq $resp_msgs->[0],
2712        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
2713
2714    };
2715
2716    if ($@) {
2717      $ex = $@;
2718    }
2719
2720    $wfh->print("done\n");
2721    $wfh->flush();
2722
2723  } else {
2724    eval { server_wait($config_file, $rfh) };
2725    if ($@) {
2726      warn($@);
2727      exit 1;
2728    }
2729
2730    exit 0;
2731  }
2732
2733  # Stop server
2734  server_stop($pid_file);
2735
2736  $self->assert_child_ok($pid);
2737
2738  if ($ex) {
2739    test_append_logfile($log_file, $ex);
2740    unlink($log_file);
2741
2742    die($ex);
2743  }
2744
2745  unlink($log_file);
2746}
2747
2748sub sql_usersetfast_bug2434 {
2749  my $self = shift;
2750  my $tmpdir = $self->{tmpdir};
2751
2752  my $config_file = "$tmpdir/sqlite.conf";
2753  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2754  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2755
2756  my $log_file = test_get_logfile();
2757
2758  my $user = 'proftpd';
2759  my $passwd = 'test';
2760  my $group = 'ftpd';
2761  my $home_dir = File::Spec->rel2abs($tmpdir);
2762  my $uid = 500;
2763  my $gid = 500;
2764
2765  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2766
2767  # Build up sqlite3 command to create users, groups tables and populate them
2768  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2769
2770  if (open(my $fh, "> $db_script")) {
2771    print $fh <<EOS;
2772CREATE TABLE ftpusers (
2773  userid TEXT,
2774  passwd TEXT,
2775  uid INTEGER,
2776  gid INTEGER,
2777  homedir TEXT,
2778  shell TEXT,
2779  lastdir TEXT
2780);
2781INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('foo', '$passwd', 500, 500, '$home_dir', '/bin/bash');
2782INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('bar', '$passwd', 500, 500, '$home_dir', '/bin/bash');
2783INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
2784
2785CREATE TABLE groups (
2786  groupname TEXT,
2787  gid INTEGER,
2788  members TEXT
2789);
2790INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2791EOS
2792
2793    unless (close($fh)) {
2794      die("Can't write $db_script: $!");
2795    }
2796
2797  } else {
2798    die("Can't open $db_script: $!");
2799  }
2800
2801  my $cmd = "sqlite3 $db_file < $db_script";
2802  build_db($cmd, $db_script);
2803
2804  # Make sure that, if we're running as root, the database file has
2805  # the permissions/privs set for use by proftpd
2806  if ($< == 0) {
2807    unless (chmod(0666, $db_file)) {
2808      die("Can't set perms on $db_file to 0666: $!");
2809    }
2810  }
2811
2812  my $config = {
2813    PidFile => $pid_file,
2814    ScoreboardFile => $scoreboard_file,
2815    SystemLog => $log_file,
2816
2817    # This bug requires that a chroot occur in order to be tickled.
2818    DefaultRoot => '~',
2819
2820    IfModules => {
2821      'mod_delay.c' => {
2822        DelayEngine => 'off',
2823      },
2824
2825      'mod_sql.c' => [
2826        'SQLAuthenticate users usersetfast groups',
2827        'SQLAuthTypes plaintext',
2828        'SQLBackend sqlite3',
2829        "SQLConnectInfo $db_file",
2830        "SQLLogFile $log_file",
2831        'SQLNamedQuery get-all-usernames SELECT "userid FROM ftpusers"',
2832        'SQLNamedQuery get-all-users SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers"',
2833        'SQLNamedQuery get-user-by-name SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE userid = \'%U\'"',
2834        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE uid = %{0}"',
2835        'SQLUserInfo custom:/get-user-by-name/get-user-by-id/get-all-usernames/get-all-users',
2836      ],
2837    },
2838  };
2839
2840  my ($port, $config_user, $config_group) = config_write($config_file, $config);
2841
2842  # Open pipes, for use between the parent and child processes.  Specifically,
2843  # the child will indicate when it's done with its test by writing a message
2844  # to the parent.
2845  my ($rfh, $wfh);
2846  unless (pipe($rfh, $wfh)) {
2847    die("Can't open pipe: $!");
2848  }
2849
2850  my $ex;
2851
2852  # Fork child
2853  $self->handle_sigchld();
2854  defined(my $pid = fork()) or die("Can't fork: $!");
2855  if ($pid) {
2856    eval {
2857      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
2858
2859      $client->login($user, $passwd);
2860
2861      my $resp_msgs = $client->response_msgs();
2862      my $nmsgs = scalar(@$resp_msgs);
2863
2864      my $expected;
2865
2866      $expected = 1;
2867      $self->assert($expected == $nmsgs,
2868        test_msg("Expected $expected, got $nmsgs"));
2869
2870      $expected = "User proftpd logged in";
2871      $self->assert($expected eq $resp_msgs->[0],
2872        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
2873
2874    };
2875
2876    if ($@) {
2877      $ex = $@;
2878    }
2879
2880    $wfh->print("done\n");
2881    $wfh->flush();
2882
2883  } else {
2884    eval { server_wait($config_file, $rfh) };
2885    if ($@) {
2886      warn($@);
2887      exit 1;
2888    }
2889
2890    exit 0;
2891  }
2892
2893  # Stop server
2894  server_stop($pid_file);
2895
2896  $self->assert_child_ok($pid);
2897
2898  if ($ex) {
2899    test_append_logfile($log_file, $ex);
2900    unlink($log_file);
2901
2902    die($ex);
2903  }
2904
2905  unlink($log_file);
2906}
2907
2908sub sql_custom_group_info_bug3043 {
2909  my $self = shift;
2910  my $tmpdir = $self->{tmpdir};
2911
2912  my $config_file = "$tmpdir/sqlite.conf";
2913  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
2914  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
2915
2916  my $log_file = test_get_logfile();
2917
2918  my $user = 'proftpd';
2919  my $passwd = 'test';
2920  my $group = 'ftpd';
2921  my $home_dir = File::Spec->rel2abs($tmpdir);
2922  my $uid = 500;
2923  my $gid = 500;
2924
2925  # Make sure that, if we're running as root, that the home directory has
2926  # permissions/privs set for the account we create
2927  if ($< == 0) {
2928    unless (chmod(0755, $home_dir)) {
2929      die("Can't set perms on $home_dir to 0755: $!");
2930    }
2931
2932    unless (chown($uid, $gid, $home_dir)) {
2933      die("Can't set owner of $home_dir to $uid/$gid: $!");
2934    }
2935  }
2936
2937  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
2938
2939  # Build up sqlite3 command to create users, groups tables and populate them
2940  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
2941
2942  if (open(my $fh, "> $db_script")) {
2943    print $fh <<EOS;
2944CREATE TABLE users (
2945  userid TEXT,
2946  passwd TEXT,
2947  uid INTEGER,
2948  gid INTEGER,
2949  homedir TEXT,
2950  shell TEXT
2951);
2952INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
2953
2954CREATE TABLE ftpgroups (
2955  groupname TEXT,
2956  gid INTEGER,
2957  members TEXT
2958);
2959INSERT INTO ftpgroups (groupname, gid, members) VALUES ('$group', $gid, '$user');
2960EOS
2961
2962    unless (close($fh)) {
2963      die("Can't write $db_script: $!");
2964    }
2965
2966  } else {
2967    die("Can't open $db_script: $!");
2968  }
2969
2970  my $cmd = "sqlite3 $db_file < $db_script";
2971  build_db($cmd, $db_script);
2972
2973  # Make sure that, if we're running as root, the database file has
2974  # the permissions/privs set for use by proftpd
2975  if ($< == 0) {
2976    unless (chmod(0666, $db_file)) {
2977      die("Can't set perms on $db_file to 0666: $!");
2978    }
2979  }
2980
2981  my $config = {
2982    PidFile => $pid_file,
2983    ScoreboardFile => $scoreboard_file,
2984    SystemLog => $log_file,
2985
2986    IfModules => {
2987      'mod_delay.c' => {
2988        DelayEngine => 'off',
2989      },
2990
2991      'mod_sql.c' => [
2992        'SQLMinID 0',
2993        'SQLAuthenticate users groups',
2994        'SQLAuthTypes plaintext',
2995        'SQLBackend sqlite3',
2996        "SQLConnectInfo $db_file",
2997        "SQLLogFile $log_file",
2998        'SQLNamedQuery get-group-by-name SELECT "groupname, gid, members FROM ftpgroups WHERE groupname = \'%{0}\'"',
2999        'SQLNamedQuery get-group-by-id SELECT "groupname, gid, members FROM ftpgroups WHERE gid = %{0}"',
3000        'SQLNamedQuery get-members-by-user SELECT "members FROM ftpgroups WHERE (members LIKE \'%%,%{0},%%\' OR members LIKE \'%{0},%%\' OR members LIKE \'%%,%{0}\')"',
3001        'SQLGroupInfo custom:/get-group-by-name/get-group-by-id/get-members-by-user',
3002      ],
3003    },
3004  };
3005
3006  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3007
3008  # Open pipes, for use between the parent and child processes.  Specifically,
3009  # the child will indicate when it's done with its test by writing a message
3010  # to the parent.
3011  my ($rfh, $wfh);
3012  unless (pipe($rfh, $wfh)) {
3013    die("Can't open pipe: $!");
3014  }
3015
3016  my $ex;
3017
3018  # Fork child
3019  $self->handle_sigchld();
3020  defined(my $pid = fork()) or die("Can't fork: $!");
3021  if ($pid) {
3022    eval {
3023      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3024
3025      $client->login($user, $passwd);
3026
3027      my $resp_msgs = $client->response_msgs();
3028      my $nmsgs = scalar(@$resp_msgs);
3029
3030      my $expected;
3031
3032      $expected = 1;
3033      $self->assert($expected == $nmsgs,
3034        test_msg("Expected $expected, got $nmsgs"));
3035
3036      $expected = "User proftpd logged in";
3037      $self->assert($expected eq $resp_msgs->[0],
3038        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
3039
3040    };
3041
3042    if ($@) {
3043      $ex = $@;
3044    }
3045
3046    $wfh->print("done\n");
3047    $wfh->flush();
3048
3049  } else {
3050    eval { server_wait($config_file, $rfh) };
3051    if ($@) {
3052      warn($@);
3053      exit 1;
3054    }
3055
3056    exit 0;
3057  }
3058
3059  # Stop server
3060  server_stop($pid_file);
3061
3062  $self->assert_child_ok($pid);
3063
3064  if ($ex) {
3065    test_append_logfile($log_file, $ex);
3066    unlink($log_file);
3067
3068    die($ex);
3069  }
3070
3071  unlink($log_file);
3072}
3073
3074sub sql_groupset_bug3043 {
3075  my $self = shift;
3076  my $tmpdir = $self->{tmpdir};
3077
3078  my $config_file = "$tmpdir/sqlite.conf";
3079  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3080  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3081
3082  my $log_file = test_get_logfile();
3083
3084  my $user = 'proftpd';
3085  my $passwd = 'test';
3086  my $group = 'ftpd';
3087  my $home_dir = File::Spec->rel2abs($tmpdir);
3088  my $uid = 500;
3089  my $gid = 500;
3090
3091  # Make sure that, if we're running as root, that the home directory has
3092  # permissions/privs set for the account we create
3093  if ($< == 0) {
3094    unless (chmod(0755, $home_dir)) {
3095      die("Can't set perms on $home_dir to 0755: $!");
3096    }
3097
3098    unless (chown($uid, $gid, $home_dir)) {
3099      die("Can't set owner of $home_dir to $uid/$gid: $!");
3100    }
3101  }
3102
3103  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3104
3105  # Build up sqlite3 command to create users, groups tables and populate them
3106  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3107
3108  if (open(my $fh, "> $db_script")) {
3109    print $fh <<EOS;
3110CREATE TABLE users (
3111  userid TEXT,
3112  passwd TEXT,
3113  uid INTEGER,
3114  gid INTEGER,
3115  homedir TEXT,
3116  shell TEXT
3117);
3118INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
3119
3120CREATE TABLE ftpgroups (
3121  groupname TEXT,
3122  gid INTEGER,
3123  members TEXT
3124);
3125INSERT INTO ftpgroups (groupname, gid, members) VALUES ('$group', $gid, '$user');
3126EOS
3127
3128    unless (close($fh)) {
3129      die("Can't write $db_script: $!");
3130    }
3131
3132  } else {
3133    die("Can't open $db_script: $!");
3134  }
3135
3136  my $cmd = "sqlite3 $db_file < $db_script";
3137  build_db($cmd, $db_script);
3138
3139  # Make sure that, if we're running as root, the database file has
3140  # the permissions/privs set for use by proftpd
3141  if ($< == 0) {
3142    unless (chmod(0666, $db_file)) {
3143      die("Can't set perms on $db_file to 0666: $!");
3144    }
3145  }
3146
3147  my $config = {
3148    PidFile => $pid_file,
3149    ScoreboardFile => $scoreboard_file,
3150    SystemLog => $log_file,
3151
3152    IfModules => {
3153      'mod_delay.c' => {
3154        DelayEngine => 'off',
3155      },
3156
3157      'mod_sql.c' => [
3158        'SQLMinID 0',
3159        'SQLAuthenticate users groups groupset',
3160        'SQLAuthTypes plaintext',
3161        'SQLBackend sqlite3',
3162        "SQLConnectInfo $db_file",
3163        "SQLLogFile $log_file",
3164        'SQLNamedQuery get-group-by-name SELECT "groupname, gid, members FROM ftpgroups WHERE groupname = \'%{0}\'"',
3165        'SQLNamedQuery get-group-by-id SELECT "groupname, gid, members FROM ftpgroups WHERE gid = %{0}"',
3166        'SQLNamedQuery get-members-by-user SELECT "members FROM ftpgroups WHERE (members LIKE \'%%,%{0},%%\' OR members LIKE \'%{0},%%\' OR members LIKE \'%%,%{0}\')"',
3167        'SQLNamedQuery get-all-groupnames SELECT "groupname FROM ftpgroups"',
3168        'SQLGroupInfo custom:/get-group-by-name/get-group-by-id/get-members-by-user/get-all-groupnames',
3169      ],
3170    },
3171  };
3172
3173  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3174
3175  # Open pipes, for use between the parent and child processes.  Specifically,
3176  # the child will indicate when it's done with its test by writing a message
3177  # to the parent.
3178  my ($rfh, $wfh);
3179  unless (pipe($rfh, $wfh)) {
3180    die("Can't open pipe: $!");
3181  }
3182
3183  my $ex;
3184
3185  # Fork child
3186  $self->handle_sigchld();
3187  defined(my $pid = fork()) or die("Can't fork: $!");
3188  if ($pid) {
3189    eval {
3190      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3191
3192      $client->login($user, $passwd);
3193
3194      my $resp_msgs = $client->response_msgs();
3195      my $nmsgs = scalar(@$resp_msgs);
3196
3197      my $expected;
3198
3199      $expected = 1;
3200      $self->assert($expected == $nmsgs,
3201        test_msg("Expected $expected, got $nmsgs"));
3202
3203      $expected = "User proftpd logged in";
3204      $self->assert($expected eq $resp_msgs->[0],
3205        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
3206
3207    };
3208
3209    if ($@) {
3210      $ex = $@;
3211    }
3212
3213    $wfh->print("done\n");
3214    $wfh->flush();
3215
3216  } else {
3217    eval { server_wait($config_file, $rfh) };
3218    if ($@) {
3219      warn($@);
3220      exit 1;
3221    }
3222
3223    exit 0;
3224  }
3225
3226  # Stop server
3227  server_stop($pid_file);
3228
3229  $self->assert_child_ok($pid);
3230
3231  if ($ex) {
3232    test_append_logfile($log_file, $ex);
3233    unlink($log_file);
3234
3235    die($ex);
3236  }
3237
3238  unlink($log_file);
3239}
3240
3241sub sql_groupsetfast_bug3043 {
3242  my $self = shift;
3243  my $tmpdir = $self->{tmpdir};
3244
3245  my $config_file = "$tmpdir/sqlite.conf";
3246  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3247  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3248
3249  my $log_file = test_get_logfile();
3250
3251  my $user = 'proftpd';
3252  my $passwd = 'test';
3253  my $group = 'ftpd';
3254  my $home_dir = File::Spec->rel2abs($tmpdir);
3255  my $uid = 500;
3256  my $gid = 500;
3257
3258  # Make sure that, if we're running as root, that the home directory has
3259  # permissions/privs set for the account we create
3260  if ($< == 0) {
3261    unless (chmod(0755, $home_dir)) {
3262      die("Can't set perms on $home_dir to 0755: $!");
3263    }
3264
3265    unless (chown($uid, $gid, $home_dir)) {
3266      die("Can't set owner of $home_dir to $uid/$gid: $!");
3267    }
3268  }
3269
3270  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3271
3272  # Build up sqlite3 command to create users, groups tables and populate them
3273  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3274
3275  if (open(my $fh, "> $db_script")) {
3276    print $fh <<EOS;
3277CREATE TABLE users (
3278  userid TEXT,
3279  passwd TEXT,
3280  uid INTEGER,
3281  gid INTEGER,
3282  homedir TEXT,
3283  shell TEXT
3284);
3285INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
3286
3287CREATE TABLE ftpgroups (
3288  groupname TEXT,
3289  gid INTEGER,
3290  members TEXT
3291);
3292INSERT INTO ftpgroups (groupname, gid, members) VALUES ('$group', $gid, '$user');
3293EOS
3294
3295    unless (close($fh)) {
3296      die("Can't write $db_script: $!");
3297    }
3298
3299  } else {
3300    die("Can't open $db_script: $!");
3301  }
3302
3303  my $cmd = "sqlite3 $db_file < $db_script";
3304  build_db($cmd, $db_script);
3305
3306  # Make sure that, if we're running as root, the database file has
3307  # the permissions/privs set for use by proftpd
3308  if ($< == 0) {
3309    unless (chmod(0666, $db_file)) {
3310      die("Can't set perms on $db_file to 0666: $!");
3311    }
3312  }
3313
3314  my $config = {
3315    PidFile => $pid_file,
3316    ScoreboardFile => $scoreboard_file,
3317    SystemLog => $log_file,
3318
3319    IfModules => {
3320      'mod_delay.c' => {
3321        DelayEngine => 'off',
3322      },
3323
3324      'mod_sql.c' => [
3325        'SQLMinID 0',
3326        'SQLAuthenticate users groups groupsetfast',
3327        'SQLAuthTypes plaintext',
3328        'SQLBackend sqlite3',
3329        "SQLConnectInfo $db_file",
3330        "SQLLogFile $log_file",
3331        'SQLNamedQuery get-group-by-name SELECT "groupname, gid, members FROM ftpgroups WHERE groupname = \'%{0}\'"',
3332        'SQLNamedQuery get-group-by-id SELECT "groupname, gid, members FROM ftpgroups WHERE gid = %{0}"',
3333        'SQLNamedQuery get-group-by-member SELECT "groupname, gid, members FROM ftpgroups WHERE (members LIKE \'%%,%{0},%%\' OR members LIKE \'%{0},%%\' OR members LIKE \'%%,%{0}\')"',
3334        'SQLNamedQuery get-all-groupnames SELECT "groupname FROM ftpgroups"',
3335        'SQLNamedQuery get-all-groups SELECT "groupname, gid, members FROM ftpgroups"',
3336        'SQLGroupInfo custom:/get-group-by-name/get-group-by-id/get-group-by-member/get-all-groupnames/get-all-groups',
3337      ],
3338    },
3339  };
3340
3341  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3342
3343  # Open pipes, for use between the parent and child processes.  Specifically,
3344  # the child will indicate when it's done with its test by writing a message
3345  # to the parent.
3346  my ($rfh, $wfh);
3347  unless (pipe($rfh, $wfh)) {
3348    die("Can't open pipe: $!");
3349  }
3350
3351  my $ex;
3352
3353  # Fork child
3354  $self->handle_sigchld();
3355  defined(my $pid = fork()) or die("Can't fork: $!");
3356  if ($pid) {
3357    eval {
3358      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3359
3360      $client->login($user, $passwd);
3361
3362      my $resp_msgs = $client->response_msgs();
3363      my $nmsgs = scalar(@$resp_msgs);
3364
3365      my $expected;
3366
3367      $expected = 1;
3368      $self->assert($expected == $nmsgs,
3369        test_msg("Expected $expected, got $nmsgs"));
3370
3371      $expected = "User proftpd logged in";
3372      $self->assert($expected eq $resp_msgs->[0],
3373        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
3374
3375    };
3376
3377    if ($@) {
3378      $ex = $@;
3379    }
3380
3381    $wfh->print("done\n");
3382    $wfh->flush();
3383
3384  } else {
3385    eval { server_wait($config_file, $rfh) };
3386    if ($@) {
3387      warn($@);
3388      exit 1;
3389    }
3390
3391    exit 0;
3392  }
3393
3394  # Stop server
3395  server_stop($pid_file);
3396
3397  $self->assert_child_ok($pid);
3398
3399  if ($ex) {
3400    test_append_logfile($log_file, $ex);
3401    unlink($log_file);
3402
3403    die($ex);
3404  }
3405
3406  unlink($log_file);
3407}
3408
3409sub get_renames {
3410  my $db_file = shift;
3411  my $where = shift;
3412
3413  my $sql = "SELECT user, ip_addr, rename_from FROM ftpsessions";
3414  if ($where) {
3415    $sql .= " WHERE $where";
3416  }
3417
3418  my $cmd = "sqlite3 $db_file \"$sql\"";
3419
3420  if ($ENV{TEST_VERBOSE}) {
3421    print STDERR "Executing sqlite3: $cmd\n";
3422  }
3423
3424  my $res = join('', `$cmd`);
3425  chomp($res);
3426
3427  # The default sqlite3 delimiter is '|'
3428  return split(/\|/, $res);
3429}
3430
3431sub sql_sqllog_var_w {
3432  my $self = shift;
3433  my $tmpdir = $self->{tmpdir};
3434
3435  my $config_file = "$tmpdir/sqlite.conf";
3436  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3437  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3438
3439  my $log_file = test_get_logfile();
3440
3441  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
3442  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
3443
3444  my $user = 'proftpd';
3445  my $passwd = 'test';
3446  my $group = 'ftpd';
3447  my $home_dir = File::Spec->rel2abs($tmpdir);
3448  my $uid = 500;
3449  my $gid = 500;
3450
3451  # Make sure that, if we're running as root, that the home directory has
3452  # permissions/privs set for the account we create
3453  if ($< == 0) {
3454    unless (chmod(0755, $home_dir)) {
3455      die("Can't set perms on $home_dir to 0755: $!");
3456    }
3457
3458    unless (chown($uid, $gid, $home_dir)) {
3459      die("Can't set owner of $home_dir to $uid/$gid: $!");
3460    }
3461  }
3462
3463  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
3464    '/bin/bash');
3465  auth_group_write($auth_group_file, $group, $gid, $user);
3466
3467  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3468
3469  # Build up sqlite3 command to create users, groups tables and populate them
3470  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3471
3472  if (open(my $fh, "> $db_script")) {
3473    print $fh <<EOS;
3474CREATE TABLE ftpsessions (
3475  user TEXT,
3476  ip_addr TEXT,
3477  rename_from TEXT
3478);
3479EOS
3480
3481    unless (close($fh)) {
3482      die("Can't write $db_script: $!");
3483    }
3484
3485  } else {
3486    die("Can't open $db_script: $!");
3487  }
3488
3489  my $cmd = "sqlite3 $db_file < $db_script";
3490  build_db($cmd, $db_script);
3491
3492  # Make sure that, if we're running as root, the database file has
3493  # the permissions/privs set for use by proftpd
3494  if ($< == 0) {
3495    unless (chmod(0666, $db_file)) {
3496      die("Can't set perms on $db_file to 0666: $!");
3497    }
3498  }
3499
3500  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
3501  if (open(my $fh, "> $src_file")) {
3502    close($fh);
3503
3504  } else {
3505    die("Can't open $src_file: $!");
3506  }
3507
3508  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
3509
3510  my $config = {
3511    PidFile => $pid_file,
3512    ScoreboardFile => $scoreboard_file,
3513    SystemLog => $log_file,
3514
3515    AuthUserFile => $auth_user_file,
3516    AuthGroupFile => $auth_group_file,
3517
3518    IfModules => {
3519      'mod_delay.c' => {
3520        DelayEngine => 'off',
3521      },
3522
3523      'mod_sql.c' => {
3524        SQLEngine => 'log',
3525        SQLBackend => 'sqlite3',
3526        SQLConnectInfo => $db_file,
3527        SQLLogFile => $log_file,
3528        SQLNamedQuery => 'rename FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from) VALUES (\'%u\', \'%L\', \'%w\')"',
3529        SQLLog => 'RNTO rename',
3530      },
3531    },
3532  };
3533
3534  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3535
3536  # Open pipes, for use between the parent and child processes.  Specifically,
3537  # the child will indicate when it's done with its test by writing a message
3538  # to the parent.
3539  my ($rfh, $wfh);
3540  unless (pipe($rfh, $wfh)) {
3541    die("Can't open pipe: $!");
3542  }
3543
3544  my $ex;
3545
3546  # Fork child
3547  $self->handle_sigchld();
3548  defined(my $pid = fork()) or die("Can't fork: $!");
3549  if ($pid) {
3550    eval {
3551      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3552      $client->login($user, $passwd);
3553      $client->rnfr('test.txt');
3554      $client->rnto('foo.txt');
3555    };
3556
3557    if ($@) {
3558      $ex = $@;
3559    }
3560
3561    $wfh->print("done\n");
3562    $wfh->flush();
3563
3564  } else {
3565    eval { server_wait($config_file, $rfh) };
3566    if ($@) {
3567      warn($@);
3568      exit 1;
3569    }
3570
3571    exit 0;
3572  }
3573
3574  # Stop server
3575  server_stop($pid_file);
3576
3577  $self->assert_child_ok($pid);
3578
3579  if ($ex) {
3580    test_append_logfile($log_file, $ex);
3581    unlink($log_file);
3582
3583    die($ex);
3584  }
3585
3586  my ($login, $ip_addr, $rnfr_path) = get_renames($db_file, "user = \'$user\'");
3587
3588  my $expected;
3589
3590  $expected = $user;
3591  $self->assert($expected eq $login,
3592    test_msg("Expected '$expected', got '$login'"));
3593
3594  $expected = '127.0.0.1';
3595  $self->assert($expected eq $ip_addr,
3596    test_msg("Expected '$expected', got '$ip_addr'"));
3597
3598  if ($^O eq 'darwin') {
3599    # MacOSX-specific hack
3600    $src_file = '/private' . $src_file;
3601  }
3602
3603  $expected = $src_file;
3604  $self->assert($expected eq $rnfr_path,
3605    test_msg("Expected '$expected', got '$rnfr_path'"));
3606
3607  unlink($log_file);
3608}
3609
3610sub sql_sqllog_var_w_chrooted {
3611  my $self = shift;
3612  my $tmpdir = $self->{tmpdir};
3613
3614  my $config_file = "$tmpdir/sqlite.conf";
3615  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3616  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3617
3618  my $log_file = test_get_logfile();
3619
3620  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
3621  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
3622
3623  my $user = 'proftpd';
3624  my $passwd = 'test';
3625  my $group = 'ftpd';
3626  my $home_dir = File::Spec->rel2abs($tmpdir);
3627  my $uid = 500;
3628  my $gid = 500;
3629
3630  # Make sure that, if we're running as root, that the home directory has
3631  # permissions/privs set for the account we create
3632  if ($< == 0) {
3633    unless (chmod(0755, $home_dir)) {
3634      die("Can't set perms on $home_dir to 0755: $!");
3635    }
3636
3637    unless (chown($uid, $gid, $home_dir)) {
3638      die("Can't set owner of $home_dir to $uid/$gid: $!");
3639    }
3640  }
3641
3642  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
3643    '/bin/bash');
3644  auth_group_write($auth_group_file, $group, $gid, $user);
3645
3646  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3647
3648  # Build up sqlite3 command to create users, groups tables and populate them
3649  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3650
3651  if (open(my $fh, "> $db_script")) {
3652    print $fh <<EOS;
3653CREATE TABLE ftpsessions (
3654  user TEXT,
3655  ip_addr TEXT,
3656  rename_from TEXT
3657);
3658EOS
3659
3660    unless (close($fh)) {
3661      die("Can't write $db_script: $!");
3662    }
3663
3664  } else {
3665    die("Can't open $db_script: $!");
3666  }
3667
3668  my $cmd = "sqlite3 $db_file < $db_script";
3669  build_db($cmd, $db_script);
3670
3671  # Make sure that, if we're running as root, the database file has
3672  # the permissions/privs set for use by proftpd
3673  if ($< == 0) {
3674    unless (chmod(0666, $db_file)) {
3675      die("Can't set perms on $db_file to 0666: $!");
3676    }
3677  }
3678
3679  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
3680  if (open(my $fh, "> $src_file")) {
3681    close($fh);
3682
3683  } else {
3684    die("Can't open $src_file: $!");
3685  }
3686
3687  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
3688
3689  my $config = {
3690    PidFile => $pid_file,
3691    ScoreboardFile => $scoreboard_file,
3692    SystemLog => $log_file,
3693
3694    AuthUserFile => $auth_user_file,
3695    AuthGroupFile => $auth_group_file,
3696    DefaultRoot => '~',
3697
3698    IfModules => {
3699      'mod_delay.c' => {
3700        DelayEngine => 'off',
3701      },
3702
3703      'mod_sql.c' => {
3704        SQLEngine => 'log',
3705        SQLBackend => 'sqlite3',
3706        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
3707        SQLLogFile => $log_file,
3708        SQLNamedQuery => 'rename FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from) VALUES (\'%u\', \'%L\', \'%w\')"',
3709        SQLLog => 'RNTO rename',
3710      },
3711    },
3712  };
3713
3714  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3715
3716  # Open pipes, for use between the parent and child processes.  Specifically,
3717  # the child will indicate when it's done with its test by writing a message
3718  # to the parent.
3719  my ($rfh, $wfh);
3720  unless (pipe($rfh, $wfh)) {
3721    die("Can't open pipe: $!");
3722  }
3723
3724  my $ex;
3725
3726  # Fork child
3727  $self->handle_sigchld();
3728  defined(my $pid = fork()) or die("Can't fork: $!");
3729  if ($pid) {
3730    eval {
3731      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3732
3733      $client->login($user, $passwd);
3734      $client->rnfr('test.txt');
3735      $client->rnto('foo.txt');
3736    };
3737
3738    if ($@) {
3739      $ex = $@;
3740    }
3741
3742    $wfh->print("done\n");
3743    $wfh->flush();
3744
3745  } else {
3746    eval { server_wait($config_file, $rfh) };
3747    if ($@) {
3748      warn($@);
3749      exit 1;
3750    }
3751
3752    exit 0;
3753  }
3754
3755  # Stop server
3756  server_stop($pid_file);
3757
3758  $self->assert_child_ok($pid);
3759
3760  if ($ex) {
3761    test_append_logfile($log_file, $ex);
3762    unlink($log_file);
3763
3764    die($ex);
3765  }
3766
3767  my ($login, $ip_addr, $rnfr_path) = get_renames($db_file, "user = \'$user\'");
3768
3769  my $expected;
3770
3771  $expected = $user;
3772  $self->assert($expected eq $login,
3773    test_msg("Expected '$expected', got '$login'"));
3774
3775  $expected = '127.0.0.1';
3776  $self->assert($expected eq $ip_addr,
3777    test_msg("Expected '$expected', got '$ip_addr'"));
3778
3779  $expected = $src_file;
3780  $self->assert($expected eq $rnfr_path,
3781    test_msg("Expected '$expected', got '$rnfr_path'"));
3782
3783  unlink($log_file);
3784}
3785
3786sub sql_openssl_auth_type_md5 {
3787  my $self = shift;
3788  my $tmpdir = $self->{tmpdir};
3789
3790  my $config_file = "$tmpdir/sqlite.conf";
3791  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3792  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3793
3794  my $log_file = test_get_logfile();
3795
3796  my $user = 'proftpd';
3797  my $passwd = '{md5}X03MO1qnZdYdgyfeuILPmQ==';
3798  my $group = 'ftpd';
3799  my $home_dir = File::Spec->rel2abs($tmpdir);
3800  my $uid = 500;
3801  my $gid = 500;
3802
3803  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3804
3805  # Build up sqlite3 command to create users, groups tables and populate them
3806  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3807
3808  if (open(my $fh, "> $db_script")) {
3809    print $fh <<EOS;
3810CREATE TABLE users (
3811  userid TEXT,
3812  passwd TEXT,
3813  uid INTEGER,
3814  gid INTEGER,
3815  homedir TEXT,
3816  shell TEXT,
3817  lastdir TEXT
3818);
3819INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
3820
3821CREATE TABLE groups (
3822  groupname TEXT,
3823  gid INTEGER,
3824  members TEXT
3825);
3826INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
3827EOS
3828
3829    unless (close($fh)) {
3830      die("Can't write $db_script: $!");
3831    }
3832
3833  } else {
3834    die("Can't open $db_script: $!");
3835  }
3836
3837  my $cmd = "sqlite3 $db_file < $db_script";
3838  build_db($cmd, $db_script);
3839
3840  # Make sure that, if we're running as root, the database file has
3841  # the permissions/privs set for use by proftpd
3842  if ($< == 0) {
3843    unless (chmod(0666, $db_file)) {
3844      die("Can't set perms on $db_file to 0666: $!");
3845    }
3846  }
3847
3848  my $config = {
3849    PidFile => $pid_file,
3850    ScoreboardFile => $scoreboard_file,
3851    SystemLog => $log_file,
3852
3853    IfModules => {
3854      'mod_delay.c' => {
3855        DelayEngine => 'off',
3856      },
3857
3858      'mod_sql.c' => {
3859        SQLAuthTypes => 'OpenSSL',
3860        SQLBackend => 'sqlite3',
3861        SQLConnectInfo => $db_file,
3862        SQLLogFile => $log_file,
3863      },
3864    },
3865  };
3866
3867  my ($port, $config_user, $config_group) = config_write($config_file, $config);
3868
3869  # Open pipes, for use between the parent and child processes.  Specifically,
3870  # the child will indicate when it's done with its test by writing a message
3871  # to the parent.
3872  my ($rfh, $wfh);
3873  unless (pipe($rfh, $wfh)) {
3874    die("Can't open pipe: $!");
3875  }
3876
3877  my $ex;
3878
3879  # Fork child
3880  $self->handle_sigchld();
3881  defined(my $pid = fork()) or die("Can't fork: $!");
3882  if ($pid) {
3883    eval {
3884      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
3885
3886      $client->login($user, 'password');
3887
3888      my $resp_msgs = $client->response_msgs();
3889      my $nmsgs = scalar(@$resp_msgs);
3890
3891      my $expected;
3892
3893      $expected = 1;
3894      $self->assert($expected == $nmsgs,
3895        test_msg("Expected $expected, got $nmsgs"));
3896
3897      $expected = "User proftpd logged in";
3898      $self->assert($expected eq $resp_msgs->[0],
3899        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
3900
3901    };
3902
3903    if ($@) {
3904      $ex = $@;
3905    }
3906
3907    $wfh->print("done\n");
3908    $wfh->flush();
3909
3910  } else {
3911    eval { server_wait($config_file, $rfh) };
3912    if ($@) {
3913      warn($@);
3914      exit 1;
3915    }
3916
3917    exit 0;
3918  }
3919
3920  # Stop server
3921  server_stop($pid_file);
3922
3923  $self->assert_child_ok($pid);
3924
3925  if ($ex) {
3926    test_append_logfile($log_file, $ex);
3927    unlink($log_file);
3928
3929    die($ex);
3930  }
3931
3932  unlink($log_file);
3933}
3934
3935sub sql_openssl_auth_type_sha1 {
3936  my $self = shift;
3937  my $tmpdir = $self->{tmpdir};
3938
3939  my $config_file = "$tmpdir/sqlite.conf";
3940  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
3941  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
3942
3943  my $log_file = test_get_logfile();
3944
3945  my $user = 'proftpd';
3946  my $passwd = '{sha1}W6ph5Mm5Pz8GgiULbPgzG37mj9g=';
3947  my $group = 'ftpd';
3948  my $home_dir = File::Spec->rel2abs($tmpdir);
3949  my $uid = 500;
3950  my $gid = 500;
3951
3952  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
3953
3954  # Build up sqlite3 command to create users, groups tables and populate them
3955  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
3956
3957  if (open(my $fh, "> $db_script")) {
3958    print $fh <<EOS;
3959CREATE TABLE users (
3960  userid TEXT,
3961  passwd TEXT,
3962  uid INTEGER,
3963  gid INTEGER,
3964  homedir TEXT,
3965  shell TEXT,
3966  lastdir TEXT
3967);
3968INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
3969
3970CREATE TABLE groups (
3971  groupname TEXT,
3972  gid INTEGER,
3973  members TEXT
3974);
3975INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
3976EOS
3977
3978    unless (close($fh)) {
3979      die("Can't write $db_script: $!");
3980    }
3981
3982  } else {
3983    die("Can't open $db_script: $!");
3984  }
3985
3986  my $cmd = "sqlite3 $db_file < $db_script";
3987  build_db($cmd, $db_script);
3988
3989  # Make sure that, if we're running as root, the database file has
3990  # the permissions/privs set for use by proftpd
3991  if ($< == 0) {
3992    unless (chmod(0666, $db_file)) {
3993      die("Can't set perms on $db_file to 0666: $!");
3994    }
3995  }
3996
3997  my $config = {
3998    PidFile => $pid_file,
3999    ScoreboardFile => $scoreboard_file,
4000    SystemLog => $log_file,
4001
4002    IfModules => {
4003      'mod_delay.c' => {
4004        DelayEngine => 'off',
4005      },
4006
4007      'mod_sql.c' => {
4008        SQLAuthTypes => 'OpenSSL',
4009        SQLBackend => 'sqlite3',
4010        SQLConnectInfo => $db_file,
4011        SQLLogFile => $log_file,
4012      },
4013    },
4014  };
4015
4016  my ($port, $config_user, $config_group) = config_write($config_file, $config);
4017
4018  # Open pipes, for use between the parent and child processes.  Specifically,
4019  # the child will indicate when it's done with its test by writing a message
4020  # to the parent.
4021  my ($rfh, $wfh);
4022  unless (pipe($rfh, $wfh)) {
4023    die("Can't open pipe: $!");
4024  }
4025
4026  my $ex;
4027
4028  # Fork child
4029  $self->handle_sigchld();
4030  defined(my $pid = fork()) or die("Can't fork: $!");
4031  if ($pid) {
4032    eval {
4033      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
4034
4035      $client->login($user, 'password');
4036
4037      my $resp_msgs = $client->response_msgs();
4038      my $nmsgs = scalar(@$resp_msgs);
4039
4040      my $expected;
4041
4042      $expected = 1;
4043      $self->assert($expected == $nmsgs,
4044        test_msg("Expected $expected, got $nmsgs"));
4045
4046      $expected = "User proftpd logged in";
4047      $self->assert($expected eq $resp_msgs->[0],
4048        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
4049
4050    };
4051
4052    if ($@) {
4053      $ex = $@;
4054    }
4055
4056    $wfh->print("done\n");
4057    $wfh->flush();
4058
4059  } else {
4060    eval { server_wait($config_file, $rfh) };
4061    if ($@) {
4062      warn($@);
4063      exit 1;
4064    }
4065
4066    exit 0;
4067  }
4068
4069  # Stop server
4070  server_stop($pid_file);
4071
4072  $self->assert_child_ok($pid);
4073
4074  if ($ex) {
4075    test_append_logfile($log_file, $ex);
4076    unlink($log_file);
4077
4078    die($ex);
4079  }
4080
4081  unlink($log_file);
4082}
4083
4084sub get_resp_mesgs {
4085  my $db_file = shift;
4086  my $where = shift;
4087  my $cols = shift;
4088
4089  my $sql = "SELECT $cols FROM ftpsessions";
4090  if ($where) {
4091    $sql .= " WHERE $where";
4092  }
4093
4094  my $cmd = "sqlite3 $db_file \"$sql\"";
4095
4096  if ($ENV{TEST_VERBOSE}) {
4097    print STDERR "Executing sqlite3: $cmd\n";
4098  }
4099
4100  my $res = join('', `$cmd`);
4101  chomp($res);
4102
4103  # The default sqlite3 delimiter is '|'
4104  return split(/\|/, $res);
4105}
4106
4107sub sql_sqllog_var_S {
4108  my $self = shift;
4109  my $tmpdir = $self->{tmpdir};
4110
4111  my $config_file = "$tmpdir/sqlite.conf";
4112  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
4113  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
4114
4115  my $log_file = test_get_logfile();
4116
4117  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
4118  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
4119
4120  my $user = 'proftpd';
4121  my $passwd = 'test';
4122  my $group = 'ftpd';
4123  my $home_dir = File::Spec->rel2abs($tmpdir);
4124  my $uid = 500;
4125  my $gid = 500;
4126
4127  # Make sure that, if we're running as root, that the home directory has
4128  # permissions/privs set for the account we create
4129  if ($< == 0) {
4130    unless (chmod(0755, $home_dir)) {
4131      die("Can't set perms on $home_dir to 0755: $!");
4132    }
4133
4134    unless (chown($uid, $gid, $home_dir)) {
4135      die("Can't set owner of $home_dir to $uid/$gid: $!");
4136    }
4137  }
4138
4139  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
4140    '/bin/bash');
4141  auth_group_write($auth_group_file, $group, $gid, $user);
4142
4143  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4144
4145  # Build up sqlite3 command to create users, groups tables and populate them
4146  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4147
4148  if (open(my $fh, "> $db_script")) {
4149    print $fh <<EOS;
4150CREATE TABLE ftpsessions (
4151  user TEXT,
4152  ip_addr TEXT,
4153  resp_mesg TEXT
4154);
4155EOS
4156
4157    unless (close($fh)) {
4158      die("Can't write $db_script: $!");
4159    }
4160
4161  } else {
4162    die("Can't open $db_script: $!");
4163  }
4164
4165  my $cmd = "sqlite3 $db_file < $db_script";
4166  build_db($cmd, $db_script);
4167
4168  # Make sure that, if we're running as root, the database file has
4169  # the permissions/privs set for use by proftpd
4170  if ($< == 0) {
4171    unless (chmod(0666, $db_file)) {
4172      die("Can't set perms on $db_file to 0666: $!");
4173    }
4174  }
4175
4176  my $config = {
4177    PidFile => $pid_file,
4178    ScoreboardFile => $scoreboard_file,
4179    SystemLog => $log_file,
4180
4181    AuthUserFile => $auth_user_file,
4182    AuthGroupFile => $auth_group_file,
4183
4184    IfModules => {
4185      'mod_delay.c' => {
4186        DelayEngine => 'off',
4187      },
4188
4189      'mod_sql.c' => {
4190        SQLEngine => 'log',
4191        SQLBackend => 'sqlite3',
4192        SQLConnectInfo => $db_file,
4193        SQLLogFile => $log_file,
4194        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, ip_addr, resp_mesg) VALUES (\'%u\', \'%L\', \'%S\')"',
4195        SQLLog => 'LIST info',
4196      },
4197    },
4198  };
4199
4200  my ($port, $config_user, $config_group) = config_write($config_file, $config);
4201
4202  # Open pipes, for use between the parent and child processes.  Specifically,
4203  # the child will indicate when it's done with its test by writing a message
4204  # to the parent.
4205  my ($rfh, $wfh);
4206  unless (pipe($rfh, $wfh)) {
4207    die("Can't open pipe: $!");
4208  }
4209
4210  my $ex;
4211
4212  # Fork child
4213  $self->handle_sigchld();
4214  defined(my $pid = fork()) or die("Can't fork: $!");
4215  if ($pid) {
4216    eval {
4217      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
4218      $client->login($user, $passwd);
4219      $client->list();
4220      $client->quit();
4221    };
4222
4223    if ($@) {
4224      $ex = $@;
4225    }
4226
4227    $wfh->print("done\n");
4228    $wfh->flush();
4229
4230  } else {
4231    eval { server_wait($config_file, $rfh) };
4232    if ($@) {
4233      warn($@);
4234      exit 1;
4235    }
4236
4237    exit 0;
4238  }
4239
4240  # Stop server
4241  server_stop($pid_file);
4242
4243  $self->assert_child_ok($pid);
4244
4245  if ($ex) {
4246    test_append_logfile($log_file, $ex);
4247    unlink($log_file);
4248
4249    die($ex);
4250  }
4251
4252  my ($login, $ip_addr, $resp_mesg) = get_resp_mesgs($db_file,
4253    "user = \'$user\'", "user, ip_addr, resp_mesg");
4254
4255  my $expected;
4256
4257  $expected = $user;
4258  $self->assert($expected eq $login,
4259    test_msg("Expected '$expected', got '$login'"));
4260
4261  $expected = '127.0.0.1';
4262  $self->assert($expected eq $ip_addr,
4263    test_msg("Expected '$expected', got '$ip_addr'"));
4264
4265  $expected = 'Transfer complete';
4266  $self->assert($expected eq $resp_mesg,
4267    test_msg("Expected '$expected', got '$resp_mesg'"));
4268
4269  unlink($log_file);
4270}
4271
4272sub sql_sqllog_var_S_err {
4273  my $self = shift;
4274  my $tmpdir = $self->{tmpdir};
4275  my $setup = test_setup($tmpdir, 'sqlite');
4276
4277  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4278
4279  # Build up sqlite3 command to create users, groups tables and populate them
4280  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4281
4282  if (open(my $fh, "> $db_script")) {
4283    print $fh <<EOS;
4284CREATE TABLE ftpsessions (
4285  user TEXT,
4286  ip_addr TEXT,
4287  resp_mesg TEXT
4288);
4289EOS
4290
4291    unless (close($fh)) {
4292      die("Can't write $db_script: $!");
4293    }
4294
4295  } else {
4296    die("Can't open $db_script: $!");
4297  }
4298
4299  my $cmd = "sqlite3 $db_file < $db_script";
4300  build_db($cmd, $db_script);
4301
4302  # Make sure that, if we're running as root, the database file has
4303  # the permissions/privs set for use by proftpd
4304  if ($< == 0) {
4305    unless (chmod(0666, $db_file)) {
4306      die("Can't set perms on $db_file to 0666: $!");
4307    }
4308  }
4309
4310  my $config = {
4311    PidFile => $setup->{pid_file},
4312    ScoreboardFile => $setup->{scoreboard_file},
4313    SystemLog => $setup->{log_file},
4314    TraceLog => $setup->{log_file},
4315    Trace => 'jot:20 sql:20',
4316
4317    AuthUserFile => $setup->{auth_user_file},
4318    AuthGroupFile => $setup->{auth_group_file},
4319
4320    IfModules => {
4321      'mod_delay.c' => {
4322        DelayEngine => 'off',
4323      },
4324
4325      'mod_sql.c' => {
4326        SQLEngine => 'log',
4327        SQLBackend => 'sqlite3',
4328        SQLConnectInfo => $db_file,
4329        SQLLogFile => $setup->{log_file},
4330        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, ip_addr, resp_mesg) VALUES (\'%u\', \'%L\', \'%S\')"',
4331        SQLLog => 'ERR_* info',
4332      },
4333    },
4334  };
4335
4336  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
4337    $config);
4338
4339  # Open pipes, for use between the parent and child processes.  Specifically,
4340  # the child will indicate when it's done with its test by writing a message
4341  # to the parent.
4342  my ($rfh, $wfh);
4343  unless (pipe($rfh, $wfh)) {
4344    die("Can't open pipe: $!");
4345  }
4346
4347  my $ex;
4348
4349  # Fork child
4350  $self->handle_sigchld();
4351  defined(my $pid = fork()) or die("Can't fork: $!");
4352  if ($pid) {
4353    eval {
4354      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
4355      $client->login($setup->{user}, $setup->{passwd});
4356      eval { $client->quote('CLIENT', 'NcFTP 3.2.2 linux-x86_64-glibc2.3') };
4357      $client->quit();
4358    };
4359    if ($@) {
4360      $ex = $@;
4361    }
4362
4363    $wfh->print("done\n");
4364    $wfh->flush();
4365
4366  } else {
4367    eval { server_wait($setup->{config_file}, $rfh) };
4368    if ($@) {
4369      warn($@);
4370      exit 1;
4371    }
4372
4373    exit 0;
4374  }
4375
4376  # Stop server
4377  server_stop($setup->{pid_file});
4378  $self->assert_child_ok($pid);
4379
4380  if ($ex) {
4381    test_cleanup($setup->{log_file}, $ex);
4382  }
4383
4384  eval {
4385    my ($login, $ip_addr, $resp_mesg) = get_resp_mesgs($db_file,
4386      "user = \'$setup->{user}\'", "user, ip_addr, resp_mesg");
4387
4388    my $expected = $setup->{user};
4389    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
4390
4391    $expected = '127.0.0.1';
4392    $self->assert($expected eq $ip_addr,
4393      "Expected '$expected', got '$ip_addr'");
4394
4395    $expected = 'CLIENT not understood';
4396    $self->assert($expected eq $resp_mesg,
4397      "Expected '$expected', got '$resp_mesg'");
4398  };
4399  if ($@) {
4400    $ex = $@;
4401  }
4402
4403  test_cleanup($setup->{log_file}, $ex);
4404}
4405
4406sub sql_negative_cache_bug3282 {
4407  my $self = shift;
4408  my $tmpdir = $self->{tmpdir};
4409
4410  my $config_file = "$tmpdir/sqlite.conf";
4411  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
4412  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
4413
4414  my $log_file = test_get_logfile();
4415
4416  my $user = 'proftpd';
4417  my $passwd = 'test';
4418  my $group = 'ftpd';
4419  my $home_dir = File::Spec->rel2abs($tmpdir);
4420  my $uid = 500;
4421  my $gid = 500;
4422
4423  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4424
4425  # Build up sqlite3 command to create users, groups tables and populate them
4426  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4427
4428  if (open(my $fh, "> $db_script")) {
4429    print $fh <<EOS;
4430CREATE TABLE users (
4431  userid TEXT,
4432  passwd TEXT,
4433  uid INTEGER,
4434  gid INTEGER,
4435  homedir TEXT,
4436  shell TEXT,
4437  lastdir TEXT
4438);
4439INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
4440
4441CREATE TABLE groups (
4442  groupname TEXT,
4443  gid INTEGER,
4444  members TEXT
4445);
4446INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
4447EOS
4448
4449    unless (close($fh)) {
4450      die("Can't write $db_script: $!");
4451    }
4452
4453  } else {
4454    die("Can't open $db_script: $!");
4455  }
4456
4457  my $cmd = "sqlite3 $db_file < $db_script";
4458  build_db($cmd, $db_script);
4459
4460  # Make sure that, if we're running as root, the database file has
4461  # the permissions/privs set for use by proftpd
4462  if ($< == 0) {
4463    unless (chmod(0666, $db_file)) {
4464      die("Can't set perms on $db_file to 0666: $!");
4465    }
4466  }
4467
4468  my $config = {
4469    PidFile => $pid_file,
4470    ScoreboardFile => $scoreboard_file,
4471    SystemLog => $log_file,
4472    TraceLog => $log_file,
4473    Trace => 'auth:10',
4474
4475    IfModules => {
4476      'mod_delay.c' => {
4477        DelayEngine => 'off',
4478      },
4479
4480      'mod_sql.c' => {
4481        SQLAuthTypes => 'plaintext',
4482        SQLBackend => 'sqlite3',
4483        SQLConnectInfo => $db_file,
4484        SQLLogFile => $log_file,
4485        SQLNegativeCache => 'on',
4486      },
4487    },
4488  };
4489
4490  my ($port, $config_user, $config_group) = config_write($config_file, $config);
4491
4492  # Open pipes, for use between the parent and child processes.  Specifically,
4493  # the child will indicate when it's done with its test by writing a message
4494  # to the parent.
4495  my ($rfh, $wfh);
4496  unless (pipe($rfh, $wfh)) {
4497    die("Can't open pipe: $!");
4498  }
4499
4500  my $ex;
4501
4502  # Fork child
4503  $self->handle_sigchld();
4504  defined(my $pid = fork()) or die("Can't fork: $!");
4505  if ($pid) {
4506    eval {
4507      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
4508
4509      eval { $client->login('foo', 'foo') };
4510      unless ($@) {
4511        die("Login succeeded unexpectedly");
4512      }
4513
4514      $client->login($user, $passwd);
4515
4516      my $expected;
4517
4518      my $resp_msgs = $client->response_msgs();
4519      my $nmsgs = scalar(@$resp_msgs);
4520
4521      $expected = 1;
4522      $self->assert($expected == $nmsgs,
4523        test_msg("Expected $expected, got $nmsgs"));
4524
4525      $expected = "User proftpd logged in";
4526      $self->assert($expected eq $resp_msgs->[0],
4527        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
4528    };
4529
4530    if ($@) {
4531      $ex = $@;
4532    }
4533
4534    $wfh->print("done\n");
4535    $wfh->flush();
4536
4537  } else {
4538    eval { server_wait($config_file, $rfh) };
4539    if ($@) {
4540      warn($@);
4541      exit 1;
4542    }
4543
4544    exit 0;
4545  }
4546
4547  # Stop server
4548  server_stop($pid_file);
4549
4550  $self->assert_child_ok($pid);
4551
4552  if ($ex) {
4553    test_append_logfile($log_file, $ex);
4554    unlink($log_file);
4555
4556    die($ex);
4557  }
4558
4559  unlink($log_file);
4560}
4561
4562sub sql_sqllog_var_T_rnfr {
4563  my $self = shift;
4564  my $tmpdir = $self->{tmpdir};
4565  my $setup = test_setup($tmpdir, 'sqlite');
4566
4567  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4568
4569  # Build up sqlite3 command to create users, groups tables and populate them
4570  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4571
4572  if (open(my $fh, "> $db_script")) {
4573    print $fh <<EOS;
4574CREATE TABLE ftpsessions (
4575  user TEXT,
4576  ip_addr TEXT,
4577  rename_from TEXT,
4578  rename_time FLOAT
4579);
4580EOS
4581
4582    unless (close($fh)) {
4583      die("Can't write $db_script: $!");
4584    }
4585
4586  } else {
4587    die("Can't open $db_script: $!");
4588  }
4589
4590  my $cmd = "sqlite3 $db_file < $db_script";
4591  build_db($cmd, $db_script);
4592
4593  # Make sure that, if we're running as root, the database file has
4594  # the permissions/privs set for use by proftpd
4595  if ($< == 0) {
4596    unless (chmod(0666, $db_file)) {
4597      die("Can't set perms on $db_file to 0666: $!");
4598    }
4599  }
4600
4601  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
4602  if (open(my $fh, "> $src_file")) {
4603    close($fh);
4604
4605  } else {
4606    die("Can't open $src_file: $!");
4607  }
4608
4609  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
4610
4611  my $config = {
4612    PidFile => $setup->{pid_file},
4613    ScoreboardFile => $setup->{scoreboard_file},
4614    SystemLog => $setup->{log_file},
4615    TraceLog => $setup->{log_file},
4616    Trace => 'jot:20 sql:20',
4617
4618    AuthUserFile => $setup->{auth_user_file},
4619    AuthGroupFile => $setup->{auth_group_file},
4620
4621    IfModules => {
4622      'mod_delay.c' => {
4623        DelayEngine => 'off',
4624      },
4625
4626      'mod_sql.c' => {
4627        SQLEngine => 'log',
4628        SQLBackend => 'sqlite3',
4629        SQLConnectInfo => $db_file,
4630        SQLLogFile => $setup->{log_file},
4631        SQLNamedQuery => 'rename FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from, rename_time) VALUES (\'%u\', \'%L\', \'%w\', %T)"',
4632        SQLLog => 'RNTO rename',
4633      },
4634    },
4635  };
4636
4637  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
4638    $config);
4639
4640  # Open pipes, for use between the parent and child processes.  Specifically,
4641  # the child will indicate when it's done with its test by writing a message
4642  # to the parent.
4643  my ($rfh, $wfh);
4644  unless (pipe($rfh, $wfh)) {
4645    die("Can't open pipe: $!");
4646  }
4647
4648  my $ex;
4649
4650  # Fork child
4651  $self->handle_sigchld();
4652  defined(my $pid = fork()) or die("Can't fork: $!");
4653  if ($pid) {
4654    eval {
4655      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
4656      $client->login($setup->{user}, $setup->{passwd});
4657      $client->rnfr('test.txt');
4658      $client->rnto('foo.txt');
4659    };
4660    if ($@) {
4661      $ex = $@;
4662    }
4663
4664    $wfh->print("done\n");
4665    $wfh->flush();
4666
4667  } else {
4668    eval { server_wait($setup->{config_file}, $rfh) };
4669    if ($@) {
4670      warn($@);
4671      exit 1;
4672    }
4673
4674    exit 0;
4675  }
4676
4677  # Stop server
4678  server_stop($setup->{pid_file});
4679  $self->assert_child_ok($pid);
4680
4681  if ($ex) {
4682    test_cleanup($setup->{log_file}, $ex);
4683  }
4684
4685  eval {
4686    my $query = "SELECT user, ip_addr, rename_from, rename_time FROM ftpsessions WHERE user = \'$setup->{user}\'";
4687    $cmd = "sqlite3 $db_file \"$query\"";
4688
4689    if ($ENV{TEST_VERBOSE}) {
4690      print STDERR "Executing sqlite3: $cmd\n";
4691    }
4692
4693    my $res = join('', `$cmd`);
4694    chomp($res);
4695
4696    my ($login, $ip_addr, $rnfr_path, $rename_time) = split(/\|/, $res);
4697
4698    my $expected = $setup->{user};
4699    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
4700
4701    $expected = '127.0.0.1';
4702    $self->assert($expected eq $ip_addr,
4703      "Expected '$expected', got '$ip_addr'");
4704
4705    if ($^O eq 'darwin') {
4706      # MacOSX-specific hack
4707      $src_file = '/private' . $src_file;
4708    }
4709
4710    $expected = $src_file;
4711    $self->assert($expected eq $rnfr_path,
4712      "Expected '$expected', got '$rnfr_path'");
4713
4714    $expected = '0.0';
4715    $self->assert($expected eq $rename_time,
4716      "Expected '$expected', got '$rename_time'");
4717  };
4718  if ($@) {
4719    $ex = $@;
4720  }
4721
4722  test_cleanup($setup->{log_file}, $ex);
4723}
4724
4725sub sql_sqllog_var_T_retr {
4726  my $self = shift;
4727  my $tmpdir = $self->{tmpdir};
4728  my $setup = test_setup($tmpdir, 'sqlite');
4729
4730  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4731
4732  # Build up sqlite3 command to create users, groups tables and populate them
4733  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4734
4735  if (open(my $fh, "> $db_script")) {
4736    print $fh <<EOS;
4737CREATE TABLE ftpsessions (
4738  user TEXT PRIMARY KEY,
4739  ip_addr TEXT,
4740  download_time FLOAT
4741);
4742EOS
4743
4744    unless (close($fh)) {
4745      die("Can't write $db_script: $!");
4746    }
4747
4748  } else {
4749    die("Can't open $db_script: $!");
4750  }
4751
4752  my $cmd = "sqlite3 $db_file < $db_script";
4753  build_db($cmd, $db_script);
4754
4755  # Make sure that, if we're running as root, the database file has
4756  # the permissions/privs set for use by proftpd
4757  if ($< == 0) {
4758    unless (chmod(0666, $db_file)) {
4759      die("Can't set perms on $db_file to 0666: $!");
4760    }
4761  }
4762
4763  my $config = {
4764    PidFile => $setup->{pid_file},
4765    ScoreboardFile => $setup->{scoreboard_file},
4766    SystemLog => $setup->{log_file},
4767
4768    AuthUserFile => $setup->{auth_user_file},
4769    AuthGroupFile => $setup->{auth_group_file},
4770
4771    IfModules => {
4772      'mod_delay.c' => {
4773        DelayEngine => 'off',
4774      },
4775
4776      'mod_sql.c' => {
4777        SQLEngine => 'log',
4778        SQLBackend => 'sqlite3',
4779        SQLConnectInfo => $db_file,
4780        SQLLogFile => $setup->{log_file},
4781        SQLNamedQuery => 'download FREEFORM "INSERT INTO ftpsessions (user, ip_addr, download_time) VALUES (\'%u\', \'%L\', %T)"',
4782        SQLLog => 'RETR download',
4783      },
4784    },
4785  };
4786
4787  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
4788    $config);
4789
4790  # Open pipes, for use between the parent and child processes.  Specifically,
4791  # the child will indicate when it's done with its test by writing a message
4792  # to the parent.
4793  my ($rfh, $wfh);
4794  unless (pipe($rfh, $wfh)) {
4795    die("Can't open pipe: $!");
4796  }
4797
4798  my $ex;
4799
4800  # Fork child
4801  $self->handle_sigchld();
4802  defined(my $pid = fork()) or die("Can't fork: $!");
4803  if ($pid) {
4804    eval {
4805      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
4806      $client->login($setup->{user}, $setup->{passwd});
4807
4808      my $conn = $client->retr_raw('sqlite.conf');
4809      unless ($conn) {
4810        die("RETR sqlite.conf failed: " . $client->response_code() . " " .
4811          $client->response_msg());
4812      }
4813
4814      my $buf;
4815      while ($conn->read($buf, 8192, 30)) {
4816      }
4817      $conn->close();
4818
4819      my $resp_code = $client->response_code();
4820      my $resp_msg = $client->response_msg();
4821      $self->assert_transfer_ok($resp_code, $resp_msg);
4822
4823      $client->quit();
4824    };
4825
4826    if ($@) {
4827      $ex = $@;
4828    }
4829
4830    $wfh->print("done\n");
4831    $wfh->flush();
4832
4833  } else {
4834    eval { server_wait($setup->{config_file}, $rfh) };
4835    if ($@) {
4836      warn($@);
4837      exit 1;
4838    }
4839
4840    exit 0;
4841  }
4842
4843  # Stop server
4844  server_stop($setup->{pid_file});
4845  $self->assert_child_ok($pid);
4846
4847  eval {
4848    my $query = "SELECT user, ip_addr, download_time FROM ftpsessions WHERE user = \'$setup->{user}\'";
4849    $cmd = "sqlite3 $db_file \"$query\"";
4850
4851    if ($ENV{TEST_VERBOSE}) {
4852      print STDERR "Executing sqlite3: $cmd\n";
4853    }
4854
4855    my $res = join('', `$cmd`);
4856    chomp($res);
4857
4858    my ($login, $ip_addr, $download_time) = split(/\|/, $res);
4859
4860    if ($ENV{TEST_VERBOSE}) {
4861      print STDERR "# login = '$login', ip_addr = '$ip_addr', download_time = $download_time\n";
4862    }
4863
4864    my $expected;
4865
4866    $expected = $setup->{user};
4867    $self->assert($expected eq $login,
4868      test_msg("Expected '$expected', got '$login'"));
4869
4870    $expected = '127.0.0.1';
4871    $self->assert($expected eq $ip_addr,
4872      test_msg("Expected '$expected', got '$ip_addr'"));
4873
4874    $self->assert($download_time > 0.0,
4875      test_msg("Expected > 0.0, got $download_time"));
4876  };
4877  if ($@) {
4878    $ex = $@;
4879  }
4880
4881  test_cleanup($setup->{log_file}, $ex);
4882}
4883
4884sub sql_sqllog_var_transfer_millisecs_retr_bug4218 {
4885  my $self = shift;
4886  my $tmpdir = $self->{tmpdir};
4887  my $setup = test_setup($tmpdir, 'sqlite');
4888
4889  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
4890
4891  # Build up sqlite3 command to create users, groups tables and populate them
4892  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
4893
4894  if (open(my $fh, "> $db_script")) {
4895    print $fh <<EOS;
4896CREATE TABLE ftpsessions (
4897  user TEXT PRIMARY KEY,
4898  ip_addr TEXT,
4899  download_ms INTEGER
4900);
4901EOS
4902
4903    unless (close($fh)) {
4904      die("Can't write $db_script: $!");
4905    }
4906
4907  } else {
4908    die("Can't open $db_script: $!");
4909  }
4910
4911  my $cmd = "sqlite3 $db_file < $db_script";
4912  build_db($cmd, $db_script);
4913
4914  # Make sure that, if we're running as root, the database file has
4915  # the permissions/privs set for use by proftpd
4916  if ($< == 0) {
4917    unless (chmod(0666, $db_file)) {
4918      die("Can't set perms on $db_file to 0666: $!");
4919    }
4920  }
4921
4922  my $config = {
4923    PidFile => $setup->{pid_file},
4924    ScoreboardFile => $setup->{scoreboard_file},
4925    SystemLog => $setup->{log_file},
4926
4927    AuthUserFile => $setup->{auth_user_file},
4928    AuthGroupFile => $setup->{auth_group_file},
4929
4930    IfModules => {
4931      'mod_delay.c' => {
4932        DelayEngine => 'off',
4933      },
4934
4935      'mod_sql.c' => {
4936        SQLEngine => 'log',
4937        SQLBackend => 'sqlite3',
4938        SQLConnectInfo => $db_file,
4939        SQLLogFile => $setup->{log_file},
4940        SQLNamedQuery => 'download FREEFORM "INSERT INTO ftpsessions (user, ip_addr, download_ms) VALUES (\'%u\', \'%L\', %{transfer-millisecs})"',
4941        SQLLog => 'RETR download',
4942      },
4943    },
4944  };
4945
4946  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
4947    $config);
4948
4949  # Open pipes, for use between the parent and child processes.  Specifically,
4950  # the child will indicate when it's done with its test by writing a message
4951  # to the parent.
4952  my ($rfh, $wfh);
4953  unless (pipe($rfh, $wfh)) {
4954    die("Can't open pipe: $!");
4955  }
4956
4957  my $ex;
4958
4959  # Fork child
4960  $self->handle_sigchld();
4961  defined(my $pid = fork()) or die("Can't fork: $!");
4962  if ($pid) {
4963    eval {
4964      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
4965      $client->login($setup->{user}, $setup->{passwd});
4966
4967      my $conn = $client->retr_raw('sqlite.conf');
4968      unless ($conn) {
4969        die("RETR sqlite.conf failed: " . $client->response_code() . " " .
4970          $client->response_msg());
4971      }
4972
4973      my $buf;
4974      while ($conn->read($buf, 8192, 30)) {
4975      }
4976      $conn->close();
4977
4978      my $resp_code = $client->response_code();
4979      my $resp_msg = $client->response_msg();
4980      $self->assert_transfer_ok($resp_code, $resp_msg);
4981
4982      $client->quit();
4983    };
4984
4985    if ($@) {
4986      $ex = $@;
4987    }
4988
4989    $wfh->print("done\n");
4990    $wfh->flush();
4991
4992  } else {
4993    eval { server_wait($setup->{config_file}, $rfh) };
4994    if ($@) {
4995      warn($@);
4996      exit 1;
4997    }
4998
4999    exit 0;
5000  }
5001
5002  # Stop server
5003  server_stop($setup->{pid_file});
5004  $self->assert_child_ok($pid);
5005
5006  eval {
5007    my $query = "SELECT user, ip_addr, download_ms FROM ftpsessions WHERE user = \'$setup->{user}\'";
5008    $cmd = "sqlite3 $db_file \"$query\"";
5009
5010    if ($ENV{TEST_VERBOSE}) {
5011      print STDERR "Executing sqlite3: $cmd\n";
5012    }
5013
5014    my $res = join('', `$cmd`);
5015    chomp($res);
5016
5017    my ($login, $ip_addr, $download_ms) = split(/\|/, $res);
5018
5019    if ($ENV{TEST_VERBOSE}) {
5020      print STDERR "# login = '$login', ip_addr = '$ip_addr', download_ms = $download_ms\n";
5021    }
5022
5023    my $expected;
5024
5025    $expected = $setup->{user};
5026    $self->assert($expected eq $login,
5027      test_msg("Expected '$expected', got '$login'"));
5028
5029    $expected = '127.0.0.1';
5030    $self->assert($expected eq $ip_addr,
5031      test_msg("Expected '$expected', got '$ip_addr'"));
5032
5033    $self->assert($download_ms > 0,
5034      test_msg("Expected > 0, got $download_ms"));
5035  };
5036  if ($@) {
5037    $ex = $@;
5038  }
5039
5040  test_cleanup($setup->{log_file}, $ex);
5041}
5042
5043sub sql_sqllog_var_R_retr_bug4218 {
5044  my $self = shift;
5045  my $tmpdir = $self->{tmpdir};
5046  my $setup = test_setup($tmpdir, 'sqlite');
5047
5048  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5049
5050  # Build up sqlite3 command to create users, groups tables and populate them
5051  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5052
5053  if (open(my $fh, "> $db_script")) {
5054    print $fh <<EOS;
5055CREATE TABLE ftpsessions (
5056  user TEXT PRIMARY KEY,
5057  ip_addr TEXT,
5058  response_ms INTEGER
5059);
5060EOS
5061
5062    unless (close($fh)) {
5063      die("Can't write $db_script: $!");
5064    }
5065
5066  } else {
5067    die("Can't open $db_script: $!");
5068  }
5069
5070  my $cmd = "sqlite3 $db_file < $db_script";
5071  build_db($cmd, $db_script);
5072
5073  # Make sure that, if we're running as root, the database file has
5074  # the permissions/privs set for use by proftpd
5075  if ($< == 0) {
5076    unless (chmod(0666, $db_file)) {
5077      die("Can't set perms on $db_file to 0666: $!");
5078    }
5079  }
5080
5081  my $config = {
5082    PidFile => $setup->{pid_file},
5083    ScoreboardFile => $setup->{scoreboard_file},
5084    SystemLog => $setup->{log_file},
5085
5086    AuthUserFile => $setup->{auth_user_file},
5087    AuthGroupFile => $setup->{auth_group_file},
5088
5089    IfModules => {
5090      'mod_delay.c' => {
5091        DelayEngine => 'off',
5092      },
5093
5094      'mod_sql.c' => {
5095        SQLEngine => 'log',
5096        SQLBackend => 'sqlite3',
5097        SQLConnectInfo => $db_file,
5098        SQLLogFile => $setup->{log_file},
5099        SQLNamedQuery => 'download FREEFORM "INSERT INTO ftpsessions (user, ip_addr, response_ms) VALUES (\'%u\', \'%L\', %R)"',
5100        SQLLog => 'RETR download',
5101      },
5102    },
5103  };
5104
5105  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
5106    $config);
5107
5108  # Open pipes, for use between the parent and child processes.  Specifically,
5109  # the child will indicate when it's done with its test by writing a message
5110  # to the parent.
5111  my ($rfh, $wfh);
5112  unless (pipe($rfh, $wfh)) {
5113    die("Can't open pipe: $!");
5114  }
5115
5116  my $ex;
5117
5118  # Fork child
5119  $self->handle_sigchld();
5120  defined(my $pid = fork()) or die("Can't fork: $!");
5121  if ($pid) {
5122    eval {
5123      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
5124      $client->login($setup->{user}, $setup->{passwd});
5125
5126      my $conn = $client->retr_raw('sqlite.conf');
5127      unless ($conn) {
5128        die("RETR sqlite.conf failed: " . $client->response_code() . " " .
5129          $client->response_msg());
5130      }
5131
5132      my $buf;
5133      while ($conn->read($buf, 8192, 30)) {
5134      }
5135      $conn->close();
5136
5137      my $resp_code = $client->response_code();
5138      my $resp_msg = $client->response_msg();
5139      $self->assert_transfer_ok($resp_code, $resp_msg);
5140
5141      $client->quit();
5142    };
5143
5144    if ($@) {
5145      $ex = $@;
5146    }
5147
5148    $wfh->print("done\n");
5149    $wfh->flush();
5150
5151  } else {
5152    eval { server_wait($setup->{config_file}, $rfh) };
5153    if ($@) {
5154      warn($@);
5155      exit 1;
5156    }
5157
5158    exit 0;
5159  }
5160
5161  # Stop server
5162  server_stop($setup->{pid_file});
5163  $self->assert_child_ok($pid);
5164
5165  eval {
5166    my $query = "SELECT user, ip_addr, response_ms FROM ftpsessions WHERE user = \'$setup->{user}\'";
5167    $cmd = "sqlite3 $db_file \"$query\"";
5168
5169    if ($ENV{TEST_VERBOSE}) {
5170      print STDERR "Executing sqlite3: $cmd\n";
5171    }
5172
5173    my $res = join('', `$cmd`);
5174    chomp($res);
5175
5176    my ($login, $ip_addr, $response_ms) = split(/\|/, $res);
5177
5178    if ($ENV{TEST_VERBOSE}) {
5179      print STDERR "# login = '$login', ip_addr = '$ip_addr', response_ms = $response_ms\n";
5180    }
5181
5182    my $expected;
5183
5184    $expected = $setup->{user};
5185    $self->assert($expected eq $login,
5186      test_msg("Expected '$expected', got '$login'"));
5187
5188    $expected = '127.0.0.1';
5189    $self->assert($expected eq $ip_addr,
5190      test_msg("Expected '$expected', got '$ip_addr'"));
5191
5192    $self->assert($response_ms > 0,
5193      test_msg("Expected > 0, got $response_ms"));
5194  };
5195  if ($@) {
5196    $ex = $@;
5197  }
5198
5199  test_cleanup($setup->{log_file}, $ex);
5200}
5201
5202sub sql_sqllog_exit {
5203  my $self = shift;
5204  my $tmpdir = $self->{tmpdir};
5205
5206  my $config_file = "$tmpdir/sqlite.conf";
5207  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
5208  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
5209
5210  my $log_file = test_get_logfile();
5211
5212  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
5213  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
5214
5215  my $user = 'proftpd';
5216  my $passwd = 'test';
5217  my $group = 'ftpd';
5218  my $home_dir = File::Spec->rel2abs($tmpdir);
5219  my $uid = 500;
5220  my $gid = 500;
5221
5222  # Make sure that, if we're running as root, that the home directory has
5223  # permissions/privs set for the account we create
5224  if ($< == 0) {
5225    unless (chmod(0755, $home_dir)) {
5226      die("Can't set perms on $home_dir to 0755: $!");
5227    }
5228
5229    unless (chown($uid, $gid, $home_dir)) {
5230      die("Can't set owner of $home_dir to $uid/$gid: $!");
5231    }
5232  }
5233
5234  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
5235    '/bin/bash');
5236  auth_group_write($auth_group_file, $group, $gid, $user);
5237
5238  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5239
5240  # Build up sqlite3 command to create users, groups tables and populate them
5241  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5242
5243  if (open(my $fh, "> $db_script")) {
5244    print $fh <<EOS;
5245CREATE TABLE ftpsessions (
5246  user TEXT,
5247  ip_addr TEXT
5248);
5249EOS
5250
5251    unless (close($fh)) {
5252      die("Can't write $db_script: $!");
5253    }
5254
5255  } else {
5256    die("Can't open $db_script: $!");
5257  }
5258
5259  my $cmd = "sqlite3 $db_file < $db_script";
5260  build_db($cmd, $db_script);
5261
5262  # Make sure that, if we're running as root, the database file has
5263  # the permissions/privs set for use by proftpd
5264  if ($< == 0) {
5265    unless (chmod(0666, $db_file)) {
5266      die("Can't set perms on $db_file to 0666: $!");
5267    }
5268  }
5269
5270  my $config = {
5271    PidFile => $pid_file,
5272    ScoreboardFile => $scoreboard_file,
5273    SystemLog => $log_file,
5274
5275    AuthUserFile => $auth_user_file,
5276    AuthGroupFile => $auth_group_file,
5277
5278    IfModules => {
5279      'mod_delay.c' => {
5280        DelayEngine => 'off',
5281      },
5282
5283      'mod_sql.c' => {
5284        SQLEngine => 'log',
5285        SQLBackend => 'sqlite3',
5286        SQLConnectInfo => $db_file,
5287        SQLLogFile => $log_file,
5288        SQLNamedQuery => 'logout FREEFORM "INSERT INTO ftpsessions (user, ip_addr) VALUES (\'%u\', \'%L\')"',
5289        SQLLog => 'EXIT logout',
5290      },
5291    },
5292  };
5293
5294  my ($port, $config_user, $config_group) = config_write($config_file, $config);
5295
5296  # Open pipes, for use between the parent and child processes.  Specifically,
5297  # the child will indicate when it's done with its test by writing a message
5298  # to the parent.
5299  my ($rfh, $wfh);
5300  unless (pipe($rfh, $wfh)) {
5301    die("Can't open pipe: $!");
5302  }
5303
5304  my $ex;
5305
5306  # Fork child
5307  $self->handle_sigchld();
5308  defined(my $pid = fork()) or die("Can't fork: $!");
5309  if ($pid) {
5310    eval {
5311      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
5312
5313      $client->login($user, $passwd);
5314      $client->quit();
5315    };
5316
5317    if ($@) {
5318      $ex = $@;
5319    }
5320
5321    $wfh->print("done\n");
5322    $wfh->flush();
5323
5324  } else {
5325    eval { server_wait($config_file, $rfh) };
5326    if ($@) {
5327      warn($@);
5328      exit 1;
5329    }
5330
5331    exit 0;
5332  }
5333
5334  # Stop server
5335  server_stop($pid_file);
5336
5337  $self->assert_child_ok($pid);
5338
5339  if ($ex) {
5340    test_append_logfile($log_file, $ex);
5341    unlink($log_file);
5342
5343    die($ex);
5344  }
5345
5346  my $query = "SELECT user, ip_addr FROM ftpsessions WHERE user = \'$user\'";
5347  $cmd = "sqlite3 $db_file \"$query\"";
5348
5349  if ($ENV{TEST_VERBOSE}) {
5350    print STDERR "Executing sqlite3: $cmd\n";
5351  }
5352
5353  my $res = join('', `$cmd`);
5354  chomp($res);
5355
5356  my ($login, $ip_addr) = split(/\|/, $res);
5357
5358  my $expected;
5359
5360  $expected = $user;
5361  $self->assert($expected eq $login,
5362    test_msg("Expected '$expected', got '$login'"));
5363
5364  $expected = '127.0.0.1';
5365  $self->assert($expected eq $ip_addr,
5366    test_msg("Expected '$expected', got '$ip_addr'"));
5367
5368  unlink($log_file);
5369}
5370
5371sub sql_sqllog_exit_var_remote_port_bug4296 {
5372  my $self = shift;
5373  my $tmpdir = $self->{tmpdir};
5374  my $setup = test_setup($tmpdir, 'sqlite');
5375
5376  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5377
5378  # Build up sqlite3 command to create users, groups tables and populate them
5379  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5380
5381  if (open(my $fh, "> $db_script")) {
5382    print $fh <<EOS;
5383CREATE TABLE ftpsessions (
5384  user TEXT,
5385  ip_addr TEXT,
5386  port NUMBER
5387);
5388EOS
5389
5390    unless (close($fh)) {
5391      die("Can't write $db_script: $!");
5392    }
5393
5394  } else {
5395    die("Can't open $db_script: $!");
5396  }
5397
5398  my $cmd = "sqlite3 $db_file < $db_script";
5399  build_db($cmd, $db_script);
5400
5401  # Make sure that, if we're running as root, the database file has
5402  # the permissions/privs set for use by proftpd
5403  if ($< == 0) {
5404    unless (chmod(0666, $db_file)) {
5405      die("Can't set perms on $db_file to 0666: $!");
5406    }
5407  }
5408
5409  my $config = {
5410    PidFile => $setup->{pid_file},
5411    ScoreboardFile => $setup->{scoreboard_file},
5412    SystemLog => $setup->{log_file},
5413    TraceLog => $setup->{log_file},
5414    Trace => 'jot:20 sql:20',
5415
5416    AuthUserFile => $setup->{auth_user_file},
5417    AuthGroupFile => $setup->{auth_group_file},
5418
5419    IfModules => {
5420      'mod_delay.c' => {
5421        DelayEngine => 'off',
5422      },
5423
5424      'mod_sql.c' => {
5425        SQLEngine => 'log',
5426        SQLBackend => 'sqlite3',
5427        SQLConnectInfo => $db_file,
5428        SQLLogFile => $setup->{log_file},
5429        SQLNamedQuery => 'logout FREEFORM "INSERT INTO ftpsessions (user, ip_addr, port) VALUES (\'%u\', \'%L\', %{remote-port})"',
5430        SQLLog => 'EXIT logout',
5431      },
5432    },
5433  };
5434
5435  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
5436    $config);
5437
5438  # Open pipes, for use between the parent and child processes.  Specifically,
5439  # the child will indicate when it's done with its test by writing a message
5440  # to the parent.
5441  my ($rfh, $wfh);
5442  unless (pipe($rfh, $wfh)) {
5443    die("Can't open pipe: $!");
5444  }
5445
5446  my $ex;
5447
5448  # Fork child
5449  $self->handle_sigchld();
5450  defined(my $pid = fork()) or die("Can't fork: $!");
5451  if ($pid) {
5452    eval {
5453      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
5454      $client->login($setup->{user}, $setup->{passwd});
5455      $client->quit();
5456    };
5457    if ($@) {
5458      $ex = $@;
5459    }
5460
5461    $wfh->print("done\n");
5462    $wfh->flush();
5463
5464  } else {
5465    eval { server_wait($setup->{config_file}, $rfh) };
5466    if ($@) {
5467      warn($@);
5468      exit 1;
5469    }
5470
5471    exit 0;
5472  }
5473
5474  # Stop server
5475  server_stop($setup->{pid_file});
5476  $self->assert_child_ok($pid);
5477
5478  if ($ex) {
5479    test_cleanup($setup->{log_file}, $ex);
5480  }
5481
5482  eval {
5483    my $query = "SELECT * FROM ftpsessions where user = '$setup->{user}'";
5484    $cmd = "sqlite3 $db_file \"$query\"";
5485
5486    if ($ENV{TEST_VERBOSE}) {
5487      print STDERR "Executing sqlite3: $cmd\n";
5488    }
5489
5490    my $output = `$cmd`;
5491    my $res = join('', `$cmd`);
5492    chomp($res);
5493
5494    my ($login, $ip_addr, $remote_port) = split(/\|/, $res);
5495
5496    my $expected = $setup->{user};
5497    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
5498
5499    $expected = '127.0.0.1';
5500    $self->assert($expected eq $ip_addr,
5501      "Expected '$expected', got '$ip_addr'");
5502  };
5503  if ($@) {
5504    $ex = $@;
5505  }
5506
5507  test_cleanup($setup->{log_file}, $ex);
5508}
5509
5510sub get_locations {
5511  my $db_file = shift;
5512  my $where = shift;
5513
5514  my $sql = "SELECT user, ip_addr, dir FROM ftpsessions";
5515  if ($where) {
5516    $sql .= " WHERE $where";
5517  }
5518
5519  my $cmd = "sqlite3 $db_file \"$sql\"";
5520
5521  if ($ENV{TEST_VERBOSE}) {
5522    print STDERR "Executing sqlite3: $cmd\n";
5523  }
5524
5525  my $res = join('', `$cmd`);
5526  chomp($res);
5527
5528  if ($ENV{TEST_VERBOSE}) {
5529    print STDERR "Results: $res\n";
5530  }
5531
5532  # The default sqlite3 delimiter is '|'
5533  return split(/\|/, $res);
5534}
5535
5536sub sql_sqllog_var_d_bug3395 {
5537  my $self = shift;
5538  my $tmpdir = $self->{tmpdir};
5539  my $setup = test_setup($tmpdir, 'sqlite');
5540
5541  my $sub_dir = File::Spec->rel2abs("$tmpdir/foo");
5542  mkpath($sub_dir);
5543
5544  # Make sure that, if we're running as root, that the home directory has
5545  # permissions/privs set for the account we create
5546  if ($< == 0) {
5547    unless (chmod(0755, $sub_dir)) {
5548      die("Can't set perms on $sub_dir to 0755: $!");
5549    }
5550
5551    unless (chown($setup->{uid}, $setup->{gid}, $sub_dir)) {
5552      die("Can't set owner of $sub_dir to $setup->{uid}/$setup->{gid}: $!");
5553    }
5554  }
5555
5556  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5557
5558  # Build up sqlite3 command to create users, groups tables and populate them
5559  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5560
5561  if (open(my $fh, "> $db_script")) {
5562    print $fh <<EOS;
5563CREATE TABLE ftpsessions (
5564  user TEXT,
5565  ip_addr TEXT,
5566  dir TEXT
5567);
5568EOS
5569
5570    unless (close($fh)) {
5571      die("Can't write $db_script: $!");
5572    }
5573
5574  } else {
5575    die("Can't open $db_script: $!");
5576  }
5577
5578  my $cmd = "sqlite3 $db_file < $db_script";
5579  build_db($cmd, $db_script);
5580
5581  # Make sure that, if we're running as root, the database file has
5582  # the permissions/privs set for use by proftpd
5583  if ($< == 0) {
5584    unless (chmod(0666, $db_file)) {
5585      die("Can't set perms on $db_file to 0666: $!");
5586    }
5587  }
5588
5589  my $config = {
5590    PidFile => $setup->{pid_file},
5591    ScoreboardFile => $setup->{scoreboard_file},
5592    SystemLog => $setup->{log_file},
5593    TraceLog => $setup->{log_file},
5594    Trace => 'jot:20 sql:20',
5595
5596    AuthUserFile => $setup->{auth_user_file},
5597    AuthGroupFile => $setup->{auth_group_file},
5598
5599    IfModules => {
5600      'mod_delay.c' => {
5601        DelayEngine => 'off',
5602      },
5603
5604      'mod_sql.c' => {
5605        SQLEngine => 'log',
5606        SQLBackend => 'sqlite3',
5607        SQLConnectInfo => $db_file,
5608        SQLLogFile => $setup->{log_file},
5609        SQLNamedQuery => 'location FREEFORM "INSERT INTO ftpsessions (user, ip_addr, dir) VALUES (\'%u\', \'%L\', \'%d\')"',
5610        SQLLog => 'EXIT location',
5611      },
5612    },
5613  };
5614
5615  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
5616    $config);
5617
5618  # Open pipes, for use between the parent and child processes.  Specifically,
5619  # the child will indicate when it's done with its test by writing a message
5620  # to the parent.
5621  my ($rfh, $wfh);
5622  unless (pipe($rfh, $wfh)) {
5623    die("Can't open pipe: $!");
5624  }
5625
5626  my $ex;
5627
5628  # Fork child
5629  $self->handle_sigchld();
5630  defined(my $pid = fork()) or die("Can't fork: $!");
5631  if ($pid) {
5632    eval {
5633      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
5634      $client->login($setup->{user}, $setup->{passwd});
5635      $client->cwd('foo');
5636      $client->quit();
5637    };
5638    if ($@) {
5639      $ex = $@;
5640    }
5641
5642    $wfh->print("done\n");
5643    $wfh->flush();
5644
5645  } else {
5646    eval { server_wait($setup->{config_file}, $rfh) };
5647    if ($@) {
5648      warn($@);
5649      exit 1;
5650    }
5651
5652    exit 0;
5653  }
5654
5655  # Stop server
5656  server_stop($setup->{pid_file});
5657  $self->assert_child_ok($pid);
5658
5659  if ($ex) {
5660    test_cleanup($setup->{log_file}, $ex);
5661  }
5662
5663  eval {
5664    my ($login, $ip_addr, $dir) = get_locations($db_file, "user = \'$setup->{user}\'");
5665
5666    my $expected = $setup->{user};
5667    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
5668
5669    $expected = '127.0.0.1';
5670    $self->assert($expected eq $ip_addr,
5671      "Expected '$expected', got '$ip_addr'");
5672
5673    if ($^O eq 'darwin') {
5674      # MacOSX-specific hack
5675      $sub_dir = '/private' . $sub_dir;
5676    }
5677
5678    $expected = $sub_dir;
5679    $self->assert($expected eq $dir, "Expected '$expected', got '$dir'");
5680  };
5681  if ($@) {
5682    $ex = $@;
5683  }
5684
5685  test_cleanup($setup->{log_file}, $ex);
5686}
5687
5688sub sql_sqllog_var_d_chroot_bug3395 {
5689  my $self = shift;
5690  my $tmpdir = $self->{tmpdir};
5691
5692  my $config_file = "$tmpdir/sqlite.conf";
5693  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
5694  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
5695
5696  my $log_file = test_get_logfile();
5697
5698  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
5699  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
5700
5701  my $user = 'proftpd';
5702  my $passwd = 'test';
5703  my $group = 'ftpd';
5704  my $home_dir = File::Spec->rel2abs($tmpdir);
5705  my $uid = 500;
5706  my $gid = 500;
5707
5708  my $sub_dir = File::Spec->rel2abs("$tmpdir/foo");
5709  mkpath($sub_dir);
5710
5711  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
5712    '/bin/bash');
5713  auth_group_write($auth_group_file, $group, $gid, $user);
5714
5715  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5716
5717  # Build up sqlite3 command to create users, groups tables and populate them
5718  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5719
5720  if (open(my $fh, "> $db_script")) {
5721    print $fh <<EOS;
5722CREATE TABLE ftpsessions (
5723  user TEXT,
5724  ip_addr TEXT,
5725  dir TEXT
5726);
5727EOS
5728
5729    unless (close($fh)) {
5730      die("Can't write $db_script: $!");
5731    }
5732
5733  } else {
5734    die("Can't open $db_script: $!");
5735  }
5736
5737  my $cmd = "sqlite3 $db_file < $db_script";
5738  build_db($cmd, $db_script);
5739
5740  # Make sure that, if we're running as root, that the home directory has
5741  # permissions/privs set for the account we create
5742  if ($< == 0) {
5743    unless (chmod(0755, $home_dir, $sub_dir)) {
5744      die("Can't set perms on $home_dir to 0755: $!");
5745    }
5746
5747    unless (chown($uid, $gid, $home_dir, $sub_dir)) {
5748      die("Can't set owner of $home_dir to $uid/$gid: $!");
5749    }
5750
5751    unless (chmod(0666, $db_file)) {
5752      die("Can't set perms on $db_file to 0666: $!");
5753    }
5754  }
5755
5756  my $config = {
5757    PidFile => $pid_file,
5758    ScoreboardFile => $scoreboard_file,
5759    SystemLog => $log_file,
5760
5761    AuthUserFile => $auth_user_file,
5762    AuthGroupFile => $auth_group_file,
5763
5764    IfModules => {
5765      'mod_delay.c' => {
5766        DelayEngine => 'off',
5767      },
5768
5769      'mod_sql.c' => {
5770        SQLEngine => 'log',
5771        SQLBackend => 'sqlite3',
5772        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
5773        SQLLogFile => $log_file,
5774        SQLNamedQuery => 'location FREEFORM "INSERT INTO ftpsessions (user, ip_addr, dir) VALUES (\'%u\', \'%L\', \'%d\')"',
5775        SQLLog => 'EXIT location',
5776      },
5777    },
5778  };
5779
5780  my ($port, $config_user, $config_group) = config_write($config_file, $config);
5781
5782  # Open pipes, for use between the parent and child processes.  Specifically,
5783  # the child will indicate when it's done with its test by writing a message
5784  # to the parent.
5785  my ($rfh, $wfh);
5786  unless (pipe($rfh, $wfh)) {
5787    die("Can't open pipe: $!");
5788  }
5789
5790  my $ex;
5791
5792  # Fork child
5793  $self->handle_sigchld();
5794  defined(my $pid = fork()) or die("Can't fork: $!");
5795  if ($pid) {
5796    eval {
5797      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
5798      $client->login($user, $passwd);
5799      $client->cwd('foo');
5800      $client->quit();
5801    };
5802
5803    if ($@) {
5804      $ex = $@;
5805    }
5806
5807    $wfh->print("done\n");
5808    $wfh->flush();
5809
5810  } else {
5811    eval { server_wait($config_file, $rfh) };
5812    if ($@) {
5813      warn($@);
5814      exit 1;
5815    }
5816
5817    exit 0;
5818  }
5819
5820  # Stop server
5821  server_stop($pid_file);
5822
5823  $self->assert_child_ok($pid);
5824
5825  if ($ex) {
5826    test_append_logfile($log_file, $ex);
5827    unlink($log_file);
5828
5829    die($ex);
5830  }
5831
5832  my ($login, $ip_addr, $dir) = get_locations($db_file, "user = \'$user\'");
5833
5834  my $expected;
5835
5836  $expected = $user;
5837  $self->assert($expected eq $login,
5838    test_msg("Expected '$expected', got '$login'"));
5839
5840  $expected = '127.0.0.1';
5841  $self->assert($expected eq $ip_addr,
5842    test_msg("Expected '$expected', got '$ip_addr'"));
5843
5844  $expected = $sub_dir;
5845  $self->assert($expected eq $dir,
5846    test_msg("Expected '$expected', got '$dir'"));
5847
5848  unlink($log_file);
5849}
5850
5851sub get_ids {
5852  my $db_file = shift;
5853  my $where = shift;
5854
5855  my $sql = "SELECT user, ip_addr, uid, gid FROM ftpsessions";
5856  if ($where) {
5857    $sql .= " WHERE $where";
5858  }
5859
5860  my $cmd = "sqlite3 $db_file \"$sql\"";
5861
5862  if ($ENV{TEST_VERBOSE}) {
5863    print STDERR "Executing sqlite3: $cmd\n";
5864  }
5865
5866  my $res = join('', `$cmd`);
5867  chomp($res);
5868
5869  # The default sqlite3 delimiter is '|'
5870  return split(/\|/, $res);
5871}
5872
5873sub sql_sqllog_var_uid_gid_bug3390 {
5874  my $self = shift;
5875  my $tmpdir = $self->{tmpdir};
5876
5877  my $config_file = "$tmpdir/sqlite.conf";
5878  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
5879  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
5880
5881  my $log_file = test_get_logfile();
5882
5883  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
5884  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
5885
5886  my $user = 'proftpd';
5887  my $passwd = 'test';
5888  my $group = 'ftpd';
5889  my $home_dir = File::Spec->rel2abs($tmpdir);
5890  my $uid = 500;
5891  my $gid = 500;
5892
5893  my $sub_dir = File::Spec->rel2abs("$tmpdir/foo");
5894  mkpath($sub_dir);
5895
5896  # Make sure that, if we're running as root, that the home directory has
5897  # permissions/privs set for the account we create
5898  if ($< == 0) {
5899    unless (chmod(0755, $home_dir, $sub_dir)) {
5900      die("Can't set perms on $home_dir to 0755: $!");
5901    }
5902
5903    unless (chown($uid, $gid, $home_dir, $sub_dir)) {
5904      die("Can't set owner of $home_dir to $uid/$gid: $!");
5905    }
5906  }
5907
5908  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
5909    '/bin/bash');
5910  auth_group_write($auth_group_file, $group, $gid, $user);
5911
5912  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
5913
5914  # Build up sqlite3 command to create users, groups tables and populate them
5915  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
5916
5917  if (open(my $fh, "> $db_script")) {
5918    print $fh <<EOS;
5919CREATE TABLE ftpsessions (
5920  user TEXT,
5921  ip_addr TEXT,
5922  uid NUMBER,
5923  gid NUMBER
5924);
5925EOS
5926
5927    unless (close($fh)) {
5928      die("Can't write $db_script: $!");
5929    }
5930
5931  } else {
5932    die("Can't open $db_script: $!");
5933  }
5934
5935  my $cmd = "sqlite3 $db_file < $db_script";
5936  build_db($cmd, $db_script);
5937
5938  # Make sure that, if we're running as root, the database file has
5939  # the permissions/privs set for use by proftpd
5940  if ($< == 0) {
5941    unless (chmod(0666, $db_file)) {
5942      die("Can't set perms on $db_file to 0666: $!");
5943    }
5944  }
5945
5946  my $config = {
5947    PidFile => $pid_file,
5948    ScoreboardFile => $scoreboard_file,
5949    SystemLog => $log_file,
5950
5951    AuthUserFile => $auth_user_file,
5952    AuthGroupFile => $auth_group_file,
5953
5954    IfModules => {
5955      'mod_delay.c' => {
5956        DelayEngine => 'off',
5957      },
5958
5959      'mod_sql.c' => {
5960        SQLEngine => 'log',
5961        SQLBackend => 'sqlite3',
5962        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
5963        SQLLogFile => $log_file,
5964        SQLNamedQuery => 'location FREEFORM "INSERT INTO ftpsessions (user, ip_addr, uid, gid) VALUES (\'%u\', \'%L\', %{uid}, %{gid})"',
5965        SQLLog => 'PASS location',
5966      },
5967    },
5968  };
5969
5970  my ($port, $config_user, $config_group) = config_write($config_file, $config);
5971
5972  # Open pipes, for use between the parent and child processes.  Specifically,
5973  # the child will indicate when it's done with its test by writing a message
5974  # to the parent.
5975  my ($rfh, $wfh);
5976  unless (pipe($rfh, $wfh)) {
5977    die("Can't open pipe: $!");
5978  }
5979
5980  my $ex;
5981
5982  # Fork child
5983  $self->handle_sigchld();
5984  defined(my $pid = fork()) or die("Can't fork: $!");
5985  if ($pid) {
5986    eval {
5987      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
5988      $client->login($user, $passwd);
5989      $client->cwd('foo');
5990      $client->quit();
5991    };
5992
5993    if ($@) {
5994      $ex = $@;
5995    }
5996
5997    $wfh->print("done\n");
5998    $wfh->flush();
5999
6000  } else {
6001    eval { server_wait($config_file, $rfh) };
6002    if ($@) {
6003      warn($@);
6004      exit 1;
6005    }
6006
6007    exit 0;
6008  }
6009
6010  # Stop server
6011  server_stop($pid_file);
6012
6013  $self->assert_child_ok($pid);
6014
6015  if ($ex) {
6016    test_append_logfile($log_file, $ex);
6017    unlink($log_file);
6018
6019    die($ex);
6020  }
6021
6022  my ($login, $ip_addr, $sql_uid, $sql_gid) = get_ids($db_file, "user = \'$user\'");
6023
6024  my $expected;
6025
6026  $expected = $user;
6027  $self->assert($expected eq $login,
6028    test_msg("Expected '$expected', got '$login'"));
6029
6030  $expected = '127.0.0.1';
6031  $self->assert($expected eq $ip_addr,
6032    test_msg("Expected '$expected', got '$ip_addr'"));
6033
6034  $expected = $uid;
6035  $self->assert($expected == $sql_uid,
6036    test_msg("Expected $expected, got $sql_uid"));
6037
6038  $expected = $gid;
6039  $self->assert($expected == $sql_gid,
6040    test_msg("Expected $expected, got $sql_gid"));
6041
6042  unlink($log_file);
6043}
6044
6045sub sql_sqlite_auth_type_backend_bug3511 {
6046  my $self = shift;
6047  my $tmpdir = $self->{tmpdir};
6048  my $setup = test_setup($tmpdir, 'sqlite');
6049
6050  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6051
6052  my $config = {
6053    PidFile => $setup->{pid_file},
6054    ScoreboardFile => $setup->{scoreboard_file},
6055    SystemLog => $setup->{log_file},
6056
6057    IfModules => {
6058      'mod_delay.c' => {
6059        DelayEngine => 'off',
6060      },
6061
6062      'mod_sql.c' => {
6063        SQLAuthTypes => 'backend',
6064        SQLBackend => 'sqlite3',
6065        SQLConnectInfo => $db_file,
6066        SQLLogFile => $setup->{log_file},
6067      },
6068    },
6069  };
6070
6071  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
6072    $config);
6073
6074  # As of Bug#4281, the mod_sql module no longer supports the "Backend"
6075  # SQLAuthType itself.  Which means that attempting to use it here, with
6076  # mod_sql_sqlite, will cause the server to not start up at all.
6077
6078  my $ex;
6079
6080  # This should silently fail.
6081  server_start($setup->{config_file});
6082
6083  # This is where we detect the actual problem.
6084  eval { server_stop($setup->{pid_file}) };
6085  unless ($@) {
6086    $ex = "Server start with bad config unexpectedly";
6087  }
6088
6089  test_cleanup($setup->{log_file}, $ex);
6090}
6091
6092sub sql_sqllog_pass_ok_var_s_bug3528 {
6093  my $self = shift;
6094  my $tmpdir = $self->{tmpdir};
6095
6096  my $config_file = "$tmpdir/sqlite.conf";
6097  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
6098  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
6099
6100  my $log_file = test_get_logfile();
6101
6102  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
6103  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
6104
6105  my $user = 'proftpd';
6106  my $passwd = 'test';
6107  my $group = 'ftpd';
6108  my $home_dir = File::Spec->rel2abs($tmpdir);
6109  my $uid = 500;
6110  my $gid = 500;
6111
6112  # Make sure that, if we're running as root, that the home directory has
6113  # permissions/privs set for the account we create
6114  if ($< == 0) {
6115    unless (chmod(0755, $home_dir)) {
6116      die("Can't set perms on $home_dir to 0755: $!");
6117    }
6118
6119    unless (chown($uid, $gid, $home_dir)) {
6120      die("Can't set owner of $home_dir to $uid/$gid: $!");
6121    }
6122  }
6123
6124  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
6125    '/bin/bash');
6126  auth_group_write($auth_group_file, $group, $gid, $user);
6127
6128  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6129
6130  # Build up sqlite3 command to create users, groups tables and populate them
6131  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6132
6133  if (open(my $fh, "> $db_script")) {
6134    print $fh <<EOS;
6135CREATE TABLE ftpsessions (
6136  user TEXT,
6137  resp_code TEXT,
6138  resp_mesg TEXT
6139);
6140EOS
6141
6142    unless (close($fh)) {
6143      die("Can't write $db_script: $!");
6144    }
6145
6146  } else {
6147    die("Can't open $db_script: $!");
6148  }
6149
6150  my $cmd = "sqlite3 $db_file < $db_script";
6151  build_db($cmd, $db_script);
6152
6153  # Make sure that, if we're running as root, the database file has
6154  # the permissions/privs set for use by proftpd
6155  if ($< == 0) {
6156    unless (chmod(0666, $db_file)) {
6157      die("Can't set perms on $db_file to 0666: $!");
6158    }
6159  }
6160
6161  my $config = {
6162    PidFile => $pid_file,
6163    ScoreboardFile => $scoreboard_file,
6164    SystemLog => $log_file,
6165
6166    AuthUserFile => $auth_user_file,
6167    AuthGroupFile => $auth_group_file,
6168
6169    IfModules => {
6170      'mod_delay.c' => {
6171        DelayEngine => 'off',
6172      },
6173
6174      'mod_sql.c' => {
6175        SQLEngine => 'log',
6176        SQLBackend => 'sqlite3',
6177        SQLConnectInfo => $db_file,
6178        SQLLogFile => $log_file,
6179        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, resp_code, resp_mesg) VALUES (\'%u\', \'%s\', \'%S\')"',
6180        SQLLog => 'PASS,ERR_PASS info',
6181      },
6182    },
6183  };
6184
6185  my ($port, $config_user, $config_group) = config_write($config_file, $config);
6186
6187  # Open pipes, for use between the parent and child processes.  Specifically,
6188  # the child will indicate when it's done with its test by writing a message
6189  # to the parent.
6190  my ($rfh, $wfh);
6191  unless (pipe($rfh, $wfh)) {
6192    die("Can't open pipe: $!");
6193  }
6194
6195  my $ex;
6196
6197  # Fork child
6198  $self->handle_sigchld();
6199  defined(my $pid = fork()) or die("Can't fork: $!");
6200  if ($pid) {
6201    eval {
6202      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
6203      $client->login($user, $passwd);
6204      $client->quit();
6205    };
6206
6207    if ($@) {
6208      $ex = $@;
6209    }
6210
6211    $wfh->print("done\n");
6212    $wfh->flush();
6213
6214  } else {
6215    eval { server_wait($config_file, $rfh) };
6216    if ($@) {
6217      warn($@);
6218      exit 1;
6219    }
6220
6221    exit 0;
6222  }
6223
6224  # Stop server
6225  server_stop($pid_file);
6226
6227  $self->assert_child_ok($pid);
6228
6229  if ($ex) {
6230    test_append_logfile($log_file, $ex);
6231    unlink($log_file);
6232
6233    die($ex);
6234  }
6235
6236  my ($login, $resp_code, $resp_mesg) = get_resp_mesgs($db_file,
6237    undef, "user, resp_code, resp_mesg");
6238
6239  my $expected;
6240
6241  $expected = 230;
6242  $self->assert($expected eq $resp_code,
6243    test_msg("Expected '$expected', got '$resp_code'"));
6244
6245  $expected = "User $user logged in";
6246  $self->assert($expected eq $resp_mesg,
6247    test_msg("Expected '$expected', got '$resp_mesg'"));
6248
6249  unlink($log_file);
6250}
6251
6252sub sql_sqllog_pass_failed_var_s_bug3528 {
6253  my $self = shift;
6254  my $tmpdir = $self->{tmpdir};
6255
6256  my $config_file = "$tmpdir/sqlite.conf";
6257  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
6258  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
6259
6260  my $log_file = test_get_logfile();
6261
6262  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
6263  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
6264
6265  my $user = 'proftpd';
6266  my $passwd = 'test';
6267  my $group = 'ftpd';
6268  my $home_dir = File::Spec->rel2abs($tmpdir);
6269  my $uid = 500;
6270  my $gid = 500;
6271
6272  # Make sure that, if we're running as root, that the home directory has
6273  # permissions/privs set for the account we create
6274  if ($< == 0) {
6275    unless (chmod(0755, $home_dir)) {
6276      die("Can't set perms on $home_dir to 0755: $!");
6277    }
6278
6279    unless (chown($uid, $gid, $home_dir)) {
6280      die("Can't set owner of $home_dir to $uid/$gid: $!");
6281    }
6282  }
6283
6284  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
6285    '/bin/bash');
6286  auth_group_write($auth_group_file, $group, $gid, $user);
6287
6288  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6289
6290  # Build up sqlite3 command to create users, groups tables and populate them
6291  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6292
6293  if (open(my $fh, "> $db_script")) {
6294    print $fh <<EOS;
6295CREATE TABLE ftpsessions (
6296  user TEXT,
6297  resp_code TEXT,
6298  resp_mesg TEXT
6299);
6300EOS
6301
6302    unless (close($fh)) {
6303      die("Can't write $db_script: $!");
6304    }
6305
6306  } else {
6307    die("Can't open $db_script: $!");
6308  }
6309
6310  my $cmd = "sqlite3 $db_file < $db_script";
6311  build_db($cmd, $db_script);
6312
6313  # Make sure that, if we're running as root, the database file has
6314  # the permissions/privs set for use by proftpd
6315  if ($< == 0) {
6316    unless (chmod(0666, $db_file)) {
6317      die("Can't set perms on $db_file to 0666: $!");
6318    }
6319  }
6320
6321  my $config = {
6322    PidFile => $pid_file,
6323    ScoreboardFile => $scoreboard_file,
6324    SystemLog => $log_file,
6325
6326    AuthUserFile => $auth_user_file,
6327    AuthGroupFile => $auth_group_file,
6328
6329    IfModules => {
6330      'mod_delay.c' => {
6331        DelayEngine => 'off',
6332      },
6333
6334      'mod_sql.c' => {
6335        SQLEngine => 'log',
6336        SQLBackend => 'sqlite3',
6337        SQLConnectInfo => $db_file,
6338        SQLLogFile => $log_file,
6339        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, resp_code, resp_mesg) VALUES (\'%u\', \'%s\', \'%S\')"',
6340        SQLLog => 'PASS,ERR_PASS info',
6341      },
6342    },
6343  };
6344
6345  my ($port, $config_user, $config_group) = config_write($config_file, $config);
6346
6347  # Open pipes, for use between the parent and child processes.  Specifically,
6348  # the child will indicate when it's done with its test by writing a message
6349  # to the parent.
6350  my ($rfh, $wfh);
6351  unless (pipe($rfh, $wfh)) {
6352    die("Can't open pipe: $!");
6353  }
6354
6355  my $ex;
6356
6357  # Fork child
6358  $self->handle_sigchld();
6359  defined(my $pid = fork()) or die("Can't fork: $!");
6360  if ($pid) {
6361    eval {
6362      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
6363      eval { $client->login($user, 'foobar') };
6364      unless ($@) {
6365        die("Login succeeded unexpectedly");
6366      }
6367
6368      $client->quit();
6369    };
6370
6371    if ($@) {
6372      $ex = $@;
6373    }
6374
6375    $wfh->print("done\n");
6376    $wfh->flush();
6377
6378  } else {
6379    eval { server_wait($config_file, $rfh) };
6380    if ($@) {
6381      warn($@);
6382      exit 1;
6383    }
6384
6385    exit 0;
6386  }
6387
6388  # Stop server
6389  server_stop($pid_file);
6390
6391  $self->assert_child_ok($pid);
6392
6393  if ($ex) {
6394    test_append_logfile($log_file, $ex);
6395    unlink($log_file);
6396
6397    die($ex);
6398  }
6399
6400  my ($login, $resp_code, $resp_mesg) = get_resp_mesgs($db_file,
6401    undef, "user, resp_code, resp_mesg");
6402
6403  my $expected;
6404
6405  $expected = 530;
6406  $self->assert($expected eq $resp_code,
6407    test_msg("Expected '$expected', got '$resp_code'"));
6408
6409  $expected = "Login incorrect.";
6410  $self->assert($expected eq $resp_mesg,
6411    test_msg("Expected '$expected', got '$resp_mesg'"));
6412
6413  unlink($log_file);
6414}
6415
6416sub sql_sqllog_pass_failed_bug4301 {
6417  my $self = shift;
6418  my $tmpdir = $self->{tmpdir};
6419
6420  my $config_file = "$tmpdir/sqlite.conf";
6421  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
6422  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
6423
6424  my $log_file = test_get_logfile();
6425
6426  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
6427  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
6428
6429  my $user = 'proftpd';
6430  my $passwd = 'test';
6431  my $group = 'ftpd';
6432  my $home_dir = File::Spec->rel2abs($tmpdir);
6433  my $uid = 500;
6434  my $gid = 500;
6435
6436  # Make sure that, if we're running as root, that the home directory has
6437  # permissions/privs set for the account we create
6438  if ($< == 0) {
6439    unless (chmod(0755, $home_dir)) {
6440      die("Can't set perms on $home_dir to 0755: $!");
6441    }
6442
6443    unless (chown($uid, $gid, $home_dir)) {
6444      die("Can't set owner of $home_dir to $uid/$gid: $!");
6445    }
6446  }
6447
6448  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
6449    '/bin/bash');
6450  auth_group_write($auth_group_file, $group, $gid, $user);
6451
6452  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6453
6454  # Build up sqlite3 command to create users, groups tables and populate them
6455  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6456
6457  if (open(my $fh, "> $db_script")) {
6458    print $fh <<EOS;
6459CREATE TABLE ftpsessions (
6460  user TEXT,
6461  resp_code TEXT,
6462  resp_mesg TEXT
6463);
6464EOS
6465
6466    unless (close($fh)) {
6467      die("Can't write $db_script: $!");
6468    }
6469
6470  } else {
6471    die("Can't open $db_script: $!");
6472  }
6473
6474  my $cmd = "sqlite3 $db_file < $db_script";
6475  build_db($cmd, $db_script);
6476
6477  # Make sure that, if we're running as root, the database file has
6478  # the permissions/privs set for use by proftpd
6479  if ($< == 0) {
6480    unless (chmod(0666, $db_file)) {
6481      die("Can't set perms on $db_file to 0666: $!");
6482    }
6483  }
6484
6485  my $config = {
6486    PidFile => $pid_file,
6487    ScoreboardFile => $scoreboard_file,
6488    SystemLog => $log_file,
6489
6490    AuthUserFile => $auth_user_file,
6491    AuthGroupFile => $auth_group_file,
6492
6493    IfModules => {
6494      'mod_delay.c' => {
6495        DelayEngine => 'off',
6496      },
6497
6498      'mod_sql.c' => {
6499        SQLEngine => 'log',
6500        SQLBackend => 'sqlite3',
6501        SQLConnectInfo => $db_file,
6502        SQLLogFile => $log_file,
6503        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, resp_code, resp_mesg) VALUES (\'%u\', \'%s\', \'%S\')"',
6504        SQLLog => 'PASS info',
6505      },
6506    },
6507  };
6508
6509  my ($port, $config_user, $config_group) = config_write($config_file, $config);
6510
6511  # Open pipes, for use between the parent and child processes.  Specifically,
6512  # the child will indicate when it's done with its test by writing a message
6513  # to the parent.
6514  my ($rfh, $wfh);
6515  unless (pipe($rfh, $wfh)) {
6516    die("Can't open pipe: $!");
6517  }
6518
6519  my $ex;
6520
6521  # Fork child
6522  $self->handle_sigchld();
6523  defined(my $pid = fork()) or die("Can't fork: $!");
6524  if ($pid) {
6525    eval {
6526      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
6527      $client->user($user);
6528      eval { $client->pass('') };
6529      unless ($@) {
6530        die("PASS succeeded unexpectedly");
6531      }
6532
6533      $client->quit();
6534    };
6535
6536    if ($@) {
6537      $ex = $@;
6538    }
6539
6540    $wfh->print("done\n");
6541    $wfh->flush();
6542
6543  } else {
6544    eval { server_wait($config_file, $rfh) };
6545    if ($@) {
6546      warn($@);
6547      exit 1;
6548    }
6549
6550    exit 0;
6551  }
6552
6553  # Stop server
6554  server_stop($pid_file);
6555  $self->assert_child_ok($pid);
6556
6557  if ($ex) {
6558    test_append_logfile($log_file, $ex);
6559    unlink($log_file);
6560
6561    die($ex);
6562  }
6563
6564  # We want to ensure/assert that a failed PASS command does NOT cause
6565  # the SQLLog directive to be triggered (Bug#4301).
6566  eval {
6567    my ($login, $resp_code, $resp_mesg) = get_resp_mesgs($db_file,
6568      undef, "user, resp_code, resp_mesg");
6569    $self->assert(!defined($resp_code), "Expected undef, got $resp_code");
6570    $self->assert(!defined($resp_mesg), "Expected undef, got '$resp_mesg'");
6571  };
6572  if ($@) {
6573    $ex = $@;
6574  }
6575
6576  if ($ex) {
6577    test_append_logfile($log_file, $ex);
6578    unlink($log_file);
6579
6580    die($ex);
6581  }
6582
6583  unlink($log_file);
6584}
6585
6586sub sql_sqlshowinfo_pass_bug3423 {
6587  my $self = shift;
6588  my $tmpdir = $self->{tmpdir};
6589  my $setup = test_setup($tmpdir, 'sqlite');
6590
6591  my $lastdir = '/path/to/lastdir';
6592
6593  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6594
6595  # Build up sqlite3 command to create users, groups tables and populate them
6596  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6597
6598  if (open(my $fh, "> $db_script")) {
6599    print $fh <<EOS;
6600CREATE TABLE users (
6601  userid TEXT,
6602  passwd TEXT,
6603  uid INTEGER,
6604  gid INTEGER,
6605  homedir TEXT,
6606  shell TEXT,
6607  lastdir TEXT
6608);
6609INSERT INTO users (userid, passwd, uid, gid, homedir, shell, lastdir) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', '$lastdir');
6610
6611CREATE TABLE groups (
6612  groupname TEXT,
6613  gid INTEGER,
6614  members TEXT
6615);
6616INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
6617EOS
6618
6619    unless (close($fh)) {
6620      die("Can't write $db_script: $!");
6621    }
6622
6623  } else {
6624    die("Can't open $db_script: $!");
6625  }
6626
6627  my $cmd = "sqlite3 $db_file < $db_script";
6628  build_db($cmd, $db_script);
6629
6630  # Make sure that, if we're running as root, the database file has
6631  # the permissions/privs set for use by proftpd
6632  if ($< == 0) {
6633    unless (chmod(0666, $db_file)) {
6634      die("Can't set perms on $db_file to 0666: $!");
6635    }
6636  }
6637
6638  my $config = {
6639    PidFile => $setup->{pid_file},
6640    ScoreboardFile => $setup->{scoreboard_file},
6641    SystemLog => $setup->{log_file},
6642    TraceLog => $setup->{log_file},
6643    Trace => 'jot:20 sql:20',
6644
6645    IfModules => {
6646      'mod_delay.c' => {
6647        DelayEngine => 'off',
6648      },
6649
6650      'mod_sql.c' => [
6651        'AuthOrder mod_sql.c',
6652
6653        'SQLAuthenticate users groups',
6654        'SQLAuthTypes plaintext',
6655        'SQLBackend sqlite3',
6656        "SQLConnectInfo $db_file",
6657        "SQLLogFile $setup->{log_file}",
6658        'SQLMinID 100',
6659
6660        'SQLNamedQuery lastdir SELECT "lastdir FROM users WHERE userid = \'%u\'"',
6661
6662        # Configure the equivalent of a multiline DisplayLogin file
6663        # using the SQLShowInfo directive
6664        'SQLShowInfo PASS 230 " "',
6665        'SQLShowInfo PASS 230 "Greetings, %u"',
6666        'SQLShowInfo PASS 230 "Last directory: \"%{lastdir}\""',
6667        'SQLShowInfo PASS 230 "-"',
6668      ],
6669    },
6670  };
6671
6672  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
6673    $config);
6674
6675  # Open pipes, for use between the parent and child processes.  Specifically,
6676  # the child will indicate when it's done with its test by writing a message
6677  # to the parent.
6678  my ($rfh, $wfh);
6679  unless (pipe($rfh, $wfh)) {
6680    die("Can't open pipe: $!");
6681  }
6682
6683  my $ex;
6684
6685  # Fork child
6686  $self->handle_sigchld();
6687  defined(my $pid = fork()) or die("Can't fork: $!");
6688  if ($pid) {
6689    eval {
6690      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
6691      $client->login($setup->{user}, $setup->{passwd});
6692
6693      my $resp_msgs = $client->response_msgs();
6694      my $nmsgs = scalar(@$resp_msgs);
6695
6696      my $expected = 5;
6697      $self->assert($expected == $nmsgs, "Expected $expected, got $nmsgs");
6698
6699      $expected = " ";
6700      $self->assert($expected eq $resp_msgs->[0],
6701        "Expected '$expected', got '$resp_msgs->[0]'");
6702
6703      $expected = " Greetings, $setup->{user}";
6704      $self->assert($expected eq $resp_msgs->[1],
6705        "Expected '$expected', got '$resp_msgs->[1]'");
6706
6707      $expected = " Last directory: \"$lastdir\"";
6708      $self->assert($expected eq $resp_msgs->[2],
6709        "Expected '$expected', got '$resp_msgs->[2]'");
6710
6711      $expected = " -";
6712      $self->assert($expected eq $resp_msgs->[3],
6713        "Expected '$expected', got '$resp_msgs->[3]'");
6714
6715      $expected = "User $setup->{user} logged in";
6716      $self->assert($expected eq $resp_msgs->[4],
6717        "Expected '$expected', got '$resp_msgs->[4]'");
6718
6719      $client->quit();
6720    };
6721    if ($@) {
6722      $ex = $@;
6723    }
6724
6725    $wfh->print("done\n");
6726    $wfh->flush();
6727
6728  } else {
6729    eval { server_wait($setup->{config_file}, $rfh) };
6730    if ($@) {
6731      warn($@);
6732      exit 1;
6733    }
6734
6735    exit 0;
6736  }
6737
6738  # Stop server
6739  server_stop($setup->{pid_file});
6740  $self->assert_child_ok($pid);
6741
6742  test_cleanup($setup->{log_file}, $ex);
6743}
6744
6745sub sql_sqlshowinfo_list_bug3423 {
6746  my $self = shift;
6747  my $tmpdir = $self->{tmpdir};
6748  my $setup = test_setup($tmpdir, 'sqlite');
6749
6750  my $lastdir = '/path/to/lastdir';
6751
6752  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6753
6754  # Build up sqlite3 command to create users, groups tables and populate them
6755  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6756
6757  if (open(my $fh, "> $db_script")) {
6758    print $fh <<EOS;
6759CREATE TABLE users (
6760  userid TEXT,
6761  passwd TEXT,
6762  uid INTEGER,
6763  gid INTEGER,
6764  homedir TEXT,
6765  shell TEXT,
6766  lastdir TEXT
6767);
6768INSERT INTO users (userid, passwd, uid, gid, homedir, shell, lastdir) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', '$lastdir');
6769
6770CREATE TABLE groups (
6771  groupname TEXT,
6772  gid INTEGER,
6773  members TEXT
6774);
6775INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
6776EOS
6777
6778    unless (close($fh)) {
6779      die("Can't write $db_script: $!");
6780    }
6781
6782  } else {
6783    die("Can't open $db_script: $!");
6784  }
6785
6786  my $cmd = "sqlite3 $db_file < $db_script";
6787  build_db($cmd, $db_script);
6788
6789  # Make sure that, if we're running as root, the database file has
6790  # the permissions/privs set for use by proftpd
6791  if ($< == 0) {
6792    unless (chmod(0666, $db_file)) {
6793      die("Can't set perms on $db_file to 0666: $!");
6794    }
6795  }
6796
6797  my $config = {
6798    PidFile => $setup->{pid_file},
6799    ScoreboardFile => $setup->{scoreboard_file},
6800    SystemLog => $setup->{log_file},
6801    TraceLog => $setup->{log_file},
6802    Trace => 'jot:20 sql:20',
6803
6804    IfModules => {
6805      'mod_delay.c' => {
6806        DelayEngine => 'off',
6807      },
6808
6809      'mod_sql.c' => [
6810        'AuthOrder mod_sql.c',
6811
6812        'SQLAuthenticate users groups',
6813        'SQLAuthTypes plaintext',
6814        'SQLBackend sqlite3',
6815        "SQLConnectInfo $db_file",
6816        "SQLLogFile $setup->{log_file}",
6817        'SQLMinID 100',
6818
6819        # Configure the equivalent of a multiline DisplayLogin file
6820        # using the SQLShowInfo directive
6821
6822        'SQLShowInfo LIST 226 "Get that directory listing OK, %u?"',
6823        'SQLShowInfo LIST 226 "-"',
6824      ],
6825    },
6826  };
6827
6828  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
6829    $config);
6830
6831  # Open pipes, for use between the parent and child processes.  Specifically,
6832  # the child will indicate when it's done with its test by writing a message
6833  # to the parent.
6834  my ($rfh, $wfh);
6835  unless (pipe($rfh, $wfh)) {
6836    die("Can't open pipe: $!");
6837  }
6838
6839  my $ex;
6840
6841  # Fork child
6842  $self->handle_sigchld();
6843  defined(my $pid = fork()) or die("Can't fork: $!");
6844  if ($pid) {
6845    eval {
6846      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
6847      $client->login($setup->{user}, $setup->{passwd});
6848
6849      my $resp_code = $client->response_code();
6850      my $resp_msg = $client->response_msg();
6851
6852      my $expected = 230;
6853      $self->assert($expected == $resp_code,
6854        "Expected response code $expected, got $resp_code");
6855
6856      $expected = "User $setup->{user} logged in";
6857      $self->assert($expected eq $resp_msg,
6858        "Expected response message '$expected', got '$resp_msg'");
6859
6860      my $conn = $client->list_raw();
6861      unless ($conn) {
6862        die("LIST failed unexpectedly: " . $client->response_code() . " " .
6863          $client->response_msg());
6864      }
6865
6866      my $buf;
6867      $conn->read($buf, 8192, 25);
6868      eval { $conn->close() };
6869
6870      $resp_code = $client->response_code();
6871
6872      $expected = 226;
6873      $self->assert($expected == $resp_code,
6874        "Expected response code $expected, got $resp_code");
6875
6876      my $resp_msgs = $client->response_msgs();
6877      my $nmsgs = scalar(@$resp_msgs);
6878
6879      $expected = 4;
6880      $self->assert($expected == $nmsgs, "Expected $expected, got $nmsgs");
6881
6882      $expected = "Opening ASCII mode data connection for file list";
6883      $self->assert($expected eq $resp_msgs->[0],
6884        "Expected response message '$expected', got '$resp_msgs->[0]'");
6885
6886      $expected = "Transfer complete";
6887      $self->assert($expected eq $resp_msgs->[1],
6888        "Expected response message '$expected', got '$resp_msgs->[1]'");
6889
6890      $expected = " Get that directory listing OK, $setup->{user}?";
6891      $self->assert($expected eq $resp_msgs->[2],
6892        "Expected response message '$expected', got '$resp_msgs->[2]'");
6893
6894      $expected = "-";
6895      $self->assert($expected eq $resp_msgs->[3],
6896        "Expected response message '$expected', got '$resp_msgs->[3]'");
6897
6898      $client->quit();
6899    };
6900    if ($@) {
6901      $ex = $@;
6902    }
6903
6904    $wfh->print("done\n");
6905    $wfh->flush();
6906
6907  } else {
6908    eval { server_wait($setup->{config_file}, $rfh) };
6909    if ($@) {
6910      warn($@);
6911      exit 1;
6912    }
6913
6914    exit 0;
6915  }
6916
6917  # Stop server
6918  server_stop($setup->{pid_file});
6919  $self->assert_child_ok($pid);
6920
6921  test_cleanup($setup->{log_file}, $ex);
6922}
6923
6924sub sql_multiple_users_shared_uid_gid {
6925  my $self = shift;
6926  my $tmpdir = $self->{tmpdir};
6927
6928  my $config_file = "$tmpdir/sqlite.conf";
6929  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
6930  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
6931
6932  my $log_file = test_get_logfile();
6933
6934  my $user = 'proftpd';
6935  my $passwd = 'test';
6936  my $group = 'ftpd';
6937  my $home_dir = File::Spec->rel2abs($tmpdir);
6938  my $uid = 500;
6939  my $gid = 500;
6940
6941  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
6942
6943  # Build up sqlite3 command to create users, groups tables and populate them
6944  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
6945
6946  if (open(my $fh, "> $db_script")) {
6947    print $fh <<EOU;
6948CREATE TABLE users (
6949  userid TEXT,
6950  passwd TEXT,
6951  uid INTEGER,
6952  gid INTEGER,
6953  homedir TEXT,
6954  shell TEXT
6955);
6956EOU
6957
6958  for (my $i = 1; $i <= 10; $i++) {
6959    my $name = $user . $i;
6960
6961    print $fh "INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$name', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');\n";
6962  }
6963
6964    print $fh <<EOG;
6965CREATE TABLE groups (
6966  groupname TEXT,
6967  gid INTEGER,
6968  members TEXT
6969);
6970EOG
6971
6972  my $group_members = ($user . '1');
6973  for (my $i = 2; $i <= 5; $i++) {
6974    my $name = $user . $i;
6975    $group_members .= ",$name";
6976  }
6977
6978  my $group_name = ($group . '1');
6979  print $fh "INSERT INTO groups (groupname, gid, members) VALUES ('$group_name', $gid, '$group_members');\n";
6980
6981  $group_members = ($user . '6');
6982  for (my $i = 7; $i <= 10; $i++) {
6983    my $name = $user . $i;
6984    $group_members .= ",$name";
6985  }
6986
6987  $group_name = ($group . '2');
6988  print $fh "INSERT INTO groups (groupname, gid, members) VALUES ('$group_name', $gid, '$group_members');\n";
6989
6990    unless (close($fh)) {
6991      die("Can't write $db_script: $!");
6992    }
6993
6994  } else {
6995    die("Can't open $db_script: $!");
6996  }
6997
6998  my $cmd = "sqlite3 $db_file < $db_script";
6999  build_db($cmd, $db_script);
7000
7001  # Make sure that, if we're running as root, the database file has
7002  # the permissions/privs set for use by proftpd
7003  if ($< == 0) {
7004    unless (chmod(0666, $db_file)) {
7005      die("Can't set perms on $db_file to 0666: $!");
7006    }
7007  }
7008
7009  my $config = {
7010    PidFile => $pid_file,
7011    ScoreboardFile => $scoreboard_file,
7012    SystemLog => $log_file,
7013
7014    IfModules => {
7015      'mod_delay.c' => {
7016        DelayEngine => 'off',
7017      },
7018
7019      'mod_sql.c' => [
7020        'AuthOrder mod_sql.c',
7021
7022        'SQLAuthenticate users groups',
7023        'SQLAuthTypes plaintext',
7024        'SQLBackend sqlite3',
7025        "SQLConnectInfo $db_file",
7026        "SQLLogFile $log_file",
7027        'SQLMinID 100',
7028      ],
7029    },
7030  };
7031
7032  my ($port, $config_user, $config_group) = config_write($config_file, $config);
7033
7034  # Open pipes, for use between the parent and child processes.  Specifically,
7035  # the child will indicate when it's done with its test by writing a message
7036  # to the parent.
7037  my ($rfh, $wfh);
7038  unless (pipe($rfh, $wfh)) {
7039    die("Can't open pipe: $!");
7040  }
7041
7042  my $ex;
7043
7044  # Fork child
7045  $self->handle_sigchld();
7046  defined(my $pid = fork()) or die("Can't fork: $!");
7047  if ($pid) {
7048    eval {
7049      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7050
7051      my $name = $user . '10';
7052      $client->login($name, $passwd);
7053      $client->quit();
7054    };
7055
7056    if ($@) {
7057      $ex = $@;
7058    }
7059
7060    $wfh->print("done\n");
7061    $wfh->flush();
7062
7063  } else {
7064    eval { server_wait($config_file, $rfh) };
7065    if ($@) {
7066      warn($@);
7067      exit 1;
7068    }
7069
7070    exit 0;
7071  }
7072
7073  # Stop server
7074  server_stop($pid_file);
7075
7076  $self->assert_child_ok($pid);
7077
7078  if ($ex) {
7079    test_append_logfile($log_file, $ex);
7080    unlink($log_file);
7081
7082    die($ex);
7083  }
7084
7085  unlink($log_file);
7086}
7087
7088sub get_cmds {
7089  my $db_file = shift;
7090  my $where = shift;
7091
7092  my $sql = "SELECT user, ip_addr, command, request FROM ftpsessions";
7093  if ($where) {
7094    $sql .= " WHERE $where";
7095  }
7096
7097  my $cmd = "sqlite3 $db_file \"$sql\"";
7098
7099  if ($ENV{TEST_VERBOSE}) {
7100    print STDERR "Executing sqlite3: $cmd\n";
7101  }
7102
7103  my $rows = [`$cmd`];
7104
7105  if ($ENV{TEST_VERBOSE}) {
7106    use Data::Dumper;
7107    print STDERR "Results: ", Dumper($rows), "\n";
7108  }
7109
7110  my $res;
7111
7112  # Return the last row found, for now
7113  foreach my $row (@$rows) {
7114    chomp($row);
7115
7116    # The default sqlite3 delimiter is '|'
7117    $res = [split(/\|/, $row)];
7118  }
7119
7120  return unless $res;
7121  return @$res;
7122}
7123
7124sub sql_resolve_tag_bug3536 {
7125  my $self = shift;
7126  my $tmpdir = $self->{tmpdir};
7127
7128  my $config_file = "$tmpdir/sqlite.conf";
7129  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
7130  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
7131
7132  my $log_file = test_get_logfile();
7133
7134  my $user = 'proftpd';
7135  my $passwd = 'test';
7136  my $group = 'ftpd';
7137  my $home_dir = File::Spec->rel2abs($tmpdir);
7138  my $uid = 500;
7139  my $gid = 500;
7140
7141  # Make sure that, if we're running as root, that the home directory has
7142  # permissions/privs set for the account we create
7143  if ($< == 0) {
7144    unless (chmod(0755, $home_dir)) {
7145      die("Can't set perms on $home_dir to 0755: $!");
7146    }
7147
7148    unless (chown($uid, $gid, $home_dir)) {
7149      die("Can't set owner of $home_dir to $uid/$gid: $!");
7150    }
7151  }
7152
7153  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
7154
7155  # Build up sqlite3 command to create users, groups tables and populate them
7156  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
7157
7158  if (open(my $fh, "> $db_script")) {
7159    print $fh <<EOS;
7160CREATE TABLE users (
7161  userid TEXT,
7162  passwd TEXT,
7163  uid INTEGER,
7164  gid INTEGER,
7165  homedir TEXT,
7166  shell TEXT
7167);
7168INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
7169
7170CREATE TABLE groups (
7171  groupname TEXT,
7172  gid INTEGER,
7173  members TEXT
7174);
7175INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
7176
7177CREATE TABLE ftpsessions (
7178  user TEXT,
7179  ip_addr TEXT,
7180  command TEXT,
7181  request TEXT
7182);
7183EOS
7184
7185    unless (close($fh)) {
7186      die("Can't write $db_script: $!");
7187    }
7188
7189  } else {
7190    die("Can't open $db_script: $!");
7191  }
7192
7193  my $cmd = "sqlite3 $db_file < $db_script";
7194  build_db($cmd, $db_script);
7195
7196  # Make sure that, if we're running as root, the database file has
7197  # the permissions/privs set for use by proftpd
7198  if ($< == 0) {
7199    unless (chmod(0666, $db_file)) {
7200      die("Can't set perms on $db_file to 0666: $!");
7201    }
7202  }
7203
7204  my $config = {
7205    PidFile => $pid_file,
7206    ScoreboardFile => $scoreboard_file,
7207    SystemLog => $log_file,
7208    TraceLog => $log_file,
7209    Trace => 'sql:20',
7210
7211    IfModules => {
7212      'mod_delay.c' => {
7213        DelayEngine => 'off',
7214      },
7215
7216      'mod_sql.c' => {
7217        SQLAuthTypes => 'plaintext',
7218        SQLBackend => 'sqlite3',
7219        SQLConnectInfo => $db_file,
7220        SQLLogFile => $log_file,
7221        SQLNamedQuery => 'command FREEFORM "INSERT INTO ftpsessions (user, ip_addr, command, request) VALUES (\'%u\', \'%L\', \'%m\', \'%r\')"',
7222        SQLLog => '* command',
7223      },
7224    },
7225  };
7226
7227  my ($port, $config_user, $config_group) = config_write($config_file, $config);
7228
7229  # Open pipes, for use between the parent and child processes.  Specifically,
7230  # the child will indicate when it's done with its test by writing a message
7231  # to the parent.
7232  my ($rfh, $wfh);
7233  unless (pipe($rfh, $wfh)) {
7234    die("Can't open pipe: $!");
7235  }
7236
7237  my $ex;
7238
7239  # Fork child
7240  $self->handle_sigchld();
7241  defined(my $pid = fork()) or die("Can't fork: $!");
7242  if ($pid) {
7243    eval {
7244      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7245
7246      # Ignore errors for these commands
7247      eval {
7248        my $name = "AAAAAAAAAA%m%m%mA%m%m%mA%m%mAA%m%m%m%m%mA%m%Z%m%mA%m%mAA%mA%ZAA%m%m%m%m%m%mA%m%ZAAA%m%m%m%m%m%m%m%m%m%m%m%m%m%m%mAa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0A%%%m%r%m%Z";
7249        $client->user($name);
7250      };
7251    };
7252
7253    if ($@) {
7254      $ex = $@;
7255    }
7256
7257    $wfh->print("done\n");
7258    $wfh->flush();
7259
7260  } else {
7261    eval { server_wait($config_file, $rfh) };
7262    if ($@) {
7263      warn($@);
7264      exit 1;
7265    }
7266
7267    exit 0;
7268  }
7269
7270  # Stop server
7271  server_stop($pid_file);
7272
7273  $self->assert_child_ok($pid);
7274
7275  if ($ex) {
7276    test_append_logfile($log_file, $ex);
7277    unlink($log_file);
7278
7279    die($ex);
7280  }
7281
7282  my ($login, $ip_addr, $req);
7283  ($login, $ip_addr, $cmd, $req) = get_cmds($db_file, "user = \'$config_user\'");
7284
7285  $self->assert(!defined($login), test_msg("Expected undef, got '$login'"));
7286  $self->assert(!defined($ip_addr), test_msg("Expected undef, got '$ip_addr'"));
7287
7288  unlink($log_file);
7289}
7290
7291sub get_session_io {
7292  my $db_file = shift;
7293  my $where = shift;
7294
7295  my $sql = "SELECT user, ip_addr, bytes_in, bytes_out FROM ftpsessions";
7296  if ($where) {
7297    $sql .= " WHERE $where";
7298  }
7299
7300  my $cmd = "sqlite3 $db_file \"$sql\"";
7301
7302  if ($ENV{TEST_VERBOSE}) {
7303    print STDERR "Executing sqlite3: $cmd\n";
7304  }
7305
7306  my $res = join('', `$cmd`);
7307  chomp($res);
7308
7309  # The default sqlite3 delimiter is '|'
7310  return split(/\|/, $res);
7311}
7312
7313sub sql_sqllog_vars_I_O_bug3554 {
7314  my $self = shift;
7315  my $tmpdir = $self->{tmpdir};
7316  my $setup = test_setup($tmpdir, 'sqlite');
7317
7318  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
7319
7320  # Build up sqlite3 command to create users, groups tables and populate them
7321  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
7322
7323  if (open(my $fh, "> $db_script")) {
7324    print $fh <<EOS;
7325CREATE TABLE ftpsessions (
7326  user TEXT,
7327  ip_addr TEXT,
7328  bytes_in NUMBER,
7329  bytes_out NUMBER
7330);
7331EOS
7332
7333    unless (close($fh)) {
7334      die("Can't write $db_script: $!");
7335    }
7336
7337  } else {
7338    die("Can't open $db_script: $!");
7339  }
7340
7341  my $cmd = "sqlite3 $db_file < $db_script";
7342  build_db($cmd, $db_script);
7343
7344  # Make sure that, if we're running as root, the database file has
7345  # the permissions/privs set for use by proftpd
7346  if ($< == 0) {
7347    unless (chmod(0666, $db_file)) {
7348      die("Can't set perms on $db_file to 0666: $!");
7349    }
7350  }
7351
7352  my $config = {
7353    PidFile => $setup->{pid_file},
7354    ScoreboardFile => $setup->{scoreboard_file},
7355    SystemLog => $setup->{log_file},
7356    TraceLog => $setup->{log_file},
7357    Trace => 'DEFAULT:10 jot:20 sql:20',
7358
7359    AuthUserFile => $setup->{auth_user_file},
7360    AuthGroupFile => $setup->{auth_group_file},
7361
7362    IfModules => {
7363      'mod_delay.c' => {
7364        DelayEngine => 'off',
7365      },
7366
7367      'mod_sql.c' => {
7368        SQLEngine => 'log',
7369        SQLBackend => 'sqlite3',
7370        SQLConnectInfo => $db_file,
7371        SQLLogFile => $setup->{log_file},
7372        SQLNamedQuery => 'session_io FREEFORM "INSERT INTO ftpsessions (user, ip_addr, bytes_in, bytes_out) VALUES (\'%u\', \'%L\', %I, %O)"',
7373        SQLLog => 'EXIT session_io',
7374      },
7375    },
7376  };
7377
7378  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
7379    $config);
7380
7381  # Open pipes, for use between the parent and child processes.  Specifically,
7382  # the child will indicate when it's done with its test by writing a message
7383  # to the parent.
7384  my ($rfh, $wfh);
7385  unless (pipe($rfh, $wfh)) {
7386    die("Can't open pipe: $!");
7387  }
7388
7389  my $ex;
7390
7391  # Fork child
7392  $self->handle_sigchld();
7393  defined(my $pid = fork()) or die("Can't fork: $!");
7394  if ($pid) {
7395    eval {
7396      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7397      $client->login($setup->{user}, $setup->{passwd});
7398      $client->type('ascii');
7399
7400      my $conn = $client->stor_raw('test.txt');
7401      unless ($conn) {
7402        die("STOR test.txt failed: " . $client->response_code() . " " .
7403          $client->response_msg());
7404      }
7405
7406      my $buf = "ABCD\n" x 8;
7407      $conn->write($buf, length($buf), 30);
7408      $conn->close();
7409
7410      my $resp_code = $client->response_code();
7411      my $resp_msg = $client->response_msg();
7412
7413      $client->quit();
7414
7415      my $expected = 226;
7416      $self->assert($expected == $resp_code,
7417        "Expected response code $expected, got $resp_code");
7418
7419      $expected = 'Transfer complete';
7420      $self->assert($expected eq $resp_msg,
7421        "Expected response message '$expected', got '$resp_msg'");
7422    };
7423    if ($@) {
7424      $ex = $@;
7425    }
7426
7427    $wfh->print("done\n");
7428    $wfh->flush();
7429
7430  } else {
7431    eval { server_wait($setup->{config_file}, $rfh) };
7432    if ($@) {
7433      warn($@);
7434      exit 1;
7435    }
7436
7437    exit 0;
7438  }
7439
7440  # Stop server
7441  server_stop($setup->{pid_file});
7442  $self->assert_child_ok($pid);
7443
7444  if ($ex) {
7445    test_cleanup($setup->{log_file}, $ex);
7446  }
7447
7448  eval {
7449    my ($login, $ip_addr, $bytes_in, $bytes_out) = get_session_io($db_file,
7450      "user = \'$setup->{user}\'");
7451
7452    my $expected = $setup->{user};
7453    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
7454
7455    $expected = '127.0.0.1';
7456    $self->assert($expected eq $ip_addr,
7457      "Expected '$expected', got '$ip_addr'");
7458
7459    $expected = 108;
7460    $self->assert($expected == $bytes_in,
7461      "Expected $expected, got $bytes_in");
7462
7463    # Why would this number vary so widely?  It's because of the notation
7464    # used to express the port number in a PASV response.  That port
7465    # number is ephemeral, chosen by the kernel.
7466
7467    my $expected_min = 232;
7468    my $expected_max = 236;
7469    $self->assert($expected_min <= $bytes_out ||
7470                  $expected_max >= $bytes_out,
7471      "Expected $expected_min - $expected_max, got $bytes_out");
7472  };
7473  if ($@) {
7474    $ex = $@;
7475  }
7476
7477  test_cleanup($setup->{log_file}, $ex);
7478}
7479
7480sub get_session_id {
7481  my $db_file = shift;
7482  my $where = shift;
7483
7484  my $sql = "SELECT user, ip_addr, session_id FROM ftpsessions";
7485  if ($where) {
7486    $sql .= " WHERE $where";
7487  }
7488
7489  my $cmd = "sqlite3 $db_file \"$sql\"";
7490
7491  if ($ENV{TEST_VERBOSE}) {
7492    print STDERR "Executing sqlite3: $cmd\n";
7493  }
7494
7495  my $res = join('', `$cmd`);
7496  chomp($res);
7497
7498  # The default sqlite3 delimiter is '|'
7499  return split(/\|/, $res);
7500}
7501
7502sub sql_sqllog_note_var_unique_id_bug3572 {
7503  my $self = shift;
7504  my $tmpdir = $self->{tmpdir};
7505
7506  my $config_file = "$tmpdir/sqlite.conf";
7507  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
7508  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
7509
7510  my $log_file = test_get_logfile();
7511
7512  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
7513  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
7514
7515  my $user = 'proftpd';
7516  my $passwd = 'test';
7517  my $group = 'ftpd';
7518  my $home_dir = File::Spec->rel2abs($tmpdir);
7519  my $uid = 500;
7520  my $gid = 500;
7521
7522  # Make sure that, if we're running as root, that the home directory has
7523  # permissions/privs set for the account we create
7524  if ($< == 0) {
7525    unless (chmod(0755, $home_dir)) {
7526      die("Can't set perms on $home_dir to 0755: $!");
7527    }
7528
7529    unless (chown($uid, $gid, $home_dir)) {
7530      die("Can't set owner of $home_dir to $uid/$gid: $!");
7531    }
7532  }
7533
7534  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
7535    '/bin/bash');
7536  auth_group_write($auth_group_file, $group, $gid, $user);
7537
7538  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
7539
7540  # Build up sqlite3 command to create users, groups tables and populate them
7541  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
7542
7543  if (open(my $fh, "> $db_script")) {
7544    print $fh <<EOS;
7545CREATE TABLE ftpsessions (
7546  user TEXT,
7547  ip_addr TEXT,
7548  session_id TEXT
7549);
7550EOS
7551
7552    unless (close($fh)) {
7553      die("Can't write $db_script: $!");
7554    }
7555
7556  } else {
7557    die("Can't open $db_script: $!");
7558  }
7559
7560  my $cmd = "sqlite3 $db_file < $db_script";
7561  build_db($cmd, $db_script);
7562
7563  # Make sure that, if we're running as root, the database file has
7564  # the permissions/privs set for use by proftpd
7565  if ($< == 0) {
7566    unless (chmod(0666, $db_file)) {
7567      die("Can't set perms on $db_file to 0666: $!");
7568    }
7569  }
7570
7571  my $config = {
7572    PidFile => $pid_file,
7573    ScoreboardFile => $scoreboard_file,
7574    SystemLog => $log_file,
7575    TraceLog => $log_file,
7576    Trace => 'DEFAULT:10',
7577
7578    AuthUserFile => $auth_user_file,
7579    AuthGroupFile => $auth_group_file,
7580
7581    IfModules => {
7582      'mod_delay.c' => {
7583        DelayEngine => 'off',
7584      },
7585
7586      'mod_sql.c' => {
7587        SQLEngine => 'log',
7588        SQLBackend => 'sqlite3',
7589        SQLConnectInfo => $db_file,
7590        SQLLogFile => $log_file,
7591        SQLNamedQuery => 'session_id FREEFORM "INSERT INTO ftpsessions (user, ip_addr, session_id) VALUES (\'%u\', \'%L\', \'%{note:UNIQUE_ID}\')"',
7592        SQLLog => 'PASS session_id',
7593      },
7594    },
7595  };
7596
7597  my ($port, $config_user, $config_group) = config_write($config_file, $config);
7598
7599  # Open pipes, for use between the parent and child processes.  Specifically,
7600  # the child will indicate when it's done with its test by writing a message
7601  # to the parent.
7602  my ($rfh, $wfh);
7603  unless (pipe($rfh, $wfh)) {
7604    die("Can't open pipe: $!");
7605  }
7606
7607  my $ex;
7608
7609  # Fork child
7610  $self->handle_sigchld();
7611  defined(my $pid = fork()) or die("Can't fork: $!");
7612  if ($pid) {
7613    eval {
7614      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7615      $client->login($user, $passwd);
7616      $client->quit();
7617    };
7618
7619    if ($@) {
7620      $ex = $@;
7621    }
7622
7623    $wfh->print("done\n");
7624    $wfh->flush();
7625
7626  } else {
7627    eval { server_wait($config_file, $rfh) };
7628    if ($@) {
7629      warn($@);
7630      exit 1;
7631    }
7632
7633    exit 0;
7634  }
7635
7636  # Stop server
7637  server_stop($pid_file);
7638
7639  $self->assert_child_ok($pid);
7640
7641  if ($ex) {
7642    test_append_logfile($log_file, $ex);
7643    unlink($log_file);
7644
7645    die($ex);
7646  }
7647
7648  my ($login, $ip_addr, $session_id) = get_session_id($db_file,
7649    "user = \'$user\'");
7650
7651  my $expected;
7652
7653  $expected = $user;
7654  $self->assert($expected eq $login,
7655    test_msg("Expected '$expected', got '$login'"));
7656
7657  $expected = '127.0.0.1';
7658  $self->assert($expected eq $ip_addr,
7659    test_msg("Expected '$expected', got '$ip_addr'"));
7660
7661  $self->assert($session_id ne '',
7662    test_msg("Expected session ID ($session_id), got ''"));
7663
7664  unlink($log_file);
7665}
7666
7667sub sql_sqllog_note_var_rewrite_bug3572 {
7668  my $self = shift;
7669  my $tmpdir = $self->{tmpdir};
7670
7671  my $config_file = "$tmpdir/sqlite.conf";
7672  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
7673  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
7674
7675  my $log_file = test_get_logfile();
7676
7677  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
7678  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
7679
7680  my $user = 'proftpd';
7681  my $passwd = 'test';
7682  my $group = 'ftpd';
7683  my $home_dir = File::Spec->rel2abs($tmpdir);
7684  my $uid = 500;
7685  my $gid = 500;
7686
7687  my $domain = 'proftpd.org';
7688
7689  # Make sure that, if we're running as root, that the home directory has
7690  # permissions/privs set for the account we create
7691  if ($< == 0) {
7692    unless (chmod(0755, $home_dir)) {
7693      die("Can't set perms on $home_dir to 0755: $!");
7694    }
7695
7696    unless (chown($uid, $gid, $home_dir)) {
7697      die("Can't set owner of $home_dir to $uid/$gid: $!");
7698    }
7699  }
7700
7701  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
7702    '/bin/bash');
7703  auth_group_write($auth_group_file, $group, $gid, $user);
7704
7705  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
7706
7707  # Build up sqlite3 command to create users, groups tables and populate them
7708  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
7709
7710  if (open(my $fh, "> $db_script")) {
7711    print $fh <<EOS;
7712CREATE TABLE ftpsessions (
7713  user TEXT,
7714  ip_addr TEXT,
7715  session_id TEXT
7716);
7717EOS
7718
7719    unless (close($fh)) {
7720      die("Can't write $db_script: $!");
7721    }
7722
7723  } else {
7724    die("Can't open $db_script: $!");
7725  }
7726
7727  my $cmd = "sqlite3 $db_file < $db_script";
7728  build_db($cmd, $db_script);
7729
7730  # Make sure that, if we're running as root, the database file has
7731  # the permissions/privs set for use by proftpd
7732  if ($< == 0) {
7733    unless (chmod(0666, $db_file)) {
7734      die("Can't set perms on $db_file to 0666: $!");
7735    }
7736  }
7737
7738  my $config = {
7739    PidFile => $pid_file,
7740    ScoreboardFile => $scoreboard_file,
7741    SystemLog => $log_file,
7742    TraceLog => $log_file,
7743    Trace => 'rewrite:10 sql:20 table:20',
7744
7745    AuthUserFile => $auth_user_file,
7746    AuthGroupFile => $auth_group_file,
7747
7748    IfModules => {
7749      'mod_delay.c' => {
7750        DelayEngine => 'off',
7751      },
7752
7753      'mod_rewrite.c' => [
7754        'RewriteEngine on',
7755        "RewriteLog $log_file",
7756
7757        'RewriteCondition %m USER',
7758        'RewriteRule ^(.*#)?([-_.0-9A-Za-z]+)(@)?(.*)? $2',
7759      ],
7760
7761      'mod_sql.c' => {
7762        SQLEngine => 'log',
7763        SQLBackend => 'sqlite3',
7764        SQLConnectInfo => $db_file,
7765        SQLLogFile => $log_file,
7766        SQLNamedQuery => 'session_id FREEFORM "INSERT INTO ftpsessions (user, ip_addr, session_id) VALUES (\'%U\', \'%L\', \'%{note:mod_rewrite.$4}\')"',
7767        SQLLog => 'USER session_id',
7768      },
7769    },
7770  };
7771
7772  my ($port, $config_user, $config_group) = config_write($config_file, $config);
7773
7774  # Open pipes, for use between the parent and child processes.  Specifically,
7775  # the child will indicate when it's done with its test by writing a message
7776  # to the parent.
7777  my ($rfh, $wfh);
7778  unless (pipe($rfh, $wfh)) {
7779    die("Can't open pipe: $!");
7780  }
7781
7782  my $ex;
7783
7784  # Fork child
7785  $self->handle_sigchld();
7786  defined(my $pid = fork()) or die("Can't fork: $!");
7787  if ($pid) {
7788    eval {
7789      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7790
7791      my $login = $user . '@' . $domain;
7792      $client->login($login, $passwd);
7793      $client->quit();
7794    };
7795
7796    if ($@) {
7797      $ex = $@;
7798    }
7799
7800    $wfh->print("done\n");
7801    $wfh->flush();
7802
7803  } else {
7804    eval { server_wait($config_file, $rfh) };
7805    if ($@) {
7806      warn($@);
7807      exit 1;
7808    }
7809
7810    exit 0;
7811  }
7812
7813  # Stop server
7814  server_stop($pid_file);
7815
7816  $self->assert_child_ok($pid);
7817
7818  if ($ex) {
7819    test_append_logfile($log_file, $ex);
7820    unlink($log_file);
7821
7822    die($ex);
7823  }
7824
7825  my ($login, $ip_addr, $session_id) = get_session_id($db_file,
7826    "user = \'$user\'");
7827
7828  my $expected;
7829
7830  $expected = $user;
7831  $self->assert($expected eq $login,
7832    test_msg("Expected '$expected', got '$login'"));
7833
7834  $expected = '127.0.0.1';
7835  $self->assert($expected eq $ip_addr,
7836    test_msg("Expected '$expected', got '$ip_addr'"));
7837
7838  $expected = $domain;
7839  $self->assert($expected eq $session_id,
7840    test_msg("Expected '$expected', got '$session_id'"));
7841
7842  unlink($log_file);
7843}
7844
7845sub get_sql_notes {
7846  my $db_file = shift;
7847  my $where = shift;
7848
7849  my $sql = "SELECT user, primary_group, home, shell FROM ftpnotes";
7850  if ($where) {
7851    $sql .= " WHERE $where";
7852  }
7853
7854  my $cmd = "sqlite3 $db_file \"$sql\"";
7855
7856  if ($ENV{TEST_VERBOSE}) {
7857    print STDERR "Executing sqlite3: $cmd\n";
7858  }
7859
7860  my $res = join('', `$cmd`);
7861  chomp($res);
7862
7863  # The default sqlite3 delimiter is '|'
7864  return split(/\|/, $res);
7865}
7866
7867sub sql_sqllog_note_sql_user_info {
7868  my $self = shift;
7869  my $tmpdir = $self->{tmpdir};
7870
7871  my $config_file = "$tmpdir/sqlite.conf";
7872  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
7873  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
7874
7875  my $log_file = test_get_logfile();
7876
7877  my $user = 'proftpd';
7878  my $group = 'ftpd',
7879  my $passwd = 'test';
7880  my $home_dir = File::Spec->rel2abs($tmpdir);
7881  my $uid = 500;
7882  my $gid = 500;
7883  my $shell = '/bin/bash';
7884
7885  # Make sure that, if we're running as root, that the home directory has
7886  # permissions/privs set for the account we create
7887  if ($< == 0) {
7888    unless (chmod(0755, $home_dir)) {
7889      die("Can't set perms on $home_dir to 0755: $!");
7890    }
7891
7892    unless (chown($uid, $gid, $home_dir)) {
7893      die("Can't set owner of $home_dir to $uid/$gid: $!");
7894    }
7895  }
7896
7897  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
7898
7899  # Build up sqlite3 command to create users, groups tables and populate them
7900  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
7901
7902  if (open(my $fh, "> $db_script")) {
7903    print $fh <<EOS;
7904CREATE TABLE users (
7905  userid TEXT,
7906  passwd TEXT,
7907  uid INTEGER,
7908  gid INTEGER,
7909  homedir TEXT,
7910  shell TEXT,
7911  lastdir TEXT
7912);
7913INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '$shell');
7914
7915CREATE TABLE groups (
7916  groupname TEXT,
7917  gid INTEGER,
7918  members TEXT
7919);
7920INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
7921
7922CREATE TABLE ftpnotes (
7923  user TEXT,
7924  primary_group TEXT,
7925  home TEXT,
7926  shell TEXT
7927);
7928EOS
7929
7930    unless (close($fh)) {
7931      die("Can't write $db_script: $!");
7932    }
7933
7934  } else {
7935    die("Can't open $db_script: $!");
7936  }
7937
7938  my $cmd = "sqlite3 $db_file < $db_script";
7939  build_db($cmd, $db_script);
7940
7941  # Make sure that, if we're running as root, the database file has
7942  # the permissions/privs set for use by proftpd
7943  if ($< == 0) {
7944    unless (chmod(0666, $db_file)) {
7945      die("Can't set perms on $db_file to 0666: $!");
7946    }
7947  }
7948
7949  my $config = {
7950    PidFile => $pid_file,
7951    ScoreboardFile => $scoreboard_file,
7952    SystemLog => $log_file,
7953    TraceLog => $log_file,
7954    Trace => 'DEFAULT:10 sql:20',
7955
7956    IfModules => {
7957      'mod_delay.c' => {
7958        DelayEngine => 'off',
7959      },
7960
7961      'mod_sql.c' => {
7962        SQLAuthTypes => 'plaintext',
7963        SQLBackend => 'sqlite3',
7964        SQLConnectInfo => $db_file,
7965        SQLMinID => 100,
7966        SQLLogFile => $log_file,
7967        SQLNamedQuery => 'sql_notes FREEFORM "INSERT INTO ftpnotes (user, primary_group, home, shell) VALUES (\'%u\', \'%{note:primary-group}\', \'%{note:home}\', \'%{note:shell}\')"',
7968        SQLLog => 'PASS sql_notes',
7969      },
7970    },
7971  };
7972
7973  my ($port, $config_user, $config_group) = config_write($config_file, $config);
7974
7975  # Open pipes, for use between the parent and child processes.  Specifically,
7976  # the child will indicate when it's done with its test by writing a message
7977  # to the parent.
7978  my ($rfh, $wfh);
7979  unless (pipe($rfh, $wfh)) {
7980    die("Can't open pipe: $!");
7981  }
7982
7983  my $ex;
7984
7985  # Fork child
7986  $self->handle_sigchld();
7987  defined(my $pid = fork()) or die("Can't fork: $!");
7988  if ($pid) {
7989    eval {
7990      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
7991      $client->login($user, $passwd);
7992      $client->quit();
7993    };
7994
7995    if ($@) {
7996      $ex = $@;
7997    }
7998
7999    $wfh->print("done\n");
8000    $wfh->flush();
8001
8002  } else {
8003    eval { server_wait($config_file, $rfh) };
8004    if ($@) {
8005      warn($@);
8006      exit 1;
8007    }
8008
8009    exit 0;
8010  }
8011
8012  # Stop server
8013  server_stop($pid_file);
8014
8015  $self->assert_child_ok($pid);
8016
8017  if ($ex) {
8018    test_append_logfile($log_file, $ex);
8019    unlink($log_file);
8020
8021    die($ex);
8022  }
8023
8024  my ($login, $sql_group, $sql_home, $sql_shell) = get_sql_notes($db_file,
8025    "user = \'$user\'");
8026
8027  my $expected;
8028
8029  $expected = $user;
8030  $self->assert($expected eq $login,
8031    test_msg("Expected '$expected', got '$login'"));
8032
8033  $expected = $group;
8034  $self->assert($expected eq $sql_group,
8035    test_msg("Expected '$expected', got '$sql_group'"));
8036
8037  $expected = $home_dir;
8038  $self->assert($expected eq $sql_home,
8039    test_msg("Expected '$expected', got '$sql_home'"));
8040
8041  $expected = $shell;
8042  $self->assert($expected eq $sql_shell,
8043    test_msg("Expected '$expected', got '$sql_shell'"));
8044
8045  unlink($log_file);
8046}
8047
8048sub sql_sqllog_var_E {
8049  my $self = shift;
8050  my $tmpdir = $self->{tmpdir};
8051  my $setup = test_setup($tmpdir, 'sqlite');
8052
8053  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
8054
8055  # Build up sqlite3 command to create users, groups tables and populate them
8056  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
8057
8058  if (open(my $fh, "> $db_script")) {
8059    print $fh <<EOS;
8060CREATE TABLE ftpsessions (
8061  user TEXT,
8062  ip_addr TEXT,
8063  resp_mesg TEXT
8064);
8065EOS
8066
8067    unless (close($fh)) {
8068      die("Can't write $db_script: $!");
8069    }
8070
8071  } else {
8072    die("Can't open $db_script: $!");
8073  }
8074
8075  my $cmd = "sqlite3 $db_file < $db_script";
8076  build_db($cmd, $db_script);
8077
8078  # Make sure that, if we're running as root, the database file has
8079  # the permissions/privs set for use by proftpd
8080  if ($< == 0) {
8081    unless (chmod(0666, $db_file)) {
8082      die("Can't set perms on $db_file to 0666: $!");
8083    }
8084  }
8085
8086  my $config = {
8087    PidFile => $setup->{pid_file},
8088    ScoreboardFile => $setup->{scoreboard_file},
8089    SystemLog => $setup->{log_file},
8090    TraceLog => $setup->{log_file},
8091    Trace => 'jot:20 sql:20',
8092
8093    AuthUserFile => $setup->{auth_user_file},
8094    AuthGroupFile => $setup->{auth_group_file},
8095
8096    IfModules => {
8097      'mod_delay.c' => {
8098        DelayEngine => 'off',
8099      },
8100
8101      'mod_sql.c' => {
8102        SQLEngine => 'log',
8103        SQLBackend => 'sqlite3',
8104        SQLConnectInfo => $db_file,
8105        SQLLogFile => $setup->{log_file},
8106        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, ip_addr, resp_mesg) VALUES (\'%u\', \'%L\', \'%E\')"',
8107        SQLLog => 'EXIT info',
8108      },
8109    },
8110  };
8111
8112  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
8113    $config);
8114
8115  # Open pipes, for use between the parent and child processes.  Specifically,
8116  # the child will indicate when it's done with its test by writing a message
8117  # to the parent.
8118  my ($rfh, $wfh);
8119  unless (pipe($rfh, $wfh)) {
8120    die("Can't open pipe: $!");
8121  }
8122
8123  my $ex;
8124
8125  # Fork child
8126  $self->handle_sigchld();
8127  defined(my $pid = fork()) or die("Can't fork: $!");
8128  if ($pid) {
8129    eval {
8130      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
8131      $client->login($setup->{user}, $setup->{passwd});
8132      $client->list();
8133      $client->quit();
8134    };
8135    if ($@) {
8136      $ex = $@;
8137    }
8138
8139    $wfh->print("done\n");
8140    $wfh->flush();
8141
8142  } else {
8143    eval { server_wait($setup->{config_file}, $rfh) };
8144    if ($@) {
8145      warn($@);
8146      exit 1;
8147    }
8148
8149    exit 0;
8150  }
8151
8152  # Stop server
8153  server_stop($setup->{pid_file});
8154  $self->assert_child_ok($pid);
8155
8156  if ($ex) {
8157    test_cleanup($setup->{log_file}, $ex);
8158  }
8159
8160  eval {
8161    my ($login, $ip_addr, $resp_mesg) = get_resp_mesgs($db_file,
8162      "user = \'$setup->{user}\'", "user, ip_addr, resp_mesg");
8163
8164    my $expected = $setup->{user};
8165    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
8166
8167    $expected = '127.0.0.1';
8168    $self->assert($expected eq $ip_addr,
8169      "Expected '$expected', got '$ip_addr'");
8170
8171    $expected = 'Quit';
8172    $self->assert($expected eq $resp_mesg,
8173      "Expected '$expected', got '$resp_mesg'");
8174  };
8175  if ($@) {
8176    $ex = $@;
8177  }
8178
8179  test_cleanup($setup->{log_file}, $ex);
8180}
8181
8182sub sql_named_conn_bug3262 {
8183  my $self = shift;
8184  my $tmpdir = $self->{tmpdir};
8185
8186  my $config_file = "$tmpdir/sqlite.conf";
8187  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
8188  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
8189
8190  my $log_file = test_get_logfile();
8191
8192  my $user = 'proftpd';
8193  my $passwd = 'test';
8194  my $group = 'ftpd';
8195  my $home_dir = File::Spec->rel2abs($tmpdir);
8196  my $uid = 500;
8197  my $gid = 500;
8198
8199  # Make sure that, if we're running as root, that the home directory has
8200  # permissions/privs set for the account we create
8201  if ($< == 0) {
8202    unless (chmod(0755, $home_dir)) {
8203      die("Can't set perms on $home_dir to 0755: $!");
8204    }
8205
8206    unless (chown($uid, $gid, $home_dir)) {
8207      die("Can't set owner of $home_dir to $uid/$gid: $!");
8208    }
8209  }
8210
8211  my $userdb_file = File::Spec->rel2abs("$tmpdir/proftpd-users.db");
8212
8213  # Build up sqlite3 command to create users, groups tables and populate them
8214  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd-users.sql");
8215
8216  if (open(my $fh, "> $db_script")) {
8217    print $fh <<EOS;
8218CREATE TABLE ftpusers (
8219  userid TEXT,
8220  passwd TEXT,
8221  uid INTEGER,
8222  gid INTEGER,
8223  homedir TEXT,
8224  shell TEXT,
8225  lastdir TEXT
8226);
8227INSERT INTO ftpusers (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
8228
8229CREATE TABLE groups (
8230  groupname TEXT,
8231  gid INTEGER,
8232  members TEXT
8233);
8234INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
8235
8236CREATE TABLE ftpsessions (
8237  user TEXT,
8238  ip_addr TEXT,
8239  timestamp TEXT
8240);
8241
8242EOS
8243
8244    unless (close($fh)) {
8245      die("Can't write $db_script: $!");
8246    }
8247
8248  } else {
8249    die("Can't open $db_script: $!");
8250  }
8251
8252  my $cmd = "sqlite3 $userdb_file < $db_script";
8253  build_db($cmd, $db_script);
8254
8255  my $logdb_file = File::Spec->rel2abs("$tmpdir/proftpd-log.db");
8256
8257  # Build up sqlite3 command to create users, groups tables and populate them
8258  $db_script = File::Spec->rel2abs("$tmpdir/proftpd-log.sql");
8259
8260  if (open(my $fh, "> $db_script")) {
8261    print $fh <<EOS;
8262CREATE TABLE ftpsessions (
8263  user TEXT,
8264  ip_addr TEXT,
8265  timestamp TEXT
8266);
8267
8268EOS
8269
8270    unless (close($fh)) {
8271      die("Can't write $db_script: $!");
8272    }
8273
8274  } else {
8275    die("Can't open $db_script: $!");
8276  }
8277
8278  $cmd = "sqlite3 $logdb_file < $db_script";
8279  build_db($cmd, $db_script);
8280
8281  # Make sure that, if we're running as root, the database file has
8282  # the permissions/privs set for use by proftpd
8283  if ($< == 0) {
8284    unless (chmod(0666, $userdb_file, $logdb_file)) {
8285      die("Can't set perms on $userdb_file, $logdb_file to 0666: $!");
8286    }
8287  }
8288
8289  my $config = {
8290    PidFile => $pid_file,
8291    ScoreboardFile => $scoreboard_file,
8292    SystemLog => $log_file,
8293
8294    IfModules => {
8295      'mod_delay.c' => {
8296        DelayEngine => 'off',
8297      },
8298
8299      'mod_sql.c' => [
8300        'SQLAuthTypes plaintext',
8301        'SQLBackend sqlite3',
8302        "SQLConnectInfo $userdb_file",
8303        "SQLNamedConnectInfo logdb sqlite3 $logdb_file foo bar PERSESSION",
8304        "SQLLogFile $log_file",
8305        'SQLNamedQuery get-user-by-name SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE userid = \'%U\'"',
8306        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers WHERE uid = %{0}"',
8307        'SQLNamedQuery get-user-names SELECT "userid FROM ftpusers"',
8308        'SQLNamedQuery get-all-users SELECT "userid, passwd, uid, gid, homedir, shell FROM ftpusers"',
8309        'SQLUserInfo custom:/get-user-by-name/get-user-by-id/get-user-names/get-all-users',
8310        'SQLNamedQuery session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')" logdb',
8311        'SQLLog PASS session_start',
8312      ],
8313    },
8314  };
8315
8316  my ($port, $config_user, $config_group) = config_write($config_file, $config);
8317
8318  # Open pipes, for use between the parent and child processes.  Specifically,
8319  # the child will indicate when it's done with its test by writing a message
8320  # to the parent.
8321  my ($rfh, $wfh);
8322  unless (pipe($rfh, $wfh)) {
8323    die("Can't open pipe: $!");
8324  }
8325
8326  my $ex;
8327
8328  # Fork child
8329  $self->handle_sigchld();
8330  defined(my $pid = fork()) or die("Can't fork: $!");
8331  if ($pid) {
8332    eval {
8333      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
8334
8335      $client->login($user, $passwd);
8336
8337      my $resp_msgs = $client->response_msgs();
8338      my $nmsgs = scalar(@$resp_msgs);
8339
8340      my $expected;
8341
8342      $expected = 1;
8343      $self->assert($expected == $nmsgs,
8344        test_msg("Expected $expected, got $nmsgs"));
8345
8346      $expected = "User proftpd logged in";
8347      $self->assert($expected eq $resp_msgs->[0],
8348        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
8349
8350    };
8351
8352    if ($@) {
8353      $ex = $@;
8354    }
8355
8356    $wfh->print("done\n");
8357    $wfh->flush();
8358
8359  } else {
8360    eval { server_wait($config_file, $rfh) };
8361    if ($@) {
8362      warn($@);
8363      exit 1;
8364    }
8365
8366    exit 0;
8367  }
8368
8369  # Stop server
8370  server_stop($pid_file);
8371
8372  $self->assert_child_ok($pid);
8373
8374  if ($ex) {
8375    test_append_logfile($log_file, $ex);
8376    unlink($log_file);
8377
8378    die($ex);
8379  }
8380
8381  my ($login, $ip_addr, $timestamp) = get_sessions($userdb_file,
8382    "user = \'$user\'");
8383
8384  $self->assert(!defined($login),
8385    test_msg("Expected undef, got '$login'"));
8386
8387  $self->assert(!defined($ip_addr),
8388    test_msg("Expected undef, got '$ip_addr'"));
8389
8390  $self->assert(!defined($timestamp),
8391    test_msg("Expected undef, got '$timestamp'"));
8392
8393  ($login, $ip_addr, $timestamp) = get_sessions($logdb_file,
8394    "user = \'$user\'");
8395
8396  my $expected;
8397
8398  $expected = $user;
8399  $self->assert($expected eq $login,
8400    test_msg("Expected '$expected', got '$login'"));
8401
8402  $expected = '127.0.0.1';
8403  $self->assert($expected eq $ip_addr,
8404    test_msg("Expected '$expected', got '$ip_addr'"));
8405
8406  $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}';
8407  $self->assert(qr/$expected/, $timestamp,
8408    test_msg("Expected '$expected', got '$timestamp'"));
8409
8410  unlink($log_file);
8411}
8412
8413sub sql_named_conn_sqllog_exit_bug3645 {
8414  my $self = shift;
8415  my $tmpdir = $self->{tmpdir};
8416
8417  my $config_file = "$tmpdir/sqlite.conf";
8418  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
8419  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
8420
8421  my $log_file = test_get_logfile();
8422
8423  my $user = 'proftpd';
8424  my $passwd = 'test';
8425  my $group = 'ftpd';
8426  my $home_dir = File::Spec->rel2abs($tmpdir);
8427  my $uid = 500;
8428  my $gid = 500;
8429
8430  # Make sure that, if we're running as root, that the home directory has
8431  # permissions/privs set for the account we create
8432  if ($< == 0) {
8433    unless (chmod(0755, $home_dir)) {
8434      die("Can't set perms on $home_dir to 0755: $!");
8435    }
8436
8437    unless (chown($uid, $gid, $home_dir)) {
8438      die("Can't set owner of $home_dir to $uid/$gid: $!");
8439    }
8440  }
8441
8442  my $userdb_file = File::Spec->rel2abs("$tmpdir/proftpd-users.db");
8443
8444  # Build up sqlite3 command to create users, groups tables and populate them
8445  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd-users.sql");
8446
8447  if (open(my $fh, "> $db_script")) {
8448    print $fh <<EOS;
8449CREATE TABLE users (
8450  userid TEXT,
8451  passwd TEXT,
8452  uid INTEGER,
8453  gid INTEGER,
8454  homedir TEXT,
8455  shell TEXT
8456);
8457INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
8458
8459CREATE TABLE groups (
8460  groupname TEXT,
8461  gid INTEGER,
8462  members TEXT
8463);
8464INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
8465EOS
8466
8467    unless (close($fh)) {
8468      die("Can't write $db_script: $!");
8469    }
8470
8471  } else {
8472    die("Can't open $db_script: $!");
8473  }
8474
8475  my $cmd = "sqlite3 $userdb_file < $db_script";
8476  build_db($cmd, $db_script);
8477
8478  my $logdb_file = File::Spec->rel2abs("$tmpdir/proftpd-log.db");
8479
8480  # Build up sqlite3 command to create users, groups tables and populate them
8481  $db_script = File::Spec->rel2abs("$tmpdir/proftpd-log.sql");
8482
8483  if (open(my $fh, "> $db_script")) {
8484    print $fh <<EOS;
8485CREATE TABLE ftpsessions (
8486  user TEXT,
8487  ip_addr TEXT,
8488  timestamp TEXT
8489);
8490
8491EOS
8492
8493    unless (close($fh)) {
8494      die("Can't write $db_script: $!");
8495    }
8496
8497  } else {
8498    die("Can't open $db_script: $!");
8499  }
8500
8501  $cmd = "sqlite3 $logdb_file < $db_script";
8502  build_db($cmd, $db_script);
8503
8504  # Make sure that, if we're running as root, the database file has
8505  # the permissions/privs set for use by proftpd
8506  if ($< == 0) {
8507    unless (chmod(0666, $userdb_file, $logdb_file)) {
8508      die("Can't set perms on $userdb_file, $logdb_file to 0666: $!");
8509    }
8510  }
8511
8512  my $config = {
8513    PidFile => $pid_file,
8514    ScoreboardFile => $scoreboard_file,
8515    SystemLog => $log_file,
8516
8517    DefaultRoot => '~',
8518
8519    IfModules => {
8520      'mod_delay.c' => {
8521        DelayEngine => 'off',
8522      },
8523
8524      'mod_sql.c' => [
8525        'SQLAuthTypes plaintext',
8526        'SQLBackend sqlite3',
8527        "SQLConnectInfo $userdb_file",
8528        "SQLNamedConnectInfo logdb sqlite3 $logdb_file",
8529        "SQLLogFile $log_file",
8530        'SQLNamedQuery session_end FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')" logdb',
8531        'SQLLog EXIT session_end',
8532      ],
8533    },
8534  };
8535
8536  my ($port, $config_user, $config_group) = config_write($config_file, $config);
8537
8538  # Open pipes, for use between the parent and child processes.  Specifically,
8539  # the child will indicate when it's done with its test by writing a message
8540  # to the parent.
8541  my ($rfh, $wfh);
8542  unless (pipe($rfh, $wfh)) {
8543    die("Can't open pipe: $!");
8544  }
8545
8546  my $ex;
8547
8548  # Fork child
8549  $self->handle_sigchld();
8550  defined(my $pid = fork()) or die("Can't fork: $!");
8551  if ($pid) {
8552    eval {
8553      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
8554
8555      $client->login($user, $passwd);
8556
8557      my $resp_msgs = $client->response_msgs();
8558      my $nmsgs = scalar(@$resp_msgs);
8559
8560      my $expected;
8561
8562      $expected = 1;
8563      $self->assert($expected == $nmsgs,
8564        test_msg("Expected $expected, got $nmsgs"));
8565
8566      $expected = "User proftpd logged in";
8567      $self->assert($expected eq $resp_msgs->[0],
8568        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
8569
8570    };
8571
8572    if ($@) {
8573      $ex = $@;
8574    }
8575
8576    $wfh->print("done\n");
8577    $wfh->flush();
8578
8579  } else {
8580    eval { server_wait($config_file, $rfh) };
8581    if ($@) {
8582      warn($@);
8583      exit 1;
8584    }
8585
8586    exit 0;
8587  }
8588
8589  # Stop server
8590  server_stop($pid_file);
8591
8592  $self->assert_child_ok($pid);
8593
8594  if ($ex) {
8595    test_append_logfile($log_file, $ex);
8596    unlink($log_file);
8597
8598    die($ex);
8599  }
8600
8601  my ($login, $ip_addr, $timestamp) = get_sessions($userdb_file,
8602    "user = \'$user\'");
8603
8604  $self->assert(!defined($login),
8605    test_msg("Expected undef, got '$login'"));
8606
8607  $self->assert(!defined($ip_addr),
8608    test_msg("Expected undef, got '$ip_addr'"));
8609
8610  $self->assert(!defined($timestamp),
8611    test_msg("Expected undef, got '$timestamp'"));
8612
8613  ($login, $ip_addr, $timestamp) = get_sessions($logdb_file,
8614    "user = \'$user\'");
8615
8616  my $expected;
8617
8618  $expected = $user;
8619  $self->assert($expected eq $login,
8620    test_msg("Expected '$expected', got '$login'"));
8621
8622  $expected = '127.0.0.1';
8623  $self->assert($expected eq $ip_addr,
8624    test_msg("Expected '$expected', got '$ip_addr'"));
8625
8626  $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}';
8627  $self->assert(qr/$expected/, $timestamp,
8628    test_msg("Expected '$expected', got '$timestamp'"));
8629
8630  unlink($log_file);
8631}
8632
8633sub sql_sqllog_vars_H_L_matching_server_bug3620 {
8634  my $self = shift;
8635  my $tmpdir = $self->{tmpdir};
8636  my $setup = test_setup($tmpdir, 'sqlite');
8637
8638  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
8639
8640  # Build up sqlite3 command to create users, groups tables and populate them
8641  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
8642
8643  if (open(my $fh, "> $db_script")) {
8644    print $fh <<EOS;
8645CREATE TABLE ftpsessions (
8646  user TEXT,
8647  ip_addr TEXT,
8648  rename_from TEXT
8649);
8650EOS
8651
8652    unless (close($fh)) {
8653      die("Can't write $db_script: $!");
8654    }
8655
8656  } else {
8657    die("Can't open $db_script: $!");
8658  }
8659
8660  my $cmd = "sqlite3 $db_file < $db_script";
8661  build_db($cmd, $db_script);
8662
8663  # Make sure that, if we're running as root, the database file has
8664  # the permissions/privs set for use by proftpd
8665  if ($< == 0) {
8666    unless (chmod(0666, $db_file)) {
8667      die("Can't set perms on $db_file to 0666: $!");
8668    }
8669  }
8670
8671  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
8672  if (open(my $fh, "> $src_file")) {
8673    close($fh);
8674
8675  } else {
8676    die("Can't open $src_file: $!");
8677  }
8678
8679  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
8680
8681  my $config = {
8682    PidFile => $setup->{pid_file},
8683    ScoreboardFile => $setup->{scoreboard_file},
8684    SystemLog => $setup->{log_file},
8685    TraceLog => $setup->{log_file},
8686    Trace => 'jot:20 sql:20',
8687
8688    AuthUserFile => $setup->{auth_user_file},
8689    AuthGroupFile => $setup->{auth_group_file},
8690    DefaultServer => 'off',
8691    SocketBindTight => 'off',
8692    Port => '0',
8693
8694    IfModules => {
8695      'mod_delay.c' => {
8696        DelayEngine => 'off',
8697      },
8698    },
8699  };
8700
8701  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
8702    $config);
8703
8704  my $sysaddr = Sys::HostAddr->new();
8705  my $real_addr = $sysaddr->main_ip();
8706  my $real_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
8707  my $vhost_addr = '0.0.0.0';
8708
8709  if (open(my $fh, ">> $setup->{config_file}")) {
8710    print $fh <<EOC;
8711<VirtualHost $vhost_addr>
8712  ServerName "DefaultServer VHost"
8713  Port $real_port
8714  DefaultServer on
8715
8716  AuthUserFile $setup->{auth_user_file}
8717  AuthGroupFile $setup->{auth_group_file}
8718  RequireValidShell off
8719  WtmpLog off
8720
8721  <IfModule mod_sql.c>
8722    SQLEngine log
8723    SQLBackend sqlite3
8724    SQLConnectInfo $db_file
8725    SQLLogFile $setup->{log_file}
8726    SQLNamedQuery session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from) VALUES ('%u', '%L', '%H')"
8727    SQLLog PASS session_start
8728  </IfModule>
8729</VirtualHost>
8730EOC
8731    unless (close($fh)) {
8732      die("Can't write $setup->{config_file}: $!");
8733    }
8734
8735  } else {
8736    die("Can't open $setup->{config_file}: $!");
8737  }
8738
8739  # Open pipes, for use between the parent and child processes.  Specifically,
8740  # the child will indicate when it's done with its test by writing a message
8741  # to the parent.
8742  my ($rfh, $wfh);
8743  unless (pipe($rfh, $wfh)) {
8744    die("Can't open pipe: $!");
8745  }
8746
8747  my $ex;
8748
8749  # Fork child
8750  $self->handle_sigchld();
8751  defined(my $pid = fork()) or die("Can't fork: $!");
8752  if ($pid) {
8753    eval {
8754      my $client = ProFTPD::TestSuite::FTP->new($real_addr, $real_port);
8755      $client->login($setup->{user}, $setup->{passwd});
8756      $client->quit();
8757    };
8758    if ($@) {
8759      $ex = $@;
8760    }
8761
8762    $wfh->print("done\n");
8763    $wfh->flush();
8764
8765  } else {
8766    eval { server_wait($setup->{config_file}, $rfh) };
8767    if ($@) {
8768      warn($@);
8769      exit 1;
8770    }
8771
8772    exit 0;
8773  }
8774
8775  # Stop server
8776  server_stop($setup->{pid_file});
8777  $self->assert_child_ok($pid);
8778
8779  if ($ex) {
8780    test_cleanup($setup->{log_file}, $ex);
8781  }
8782
8783  eval {
8784    my ($login, $ip_addr, $sess_vhost_addr) = get_renames($db_file, "user = \'$setup->{user}\'");
8785
8786    my $expected = $setup->{user};
8787    $self->assert($expected eq $login,
8788      test_msg("Expected '$expected', got '$login'"));
8789
8790    $expected = $real_addr;
8791    $self->assert($expected eq $ip_addr,
8792      test_msg("Expected '$expected', got '$ip_addr'"));
8793
8794    $expected = $vhost_addr;
8795    $self->assert($expected eq $sess_vhost_addr,
8796      test_msg("Expected '$expected', got '$sess_vhost_addr'"));
8797  };
8798  if ($@) {
8799    $ex = $@;
8800  }
8801
8802  test_cleanup($setup->{log_file}, $ex);
8803}
8804
8805sub sql_sqllog_vars_H_L_default_server_bug3620 {
8806  my $self = shift;
8807  my $tmpdir = $self->{tmpdir};
8808  my $setup = test_setup($tmpdir, 'sqlite');
8809
8810  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
8811
8812  # Build up sqlite3 command to create users, groups tables and populate them
8813  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
8814
8815  if (open(my $fh, "> $db_script")) {
8816    print $fh <<EOS;
8817CREATE TABLE ftpsessions (
8818  user TEXT,
8819  ip_addr TEXT,
8820  rename_from TEXT
8821);
8822EOS
8823
8824    unless (close($fh)) {
8825      die("Can't write $db_script: $!");
8826    }
8827
8828  } else {
8829    die("Can't open $db_script: $!");
8830  }
8831
8832  my $cmd = "sqlite3 $db_file < $db_script";
8833  build_db($cmd, $db_script);
8834
8835  # Make sure that, if we're running as root, the database file has
8836  # the permissions/privs set for use by proftpd
8837  if ($< == 0) {
8838    unless (chmod(0666, $db_file)) {
8839      die("Can't set perms on $db_file to 0666: $!");
8840    }
8841  }
8842
8843  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
8844  if (open(my $fh, "> $src_file")) {
8845    close($fh);
8846
8847  } else {
8848    die("Can't open $src_file: $!");
8849  }
8850
8851  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
8852
8853  my $config = {
8854    PidFile => $setup->{pid_file},
8855    ScoreboardFile => $setup->{scoreboard_file},
8856    SystemLog => $setup->{log_file},
8857    TraceLog => $setup->{log_file},
8858    Trace => 'jot:20 sql:20',
8859
8860    AuthUserFile => $setup->{auth_user_file},
8861    AuthGroupFile => $setup->{auth_group_file},
8862
8863    DefaultServer => 'off',
8864    SocketBindTight => 'off',
8865    Port => '0',
8866
8867    IfModules => {
8868      'mod_delay.c' => {
8869        DelayEngine => 'off',
8870      },
8871
8872      'mod_sql.c' => {
8873        SQLEngine => 'log',
8874        SQLBackend => 'sqlite3',
8875        SQLConnectInfo => $db_file,
8876        SQLLogFile => $setup->{log_file},
8877        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from) VALUES (\'%u\', \'%L\', \'%H\')"',
8878        SQLLog => 'PASS session_start',
8879      },
8880    },
8881  };
8882
8883  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
8884    $config);
8885
8886  my $sysaddr = Sys::HostAddr->new();
8887  my $real_addr = $sysaddr->main_ip();
8888  my $real_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
8889  my $vhost_addr = '0.0.0.0';
8890
8891  if (open(my $fh, ">> $setup->{config_file}")) {
8892    print $fh <<EOC;
8893
8894<VirtualHost $vhost_addr>
8895  ServerName "DefaultServer VHost"
8896  Port $real_port
8897  DefaultServer on
8898
8899  AuthUserFile $setup->{auth_user_file}
8900  AuthGroupFile $setup->{auth_group_file}
8901  RequireValidShell off
8902  WtmpLog off
8903
8904  <IfModule mod_sql.c>
8905    SQLEngine log
8906    SQLBackend sqlite3
8907    SQLConnectInfo $db_file
8908    SQLLogFile $setup->{log_file}
8909    SQLNamedQuery session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, rename_from) VALUES (\'%u\', \'%L\', \'%H\')"
8910    SQLLog PASS session_start
8911  </IfModule>
8912</VirtualHost>
8913EOC
8914    unless (close($fh)) {
8915      die("Can't write $setup->{config_file}: $!");
8916    }
8917
8918  } else {
8919    die("Can't open $setup->{config_file}: $!");
8920  }
8921
8922  # Open pipes, for use between the parent and child processes.  Specifically,
8923  # the child will indicate when it's done with its test by writing a message
8924  # to the parent.
8925  my ($rfh, $wfh);
8926  unless (pipe($rfh, $wfh)) {
8927    die("Can't open pipe: $!");
8928  }
8929
8930  my $ex;
8931
8932  # Fork child
8933  $self->handle_sigchld();
8934  defined(my $pid = fork()) or die("Can't fork: $!");
8935  if ($pid) {
8936    eval {
8937      my $client = ProFTPD::TestSuite::FTP->new($real_addr, $real_port);
8938      $client->login($setup->{user}, $setup->{passwd});
8939      $client->quit();
8940    };
8941    if ($@) {
8942      $ex = $@;
8943    }
8944
8945    $wfh->print("done\n");
8946    $wfh->flush();
8947
8948  } else {
8949    eval { server_wait($setup->{config_file}, $rfh) };
8950    if ($@) {
8951      warn($@);
8952      exit 1;
8953    }
8954
8955    exit 0;
8956  }
8957
8958  # Stop server
8959  server_stop($setup->{pid_file});
8960  $self->assert_child_ok($pid);
8961
8962  if ($ex) {
8963    test_cleanup($setup->{log_file}, $ex);
8964  }
8965
8966  eval {
8967    my ($login, $ip_addr, $sess_addr) = get_renames($db_file, "user = \'$setup->{user}\'");
8968
8969    my $expected = $setup->{user};
8970    $self->assert($expected eq $login, "Expected '$expected', got '$login'");
8971
8972    $expected = $real_addr;
8973    $self->assert($expected eq $ip_addr,
8974      "Expected '$expected', got '$ip_addr'");
8975
8976    $expected = $vhost_addr;
8977    $self->assert($expected eq $sess_addr,
8978      "Expected '$expected', got '$sess_addr'");
8979  };
8980  if ($@) {
8981    $ex = $@;
8982  }
8983
8984  test_cleanup($setup->{log_file}, $ex);
8985}
8986
8987sub sql_sqllog_exit_ifuser {
8988  my $self = shift;
8989  my $tmpdir = $self->{tmpdir};
8990
8991  my $config_file = "$tmpdir/sqlite.conf";
8992  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
8993  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
8994
8995  my $log_file = test_get_logfile();
8996
8997  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
8998  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
8999
9000  my $user = 'proftpd';
9001  my $passwd = 'test';
9002  my $group = 'ftpd';
9003  my $home_dir = File::Spec->rel2abs($tmpdir);
9004  my $uid = 500;
9005  my $gid = 500;
9006
9007  # Make sure that, if we're running as root, that the home directory has
9008  # permissions/privs set for the account we create
9009  if ($< == 0) {
9010    unless (chmod(0755, $home_dir)) {
9011      die("Can't set perms on $home_dir to 0755: $!");
9012    }
9013
9014    unless (chown($uid, $gid, $home_dir)) {
9015      die("Can't set owner of $home_dir to $uid/$gid: $!");
9016    }
9017  }
9018
9019  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9020    '/bin/bash');
9021  auth_group_write($auth_group_file, $group, $gid, $user);
9022
9023  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9024
9025  # Build up sqlite3 command to create users, groups tables and populate them
9026  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9027
9028  if (open(my $fh, "> $db_script")) {
9029    print $fh <<EOS;
9030CREATE TABLE ftpsessions (
9031  user TEXT,
9032  ip_addr TEXT
9033);
9034EOS
9035
9036    unless (close($fh)) {
9037      die("Can't write $db_script: $!");
9038    }
9039
9040  } else {
9041    die("Can't open $db_script: $!");
9042  }
9043
9044  my $cmd = "sqlite3 $db_file < $db_script";
9045  build_db($cmd, $db_script);
9046
9047  # Make sure that, if we're running as root, the database file has
9048  # the permissions/privs set for use by proftpd
9049  if ($< == 0) {
9050    unless (chmod(0666, $db_file)) {
9051      die("Can't set perms on $db_file to 0666: $!");
9052    }
9053  }
9054
9055  my $config = {
9056    PidFile => $pid_file,
9057    ScoreboardFile => $scoreboard_file,
9058    SystemLog => $log_file,
9059
9060    AuthUserFile => $auth_user_file,
9061    AuthGroupFile => $auth_group_file,
9062
9063    IfModules => {
9064      'mod_delay.c' => {
9065        DelayEngine => 'off',
9066      },
9067
9068      'mod_sql.c' => {
9069        SQLEngine => 'log',
9070        SQLBackend => 'sqlite3',
9071        SQLConnectInfo => $db_file,
9072        SQLLogFile => $log_file,
9073        SQLNamedQuery => 'logout FREEFORM "INSERT INTO ftpsessions (user, ip_addr) VALUES (\'%u\', \'%L\')"',
9074      },
9075    },
9076  };
9077
9078  my ($port, $config_user, $config_group) = config_write($config_file, $config);
9079
9080  if (open(my $fh, ">> $config_file")) {
9081    print $fh <<EOC;
9082<IfModule mod_ifsession.c>
9083  <IfUser regex .*>
9084    SQLLog EXIT logout
9085  </IfUser>
9086</IfModule>
9087EOC
9088    unless (close($fh)) {
9089      die("Can't write $config_file: $!");
9090    }
9091
9092  } else {
9093    die("Can't open $config_file: $!");
9094  }
9095
9096  # Open pipes, for use between the parent and child processes.  Specifically,
9097  # the child will indicate when it's done with its test by writing a message
9098  # to the parent.
9099  my ($rfh, $wfh);
9100  unless (pipe($rfh, $wfh)) {
9101    die("Can't open pipe: $!");
9102  }
9103
9104  my $ex;
9105
9106  # Fork child
9107  $self->handle_sigchld();
9108  defined(my $pid = fork()) or die("Can't fork: $!");
9109  if ($pid) {
9110    eval {
9111
9112      # First, just connect and quit, without logging in
9113      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9114      $client->quit();
9115
9116      # Then connect, login, and quit
9117      $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9118      $client->login($user, $passwd);
9119      $client->quit();
9120    };
9121
9122    if ($@) {
9123      $ex = $@;
9124    }
9125
9126    $wfh->print("done\n");
9127    $wfh->flush();
9128
9129  } else {
9130    eval { server_wait($config_file, $rfh) };
9131    if ($@) {
9132      warn($@);
9133      exit 1;
9134    }
9135
9136    exit 0;
9137  }
9138
9139  # Stop server
9140  server_stop($pid_file);
9141
9142  $self->assert_child_ok($pid);
9143
9144  if ($ex) {
9145    test_append_logfile($log_file, $ex);
9146    unlink($log_file);
9147
9148    die($ex);
9149  }
9150
9151  my $query = "SELECT user, ip_addr FROM ftpsessions";
9152  $cmd = "sqlite3 $db_file \"$query\"";
9153
9154  if ($ENV{TEST_VERBOSE}) {
9155    print STDERR "Executing sqlite3: $cmd\n";
9156  }
9157
9158  my @res = `$cmd`;
9159  my $res = join('', @res);
9160  chomp($res);
9161
9162  my ($login, $ip_addr) = split(/\|/, $res);
9163
9164  my $expected;
9165
9166  $expected = $user;
9167  $self->assert($expected eq $login,
9168    test_msg("Expected '$expected', got '$login'"));
9169
9170  $expected = '127.0.0.1';
9171  $self->assert($expected eq $ip_addr,
9172    test_msg("Expected '$expected', got '$ip_addr'"));
9173
9174  unlink($log_file);
9175}
9176
9177sub sql_sqllog_exit_ifgroup {
9178  my $self = shift;
9179  my $tmpdir = $self->{tmpdir};
9180
9181  my $config_file = "$tmpdir/sqlite.conf";
9182  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
9183  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
9184
9185  my $log_file = test_get_logfile();
9186
9187  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
9188  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
9189
9190  my $user = 'proftpd';
9191  my $passwd = 'test';
9192  my $group = 'ftpd';
9193  my $home_dir = File::Spec->rel2abs($tmpdir);
9194  my $uid = 500;
9195  my $gid = 500;
9196
9197  # Make sure that, if we're running as root, that the home directory has
9198  # permissions/privs set for the account we create
9199  if ($< == 0) {
9200    unless (chmod(0755, $home_dir)) {
9201      die("Can't set perms on $home_dir to 0755: $!");
9202    }
9203
9204    unless (chown($uid, $gid, $home_dir)) {
9205      die("Can't set owner of $home_dir to $uid/$gid: $!");
9206    }
9207  }
9208
9209  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9210    '/bin/bash');
9211  auth_group_write($auth_group_file, $group, $gid, $user);
9212
9213  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9214
9215  # Build up sqlite3 command to create users, groups tables and populate them
9216  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9217
9218  if (open(my $fh, "> $db_script")) {
9219    print $fh <<EOS;
9220CREATE TABLE ftpsessions (
9221  user TEXT,
9222  ip_addr TEXT
9223);
9224EOS
9225
9226    unless (close($fh)) {
9227      die("Can't write $db_script: $!");
9228    }
9229
9230  } else {
9231    die("Can't open $db_script: $!");
9232  }
9233
9234  my $cmd = "sqlite3 $db_file < $db_script";
9235  build_db($cmd, $db_script);
9236
9237  # Make sure that, if we're running as root, the database file has
9238  # the permissions/privs set for use by proftpd
9239  if ($< == 0) {
9240    unless (chmod(0666, $db_file)) {
9241      die("Can't set perms on $db_file to 0666: $!");
9242    }
9243  }
9244
9245  my $config = {
9246    PidFile => $pid_file,
9247    ScoreboardFile => $scoreboard_file,
9248    SystemLog => $log_file,
9249
9250    AuthUserFile => $auth_user_file,
9251    AuthGroupFile => $auth_group_file,
9252
9253    IfModules => {
9254      'mod_delay.c' => {
9255        DelayEngine => 'off',
9256      },
9257
9258      'mod_sql.c' => {
9259        SQLEngine => 'log',
9260        SQLBackend => 'sqlite3',
9261        SQLConnectInfo => $db_file,
9262        SQLLogFile => $log_file,
9263        SQLNamedQuery => 'logout FREEFORM "INSERT INTO ftpsessions (user, ip_addr) VALUES (\'%u\', \'%L\')"',
9264      },
9265    },
9266  };
9267
9268  my ($port, $config_user, $config_group) = config_write($config_file, $config);
9269
9270  if (open(my $fh, ">> $config_file")) {
9271    print $fh <<EOC;
9272<IfModule mod_ifsession.c>
9273  <IfGroup regex .*>
9274    SQLLog EXIT logout
9275  </IfGroup>
9276</IfModule>
9277EOC
9278    unless (close($fh)) {
9279      die("Can't write $config_file: $!");
9280    }
9281
9282  } else {
9283    die("Can't open $config_file: $!");
9284  }
9285
9286  # Open pipes, for use between the parent and child processes.  Specifically,
9287  # the child will indicate when it's done with its test by writing a message
9288  # to the parent.
9289  my ($rfh, $wfh);
9290  unless (pipe($rfh, $wfh)) {
9291    die("Can't open pipe: $!");
9292  }
9293
9294  my $ex;
9295
9296  # Fork child
9297  $self->handle_sigchld();
9298  defined(my $pid = fork()) or die("Can't fork: $!");
9299  if ($pid) {
9300    eval {
9301
9302      # First, just connect and quit, without logging in
9303      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9304      $client->quit();
9305
9306      # Then connect, login, and quit
9307      $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9308      $client->login($user, $passwd);
9309      $client->quit();
9310    };
9311
9312    if ($@) {
9313      $ex = $@;
9314    }
9315
9316    $wfh->print("done\n");
9317    $wfh->flush();
9318
9319  } else {
9320    eval { server_wait($config_file, $rfh) };
9321    if ($@) {
9322      warn($@);
9323      exit 1;
9324    }
9325
9326    exit 0;
9327  }
9328
9329  # Stop server
9330  server_stop($pid_file);
9331
9332  $self->assert_child_ok($pid);
9333
9334  if ($ex) {
9335    test_append_logfile($log_file, $ex);
9336    unlink($log_file);
9337
9338    die($ex);
9339  }
9340
9341  my $query = "SELECT user, ip_addr FROM ftpsessions";
9342  $cmd = "sqlite3 $db_file \"$query\"";
9343
9344  if ($ENV{TEST_VERBOSE}) {
9345    print STDERR "Executing sqlite3: $cmd\n";
9346  }
9347
9348  my @res = `$cmd`;
9349  my $res = join('', @res);
9350  chomp($res);
9351
9352  my ($login, $ip_addr) = split(/\|/, $res);
9353
9354  my $expected;
9355
9356  $expected = $user;
9357  $self->assert($expected eq $login,
9358    test_msg("Expected '$expected', got '$login'"));
9359
9360  $expected = '127.0.0.1';
9361  $self->assert($expected eq $ip_addr,
9362    test_msg("Expected '$expected', got '$ip_addr'"));
9363
9364  unlink($log_file);
9365}
9366
9367sub sql_sqllog_multi_pass_ifclass_bug4025 {
9368  my $self = shift;
9369  my $tmpdir = $self->{tmpdir};
9370
9371  my $config_file = "$tmpdir/sqlite.conf";
9372  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
9373  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
9374
9375  my $log_file = test_get_logfile();
9376
9377  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
9378  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
9379
9380  my $user = 'proftpd';
9381  my $passwd = 'test';
9382  my $group = 'ftpd';
9383  my $home_dir = File::Spec->rel2abs($tmpdir);
9384  my $uid = 500;
9385  my $gid = 500;
9386
9387  # Make sure that, if we're running as root, that the home directory has
9388  # permissions/privs set for the account we create
9389  if ($< == 0) {
9390    unless (chmod(0755, $home_dir)) {
9391      die("Can't set perms on $home_dir to 0755: $!");
9392    }
9393
9394    unless (chown($uid, $gid, $home_dir)) {
9395      die("Can't set owner of $home_dir to $uid/$gid: $!");
9396    }
9397  }
9398
9399  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9400    '/bin/bash');
9401  auth_group_write($auth_group_file, $group, $gid, $user);
9402
9403  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9404
9405  # Build up sqlite3 command to create users, groups tables and populate them
9406  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9407
9408  if (open(my $fh, "> $db_script")) {
9409    print $fh <<EOS;
9410CREATE TABLE ftpsessions (
9411  user TEXT,
9412  ip_addr TEXT
9413);
9414EOS
9415
9416    unless (close($fh)) {
9417      die("Can't write $db_script: $!");
9418    }
9419
9420  } else {
9421    die("Can't open $db_script: $!");
9422  }
9423
9424  my $cmd = "sqlite3 $db_file < $db_script";
9425  build_db($cmd, $db_script);
9426
9427  # Make sure that, if we're running as root, the database file has
9428  # the permissions/privs set for use by proftpd
9429  if ($< == 0) {
9430    unless (chmod(0666, $db_file)) {
9431      die("Can't set perms on $db_file to 0666: $!");
9432    }
9433  }
9434
9435  my $config = {
9436    PidFile => $pid_file,
9437    ScoreboardFile => $scoreboard_file,
9438    SystemLog => $log_file,
9439    TraceLog => $log_file,
9440    Trace => 'DEFAULT:10 ifsession:20',
9441
9442    AuthUserFile => $auth_user_file,
9443    AuthGroupFile => $auth_group_file,
9444    AuthOrder => 'mod_auth_file.c',
9445
9446    IfModules => {
9447      'mod_delay.c' => {
9448        DelayEngine => 'off',
9449      },
9450
9451      'mod_sql.c' => [
9452        'SQLEngine log',
9453        'SQLBackend sqlite3',
9454        "SQLConnectInfo $db_file",
9455        "SQLLogFile $log_file",
9456        'SQLNamedQuery login_ip FREEFORM "INSERT INTO ftpsessions (ip_addr) VALUES (\'%L\')"',
9457        'SQLNamedQuery login_user FREEFORM "INSERT INTO ftpsessions (user) VALUES (\'%u\')"',
9458      ],
9459    },
9460  };
9461
9462  my ($port, $config_user, $config_group) = config_write($config_file, $config);
9463
9464  if (open(my $fh, ">> $config_file")) {
9465    print $fh <<EOC;
9466<Class test>
9467  From 127.0.0.1
9468</Class>
9469
9470<IfModule mod_ifsession.c>
9471  <IfClass test>
9472    SQLLog PASS login_ip
9473    SQLLog PASS login_user
9474  </IfClass>
9475</IfModule>
9476EOC
9477    unless (close($fh)) {
9478      die("Can't write $config_file: $!");
9479    }
9480
9481  } else {
9482    die("Can't open $config_file: $!");
9483  }
9484
9485  # Open pipes, for use between the parent and child processes.  Specifically,
9486  # the child will indicate when it's done with its test by writing a message
9487  # to the parent.
9488  my ($rfh, $wfh);
9489  unless (pipe($rfh, $wfh)) {
9490    die("Can't open pipe: $!");
9491  }
9492
9493  my $ex;
9494
9495  # Fork child
9496  $self->handle_sigchld();
9497  defined(my $pid = fork()) or die("Can't fork: $!");
9498  if ($pid) {
9499    eval {
9500      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9501      $client->login($user, $passwd);
9502      $client->quit();
9503    };
9504
9505    if ($@) {
9506      $ex = $@;
9507    }
9508
9509    $wfh->print("done\n");
9510    $wfh->flush();
9511
9512  } else {
9513    eval { server_wait($config_file, $rfh) };
9514    if ($@) {
9515      warn($@);
9516      exit 1;
9517    }
9518
9519    exit 0;
9520  }
9521
9522  # Stop server
9523  server_stop($pid_file);
9524
9525  $self->assert_child_ok($pid);
9526
9527  if ($ex) {
9528    test_append_logfile($log_file, $ex);
9529    unlink($log_file);
9530
9531    die($ex);
9532  }
9533
9534  my $query = "SELECT user, ip_addr FROM ftpsessions";
9535  $cmd = "sqlite3 $db_file \"$query\"";
9536
9537  if ($ENV{TEST_VERBOSE}) {
9538    print STDERR "Executing sqlite3: $cmd\n";
9539  }
9540
9541  my @res = `$cmd`;
9542  my $res = join('', @res);
9543  $res =~ s/\n//g;
9544  chomp($res);
9545
9546  my ($login, $ip_addr) = split(/\|+/, $res);
9547
9548  my $expected;
9549
9550  $expected = $user;
9551  $self->assert($expected eq $login,
9552    test_msg("Expected '$expected', got '$login'"));
9553
9554  $expected = '127.0.0.1';
9555  $self->assert($expected eq $ip_addr,
9556    test_msg("Expected '$expected', got '$ip_addr'"));
9557
9558  unlink($log_file);
9559}
9560
9561sub sql_opt_no_disconnect_on_error_with_extlog_bug3633 {
9562  my $self = shift;
9563  my $tmpdir = $self->{tmpdir};
9564
9565  my $config_file = "$tmpdir/sqlite.conf";
9566  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
9567  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
9568
9569  my $log_file = test_get_logfile();
9570
9571  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
9572  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
9573
9574  my $user = 'proftpd';
9575  my $passwd = 'test';
9576  my $group = 'ftpd';
9577  my $home_dir = File::Spec->rel2abs($tmpdir);
9578  my $uid = 500;
9579  my $gid = 500;
9580
9581  # Make sure that, if we're running as root, that the home directory has
9582  # permissions/privs set for the account we create
9583  if ($< == 0) {
9584    unless (chmod(0755, $home_dir)) {
9585      die("Can't set perms on $home_dir to 0755: $!");
9586    }
9587
9588    unless (chown($uid, $gid, $home_dir)) {
9589      die("Can't set owner of $home_dir to $uid/$gid: $!");
9590    }
9591  }
9592
9593  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9594    '/bin/bash');
9595  auth_group_write($auth_group_file, $group, $gid, $user);
9596
9597  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9598
9599  # Build up sqlite3 command to create users, groups tables and populate them
9600  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9601
9602  if (open(my $fh, "> $db_script")) {
9603    print $fh <<EOS;
9604CREATE TABLE ftpsessions (
9605  user TEXT,
9606  ip_addr TEXT,
9607  timestamp TEXT
9608);
9609EOS
9610
9611    unless (close($fh)) {
9612      die("Can't write $db_script: $!");
9613    }
9614
9615  } else {
9616    die("Can't open $db_script: $!");
9617  }
9618
9619  my $cmd = "sqlite3 $db_file < $db_script";
9620  build_db($cmd, $db_script);
9621
9622  # Make sure that, if we're running as root, the database file has
9623  # the permissions/privs set for use by proftpd
9624  if ($< == 0) {
9625    unless (chmod(0666, $db_file)) {
9626      die("Can't set perms on $db_file to 0666: $!");
9627    }
9628  }
9629
9630  my $test_file = File::Spec->rel2abs("$home_dir/test.txt");
9631  if (open(my $fh, "> $test_file")) {
9632    print $fh "Hello, World!\n";
9633    unless (close($fh)) {
9634      die("Can't write $test_file: $!");
9635    }
9636
9637  } else {
9638    die("Can't open $test_file: $!");
9639  }
9640
9641  my $ext_log = File::Spec->rel2abs("$tmpdir/custom.log");
9642
9643  my $config = {
9644    PidFile => $pid_file,
9645    ScoreboardFile => $scoreboard_file,
9646    SystemLog => $log_file,
9647    TraceLog => $log_file,
9648    Trace => 'DEFAULT:10 sql:20',
9649
9650    AuthUserFile => $auth_user_file,
9651    AuthGroupFile => $auth_group_file,
9652
9653    LogFormat => 'custom "%m %f"',
9654    ExtendedLog => "$ext_log ALL custom",
9655
9656    IfModules => {
9657      'mod_delay.c' => {
9658        DelayEngine => 'off',
9659      },
9660
9661      'mod_sql.c' => {
9662        SQLEngine => 'log',
9663        SQLBackend => 'sqlite3',
9664        SQLConnectInfo => $db_file,
9665        SQLLogFile => $log_file,
9666        SQLNamedQuery => 'log_dele FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
9667        SQLLog => 'DELE log_dele',
9668
9669        SQLOptions => 'noDisconnectOnError',
9670      },
9671    },
9672  };
9673
9674  my ($port, $config_user, $config_group) = config_write($config_file, $config);
9675
9676  # Open pipes, for use between the parent and child processes.  Specifically,
9677  # the child will indicate when it's done with its test by writing a message
9678  # to the parent.
9679  my ($rfh, $wfh);
9680  unless (pipe($rfh, $wfh)) {
9681    die("Can't open pipe: $!");
9682  }
9683
9684  my $ex;
9685
9686  # Fork child
9687  $self->handle_sigchld();
9688  defined(my $pid = fork()) or die("Can't fork: $!");
9689  if ($pid) {
9690    eval {
9691      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9692
9693      $client->login($user, $passwd);
9694      $client->dele('test.txt');
9695      $client->quit();
9696    };
9697
9698    if ($@) {
9699      $ex = $@;
9700    }
9701
9702    $wfh->print("done\n");
9703    $wfh->flush();
9704
9705  } else {
9706    eval { server_wait($config_file, $rfh) };
9707    if ($@) {
9708      warn($@);
9709      exit 1;
9710    }
9711
9712    exit 0;
9713  }
9714
9715  # Stop server
9716  server_stop($pid_file);
9717
9718  $self->assert_child_ok($pid);
9719
9720  if ($ex) {
9721    test_append_logfile($log_file, $ex);
9722    unlink($log_file);
9723
9724    die($ex);
9725  }
9726
9727  # Now, read in the ExtendedLog, and see whether the %f variable was
9728  # properly written out.
9729  if (open(my $fh, "< $ext_log")) {
9730    my $ok = 0;
9731
9732    while (my $line = <$fh>) {
9733      chomp($line);
9734
9735      # We're only interested in the DELE log line
9736      unless ($line =~ /^DELE (.*)$/i) {
9737        next;
9738      }
9739
9740      my $name = $1;
9741
9742      if ($^O eq 'darwin') {
9743        # MacOSX-specific hack
9744        $test_file = '/private' . $test_file;
9745      }
9746
9747      my $expected = $test_file;
9748      $self->assert($expected eq $name,
9749        test_msg("Expected '$expected', got '$name'"));
9750
9751      $ok = 1;
9752    }
9753
9754    close($fh);
9755
9756    $self->assert($ok, test_msg("Expected ExtendedLog messages not found"));
9757
9758  } else {
9759    die("Can't read $ext_log: $!");
9760  }
9761
9762  unlink($log_file);
9763}
9764
9765sub sql_sqllog_ignore_errors_bad_table_bug3692 {
9766  my $self = shift;
9767  my $tmpdir = $self->{tmpdir};
9768
9769  my $config_file = "$tmpdir/sqlite.conf";
9770  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
9771  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
9772
9773  my $log_file = test_get_logfile();
9774
9775  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
9776  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
9777
9778  my $user = 'proftpd';
9779  my $passwd = 'test';
9780  my $group = 'ftpd';
9781  my $home_dir = File::Spec->rel2abs($tmpdir);
9782  my $uid = 500;
9783  my $gid = 500;
9784
9785  # Make sure that, if we're running as root, that the home directory has
9786  # permissions/privs set for the account we create
9787  if ($< == 0) {
9788    unless (chmod(0755, $home_dir)) {
9789      die("Can't set perms on $home_dir to 0755: $!");
9790    }
9791
9792    unless (chown($uid, $gid, $home_dir)) {
9793      die("Can't set owner of $home_dir to $uid/$gid: $!");
9794    }
9795  }
9796
9797  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9798    '/bin/bash');
9799  auth_group_write($auth_group_file, $group, $gid, $user);
9800
9801  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9802
9803  # Build up sqlite3 command to create users, groups tables and populate them
9804  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9805
9806  if (open(my $fh, "> $db_script")) {
9807    print $fh <<EOS;
9808CREATE TABLE ftpsessions (
9809  user TEXT,
9810  ip_addr TEXT,
9811  timestamp TEXT
9812);
9813EOS
9814
9815    unless (close($fh)) {
9816      die("Can't write $db_script: $!");
9817    }
9818
9819  } else {
9820    die("Can't open $db_script: $!");
9821  }
9822
9823  my $cmd = "sqlite3 $db_file < $db_script";
9824  build_db($cmd, $db_script);
9825
9826  # Make sure that, if we're running as root, the database file has
9827  # the permissions/privs set for use by proftpd
9828  if ($< == 0) {
9829    unless (chmod(0666, $db_file)) {
9830      die("Can't set perms on $db_file to 0666: $!");
9831    }
9832  }
9833
9834  my $config = {
9835    PidFile => $pid_file,
9836    ScoreboardFile => $scoreboard_file,
9837    SystemLog => $log_file,
9838
9839    AuthUserFile => $auth_user_file,
9840    AuthGroupFile => $auth_group_file,
9841
9842    IfModules => {
9843      'mod_delay.c' => {
9844        DelayEngine => 'off',
9845      },
9846
9847      'mod_sql.c' => {
9848        SQLEngine => 'log',
9849        SQLBackend => 'sqlite3',
9850        SQLConnectInfo => $db_file,
9851        SQLLogFile => $log_file,
9852        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
9853        SQLLog => 'PASS session_start IGNORE_ERRORS',
9854      },
9855    },
9856  };
9857
9858  my ($port, $config_user, $config_group) = config_write($config_file, $config);
9859
9860  # Open pipes, for use between the parent and child processes.  Specifically,
9861  # the child will indicate when it's done with its test by writing a message
9862  # to the parent.
9863  my ($rfh, $wfh);
9864  unless (pipe($rfh, $wfh)) {
9865    die("Can't open pipe: $!");
9866  }
9867
9868  my $ex;
9869
9870  # Fork child
9871  $self->handle_sigchld();
9872  defined(my $pid = fork()) or die("Can't fork: $!");
9873  if ($pid) {
9874    eval {
9875      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
9876
9877      $client->login($user, $passwd);
9878    };
9879
9880    if ($@) {
9881      $ex = $@;
9882    }
9883
9884    $wfh->print("done\n");
9885    $wfh->flush();
9886
9887  } else {
9888    eval { server_wait($config_file, $rfh) };
9889    if ($@) {
9890      warn($@);
9891      exit 1;
9892    }
9893
9894    exit 0;
9895  }
9896
9897  # Stop server
9898  server_stop($pid_file);
9899
9900  $self->assert_child_ok($pid);
9901
9902  if ($ex) {
9903    test_append_logfile($log_file, $ex);
9904    unlink($log_file);
9905
9906    die($ex);
9907  }
9908
9909  unlink($log_file);
9910}
9911
9912sub sql_sqllog_ignore_errors_bad_db_bug3692 {
9913  my $self = shift;
9914  my $tmpdir = $self->{tmpdir};
9915
9916  my $config_file = "$tmpdir/sqlite.conf";
9917  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
9918  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
9919
9920  my $log_file = test_get_logfile();
9921
9922  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
9923  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
9924
9925  my $user = 'proftpd';
9926  my $passwd = 'test';
9927  my $group = 'ftpd';
9928  my $home_dir = File::Spec->rel2abs($tmpdir);
9929  my $uid = 500;
9930  my $gid = 500;
9931
9932  # Make sure that, if we're running as root, that the home directory has
9933  # permissions/privs set for the account we create
9934  if ($< == 0) {
9935    unless (chmod(0755, $home_dir)) {
9936      die("Can't set perms on $home_dir to 0755: $!");
9937    }
9938
9939    unless (chown($uid, $gid, $home_dir)) {
9940      die("Can't set owner of $home_dir to $uid/$gid: $!");
9941    }
9942  }
9943
9944  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
9945    '/bin/bash');
9946  auth_group_write($auth_group_file, $group, $gid, $user);
9947
9948  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
9949
9950  # Build up sqlite3 command to create users, groups tables and populate them
9951  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
9952
9953  if (open(my $fh, "> $db_script")) {
9954    print $fh <<EOS;
9955CREATE TABLE ftpsessions (
9956  user TEXT,
9957  ip_addr TEXT,
9958  timestamp TEXT
9959);
9960EOS
9961
9962    unless (close($fh)) {
9963      die("Can't write $db_script: $!");
9964    }
9965
9966  } else {
9967    die("Can't open $db_script: $!");
9968  }
9969
9970  my $cmd = "sqlite3 $db_file < $db_script";
9971  build_db($cmd, $db_script);
9972
9973  # Make sure that, if we're running as root, the database file has
9974  # the permissions/privs set for use by proftpd
9975  if ($< == 0) {
9976    unless (chmod(0666, $db_file)) {
9977      die("Can't set perms on $db_file to 0666: $!");
9978    }
9979  }
9980
9981  my $config = {
9982    PidFile => $pid_file,
9983    ScoreboardFile => $scoreboard_file,
9984    SystemLog => $log_file,
9985
9986    AuthUserFile => $auth_user_file,
9987    AuthGroupFile => $auth_group_file,
9988
9989    IfModules => {
9990      'mod_delay.c' => {
9991        DelayEngine => 'off',
9992      },
9993
9994      'mod_sql.c' => {
9995        SQLEngine => 'log',
9996        SQLBackend => 'sqlite3',
9997        SQLConnectInfo => "/foobar/$db_file",
9998        SQLLogFile => $log_file,
9999        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
10000        SQLLog => 'PASS session_start IGNORE_ERRORS',
10001      },
10002    },
10003  };
10004
10005  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10006
10007  # Open pipes, for use between the parent and child processes.  Specifically,
10008  # the child will indicate when it's done with its test by writing a message
10009  # to the parent.
10010  my ($rfh, $wfh);
10011  unless (pipe($rfh, $wfh)) {
10012    die("Can't open pipe: $!");
10013  }
10014
10015  my $ex;
10016
10017  # Fork child
10018  $self->handle_sigchld();
10019  defined(my $pid = fork()) or die("Can't fork: $!");
10020  if ($pid) {
10021    eval {
10022      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10023
10024      $client->login($user, $passwd);
10025    };
10026
10027    if ($@) {
10028      $ex = $@;
10029    }
10030
10031    $wfh->print("done\n");
10032    $wfh->flush();
10033
10034  } else {
10035    eval { server_wait($config_file, $rfh) };
10036    if ($@) {
10037      warn($@);
10038      exit 1;
10039    }
10040
10041    exit 0;
10042  }
10043
10044  # Stop server
10045  server_stop($pid_file);
10046
10047  $self->assert_child_ok($pid);
10048
10049  if ($ex) {
10050    test_append_logfile($log_file, $ex);
10051    unlink($log_file);
10052
10053    die($ex);
10054  }
10055
10056  unlink($log_file);
10057}
10058
10059sub get_xfer_status {
10060  my $db_file = shift;
10061  my $where = shift;
10062
10063  my $sql = "SELECT user, ip_addr, xfer_status, xfer_path FROM ftpsessions";
10064  if ($where) {
10065    $sql .= " WHERE $where";
10066  }
10067  $sql .= " LIMIT 1";
10068
10069  my $cmd = "sqlite3 $db_file \"$sql\"";
10070
10071  if ($ENV{TEST_VERBOSE}) {
10072    print STDERR "Executing sqlite3: $cmd\n";
10073  }
10074
10075  my $res = join('', `$cmd`);
10076  chomp($res);
10077
10078  # The default sqlite3 delimiter is '|'
10079  return map { chomp($_); $_; } split(/\|/, $res);
10080}
10081
10082sub sql_sqllog_var_xfer_status_nonxfer {
10083  my $self = shift;
10084  my $tmpdir = $self->{tmpdir};
10085
10086  my $config_file = "$tmpdir/sqlite.conf";
10087  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
10088  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
10089
10090  my $log_file = test_get_logfile();
10091
10092  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
10093  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
10094
10095  my $user = 'proftpd';
10096  my $passwd = 'test';
10097  my $group = 'ftpd';
10098  my $home_dir = File::Spec->rel2abs($tmpdir);
10099  my $uid = 500;
10100  my $gid = 500;
10101
10102  # Make sure that, if we're running as root, that the home directory has
10103  # permissions/privs set for the account we create
10104  if ($< == 0) {
10105    unless (chmod(0755, $home_dir)) {
10106      die("Can't set perms on $home_dir to 0755: $!");
10107    }
10108
10109    unless (chown($uid, $gid, $home_dir)) {
10110      die("Can't set owner of $home_dir to $uid/$gid: $!");
10111    }
10112  }
10113
10114  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
10115    '/bin/bash');
10116  auth_group_write($auth_group_file, $group, $gid, $user);
10117
10118  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
10119
10120  # Build up sqlite3 command to create users, groups tables and populate them
10121  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
10122
10123  if (open(my $fh, "> $db_script")) {
10124    print $fh <<EOS;
10125CREATE TABLE ftpsessions (
10126  user TEXT,
10127  ip_addr TEXT,
10128  xfer_status TEXT,
10129  xfer_path TEXT
10130);
10131EOS
10132
10133    unless (close($fh)) {
10134      die("Can't write $db_script: $!");
10135    }
10136
10137  } else {
10138    die("Can't open $db_script: $!");
10139  }
10140
10141  my $cmd = "sqlite3 $db_file < $db_script";
10142  build_db($cmd, $db_script);
10143
10144  # Make sure that, if we're running as root, the database file has
10145  # the permissions/privs set for use by proftpd
10146  if ($< == 0) {
10147    unless (chmod(0666, $db_file)) {
10148      die("Can't set perms on $db_file to 0666: $!");
10149    }
10150  }
10151
10152  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
10153  if (open(my $fh, "> $test_file")) {
10154    close($fh);
10155
10156  } else {
10157    die("Can't open $test_file: $!");
10158  }
10159
10160  my $config = {
10161    PidFile => $pid_file,
10162    ScoreboardFile => $scoreboard_file,
10163    SystemLog => $log_file,
10164    TraceLog => $log_file,
10165    Trace => 'sql:20 command:20 netio:20',
10166
10167    AuthUserFile => $auth_user_file,
10168    AuthGroupFile => $auth_group_file,
10169
10170    IfModules => {
10171      'mod_delay.c' => {
10172        DelayEngine => 'off',
10173      },
10174
10175      'mod_sql.c' => {
10176        SQLEngine => 'log',
10177        SQLBackend => 'sqlite3',
10178        SQLConnectInfo => $db_file,
10179        SQLLogFile => $log_file,
10180        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
10181        SQLLog => 'PWD xfer_status',
10182      },
10183    },
10184  };
10185
10186  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10187
10188  # Open pipes, for use between the parent and child processes.  Specifically,
10189  # the child will indicate when it's done with its test by writing a message
10190  # to the parent.
10191  my ($rfh, $wfh);
10192  unless (pipe($rfh, $wfh)) {
10193    die("Can't open pipe: $!");
10194  }
10195
10196  my $ex;
10197
10198  # Fork child
10199  $self->handle_sigchld();
10200  defined(my $pid = fork()) or die("Can't fork: $!");
10201  if ($pid) {
10202    eval {
10203      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10204      $client->login($user, $passwd);
10205      $client->pwd();
10206      $client->quit();
10207    };
10208
10209    if ($@) {
10210      $ex = $@;
10211    }
10212
10213    $wfh->print("done\n");
10214    $wfh->flush();
10215
10216  } else {
10217    eval { server_wait($config_file, $rfh) };
10218    if ($@) {
10219      warn($@);
10220      exit 1;
10221    }
10222
10223    exit 0;
10224  }
10225
10226  # Stop server
10227  server_stop($pid_file);
10228
10229  $self->assert_child_ok($pid);
10230
10231  if ($ex) {
10232    test_append_logfile($log_file, $ex);
10233    unlink($log_file);
10234
10235    die($ex);
10236  }
10237
10238  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
10239
10240  my $expected;
10241
10242  $expected = $user;
10243  $self->assert($expected eq $login,
10244    test_msg("Expected user '$expected', got '$login'"));
10245
10246  $expected = '127.0.0.1';
10247  $self->assert($expected eq $ip_addr,
10248    test_msg("Expected IP address '$expected', got '$ip_addr'"));
10249
10250  $expected = '-';
10251  $self->assert($expected eq $xfer_status,
10252    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
10253
10254  if ($^O eq 'darwin') {
10255    # MacOSX-specific hack
10256    $home_dir = '/private' . $home_dir;
10257  }
10258
10259  $expected = $home_dir;
10260  $self->assert($expected eq $xfer_path,
10261    test_msg("Expected file path '$expected', got '$xfer_path'"));
10262
10263  unlink($log_file);
10264}
10265
10266sub sql_sqllog_var_xfer_status_success_download {
10267  my $self = shift;
10268  my $tmpdir = $self->{tmpdir};
10269
10270  my $config_file = "$tmpdir/sqlite.conf";
10271  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
10272  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
10273
10274  my $log_file = test_get_logfile();
10275
10276  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
10277  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
10278
10279  my $user = 'proftpd';
10280  my $passwd = 'test';
10281  my $group = 'ftpd';
10282  my $home_dir = File::Spec->rel2abs($tmpdir);
10283  my $uid = 500;
10284  my $gid = 500;
10285
10286  # Make sure that, if we're running as root, that the home directory has
10287  # permissions/privs set for the account we create
10288  if ($< == 0) {
10289    unless (chmod(0755, $home_dir)) {
10290      die("Can't set perms on $home_dir to 0755: $!");
10291    }
10292
10293    unless (chown($uid, $gid, $home_dir)) {
10294      die("Can't set owner of $home_dir to $uid/$gid: $!");
10295    }
10296  }
10297
10298  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
10299    '/bin/bash');
10300  auth_group_write($auth_group_file, $group, $gid, $user);
10301
10302  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
10303
10304  # Build up sqlite3 command to create users, groups tables and populate them
10305  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
10306
10307  if (open(my $fh, "> $db_script")) {
10308    print $fh <<EOS;
10309CREATE TABLE ftpsessions (
10310  user TEXT,
10311  ip_addr TEXT,
10312  xfer_status TEXT,
10313  xfer_path TEXT
10314);
10315EOS
10316
10317    unless (close($fh)) {
10318      die("Can't write $db_script: $!");
10319    }
10320
10321  } else {
10322    die("Can't open $db_script: $!");
10323  }
10324
10325  my $cmd = "sqlite3 $db_file < $db_script";
10326  build_db($cmd, $db_script);
10327
10328  # Make sure that, if we're running as root, the database file has
10329  # the permissions/privs set for use by proftpd
10330  if ($< == 0) {
10331    unless (chmod(0666, $db_file)) {
10332      die("Can't set perms on $db_file to 0666: $!");
10333    }
10334  }
10335
10336  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
10337  if (open(my $fh, "> $test_file")) {
10338    close($fh);
10339
10340  } else {
10341    die("Can't open $test_file: $!");
10342  }
10343
10344  my $config = {
10345    PidFile => $pid_file,
10346    ScoreboardFile => $scoreboard_file,
10347    SystemLog => $log_file,
10348    TraceLog => $log_file,
10349    Trace => 'sql:20 command:20 netio:20',
10350
10351    AuthUserFile => $auth_user_file,
10352    AuthGroupFile => $auth_group_file,
10353
10354    IfModules => {
10355      'mod_delay.c' => {
10356        DelayEngine => 'off',
10357      },
10358
10359      'mod_sql.c' => {
10360        SQLEngine => 'log',
10361        SQLBackend => 'sqlite3',
10362        SQLConnectInfo => $db_file,
10363        SQLLogFile => $log_file,
10364        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
10365        SQLLog => 'ERR_RETR,RETR xfer_status',
10366      },
10367    },
10368  };
10369
10370  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10371
10372  # Open pipes, for use between the parent and child processes.  Specifically,
10373  # the child will indicate when it's done with its test by writing a message
10374  # to the parent.
10375  my ($rfh, $wfh);
10376  unless (pipe($rfh, $wfh)) {
10377    die("Can't open pipe: $!");
10378  }
10379
10380  my $ex;
10381
10382  # Fork child
10383  $self->handle_sigchld();
10384  defined(my $pid = fork()) or die("Can't fork: $!");
10385  if ($pid) {
10386    eval {
10387      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10388      $client->login($user, $passwd);
10389
10390      my $conn = $client->retr_raw('test.txt');
10391      unless ($conn) {
10392        die("RETR test.txt failed: " . $client->response_code() . " " .
10393          $client->response_msg());
10394      }
10395
10396      my $buf;
10397      $conn->read($buf, 8192, 25);
10398      eval { $conn->close() };
10399
10400      my $resp_code = $client->response_code();
10401      my $resp_msg = $client->response_msg();
10402
10403      $self->assert_transfer_ok($resp_code, $resp_msg);
10404
10405      $client->quit();
10406    };
10407
10408    if ($@) {
10409      $ex = $@;
10410    }
10411
10412    $wfh->print("done\n");
10413    $wfh->flush();
10414
10415  } else {
10416    eval { server_wait($config_file, $rfh) };
10417    if ($@) {
10418      warn($@);
10419      exit 1;
10420    }
10421
10422    exit 0;
10423  }
10424
10425  # Stop server
10426  server_stop($pid_file);
10427
10428  $self->assert_child_ok($pid);
10429
10430  if ($ex) {
10431    test_append_logfile($log_file, $ex);
10432    unlink($log_file);
10433
10434    die($ex);
10435  }
10436
10437  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
10438
10439  my $expected;
10440
10441  $expected = $user;
10442  $self->assert($expected eq $login,
10443    test_msg("Expected user '$expected', got '$login'"));
10444
10445  $expected = '127.0.0.1';
10446  $self->assert($expected eq $ip_addr,
10447    test_msg("Expected IP address '$expected', got '$ip_addr'"));
10448
10449  $expected = 'success';
10450  $self->assert($expected eq $xfer_status,
10451    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
10452
10453  if ($^O eq 'darwin') {
10454    # MacOSX-specific hack
10455    $test_file = '/private' . $test_file;
10456  }
10457
10458  $expected = $test_file;
10459  $self->assert($expected eq $xfer_path,
10460    test_msg("Expected file path '$expected', got '$xfer_path'"));
10461
10462  unlink($log_file);
10463}
10464
10465sub sql_sqllog_var_xfer_status_success_upload {
10466  my $self = shift;
10467  my $tmpdir = $self->{tmpdir};
10468
10469  my $config_file = "$tmpdir/sqlite.conf";
10470  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
10471  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
10472
10473  my $log_file = test_get_logfile();
10474
10475  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
10476  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
10477
10478  my $user = 'proftpd';
10479  my $passwd = 'test';
10480  my $group = 'ftpd';
10481  my $home_dir = File::Spec->rel2abs($tmpdir);
10482  my $uid = 500;
10483  my $gid = 500;
10484
10485  # Make sure that, if we're running as root, that the home directory has
10486  # permissions/privs set for the account we create
10487  if ($< == 0) {
10488    unless (chmod(0755, $home_dir)) {
10489      die("Can't set perms on $home_dir to 0755: $!");
10490    }
10491
10492    unless (chown($uid, $gid, $home_dir)) {
10493      die("Can't set owner of $home_dir to $uid/$gid: $!");
10494    }
10495  }
10496
10497  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
10498    '/bin/bash');
10499  auth_group_write($auth_group_file, $group, $gid, $user);
10500
10501  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
10502
10503  # Build up sqlite3 command to create users, groups tables and populate them
10504  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
10505
10506  if (open(my $fh, "> $db_script")) {
10507    print $fh <<EOS;
10508CREATE TABLE ftpsessions (
10509  user TEXT,
10510  ip_addr TEXT,
10511  xfer_status TEXT,
10512  xfer_path TEXT
10513);
10514EOS
10515
10516    unless (close($fh)) {
10517      die("Can't write $db_script: $!");
10518    }
10519
10520  } else {
10521    die("Can't open $db_script: $!");
10522  }
10523
10524  my $cmd = "sqlite3 $db_file < $db_script";
10525  build_db($cmd, $db_script);
10526
10527  # Make sure that, if we're running as root, the database file has
10528  # the permissions/privs set for use by proftpd
10529  if ($< == 0) {
10530    unless (chmod(0666, $db_file)) {
10531      die("Can't set perms on $db_file to 0666: $!");
10532    }
10533  }
10534
10535  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
10536
10537  my $config = {
10538    PidFile => $pid_file,
10539    ScoreboardFile => $scoreboard_file,
10540    SystemLog => $log_file,
10541    TraceLog => $log_file,
10542    Trace => 'sql:20 command:20 netio:20',
10543
10544    AuthUserFile => $auth_user_file,
10545    AuthGroupFile => $auth_group_file,
10546
10547    IfModules => {
10548      'mod_delay.c' => {
10549        DelayEngine => 'off',
10550      },
10551
10552      'mod_sql.c' => {
10553        SQLEngine => 'log',
10554        SQLBackend => 'sqlite3',
10555        SQLConnectInfo => $db_file,
10556        SQLLogFile => $log_file,
10557        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
10558        SQLLog => 'ERR_STOR,STOR xfer_status',
10559      },
10560    },
10561  };
10562
10563  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10564
10565  # Open pipes, for use between the parent and child processes.  Specifically,
10566  # the child will indicate when it's done with its test by writing a message
10567  # to the parent.
10568  my ($rfh, $wfh);
10569  unless (pipe($rfh, $wfh)) {
10570    die("Can't open pipe: $!");
10571  }
10572
10573  my $ex;
10574
10575  # Fork child
10576  $self->handle_sigchld();
10577  defined(my $pid = fork()) or die("Can't fork: $!");
10578  if ($pid) {
10579    eval {
10580      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10581      $client->login($user, $passwd);
10582
10583      my $conn = $client->stor_raw('test.txt');
10584      unless ($conn) {
10585        die("STOR test.txt failed: " . $client->response_code() . " " .
10586          $client->response_msg());
10587      }
10588
10589      my $buf = "Hello, World!\n";
10590      $conn->write($buf, length($buf), 25);
10591      eval { $conn->close() };
10592
10593      my $resp_code = $client->response_code();
10594      my $resp_msg = $client->response_msg();
10595
10596      $self->assert_transfer_ok($resp_code, $resp_msg);
10597
10598      $client->quit();
10599    };
10600
10601    if ($@) {
10602      $ex = $@;
10603    }
10604
10605    $wfh->print("done\n");
10606    $wfh->flush();
10607
10608  } else {
10609    eval { server_wait($config_file, $rfh) };
10610    if ($@) {
10611      warn($@);
10612      exit 1;
10613    }
10614
10615    exit 0;
10616  }
10617
10618  # Stop server
10619  server_stop($pid_file);
10620
10621  $self->assert_child_ok($pid);
10622
10623  if ($ex) {
10624    test_append_logfile($log_file, $ex);
10625    unlink($log_file);
10626
10627    die($ex);
10628  }
10629
10630  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
10631
10632  my $expected;
10633
10634  $expected = $user;
10635  $self->assert($expected eq $login,
10636    test_msg("Expected user '$expected', got '$login'"));
10637
10638  $expected = '127.0.0.1';
10639  $self->assert($expected eq $ip_addr,
10640    test_msg("Expected IP address '$expected', got '$ip_addr'"));
10641
10642  $expected = 'success';
10643  $self->assert($expected eq $xfer_status,
10644    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
10645
10646  if ($^O eq 'darwin') {
10647    # MacOSX-specific hack
10648    $test_file = '/private' . $test_file;
10649  }
10650
10651  $expected = $test_file;
10652  $self->assert($expected eq $xfer_path,
10653    test_msg("Expected file path '$expected', got '$xfer_path'"));
10654
10655  unlink($log_file);
10656}
10657
10658sub sql_sqllog_var_xfer_status_cancelled {
10659  my $self = shift;
10660  my $tmpdir = $self->{tmpdir};
10661
10662  my $config_file = "$tmpdir/sqlite.conf";
10663  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
10664  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
10665
10666  my $log_file = test_get_logfile();
10667
10668  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
10669  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
10670
10671  my $user = 'proftpd';
10672  my $passwd = 'test';
10673  my $group = 'ftpd';
10674  my $home_dir = File::Spec->rel2abs($tmpdir);
10675  my $uid = 500;
10676  my $gid = 500;
10677
10678  # Make sure that, if we're running as root, that the home directory has
10679  # permissions/privs set for the account we create
10680  if ($< == 0) {
10681    unless (chmod(0755, $home_dir)) {
10682      die("Can't set perms on $home_dir to 0755: $!");
10683    }
10684
10685    unless (chown($uid, $gid, $home_dir)) {
10686      die("Can't set owner of $home_dir to $uid/$gid: $!");
10687    }
10688  }
10689
10690  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
10691    '/bin/bash');
10692  auth_group_write($auth_group_file, $group, $gid, $user);
10693
10694  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
10695
10696  # Build up sqlite3 command to create users, groups tables and populate them
10697  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
10698
10699  if (open(my $fh, "> $db_script")) {
10700    print $fh <<EOS;
10701CREATE TABLE ftpsessions (
10702  user TEXT,
10703  ip_addr TEXT,
10704  xfer_status TEXT,
10705  xfer_path TEXT
10706);
10707EOS
10708
10709    unless (close($fh)) {
10710      die("Can't write $db_script: $!");
10711    }
10712
10713  } else {
10714    die("Can't open $db_script: $!");
10715  }
10716
10717  my $cmd = "sqlite3 $db_file < $db_script";
10718  build_db($cmd, $db_script);
10719
10720  # Make sure that, if we're running as root, the database file has
10721  # the permissions/privs set for use by proftpd
10722  if ($< == 0) {
10723    unless (chmod(0666, $db_file)) {
10724      die("Can't set perms on $db_file to 0666: $!");
10725    }
10726  }
10727
10728  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
10729  if (open(my $fh, "> $test_file")) {
10730    print $fh "ABCDefgh" x 32768;
10731
10732    unless (close($fh)) {
10733      die("Can't write $test_file: $!");
10734    }
10735
10736  } else {
10737    die("Can't open $test_file: $!");
10738  }
10739
10740  my $config = {
10741    PidFile => $pid_file,
10742    ScoreboardFile => $scoreboard_file,
10743    SystemLog => $log_file,
10744    TraceLog => $log_file,
10745    Trace => 'sql:20 command:20 netio:20',
10746
10747    AuthUserFile => $auth_user_file,
10748    AuthGroupFile => $auth_group_file,
10749
10750    IfModules => {
10751      'mod_delay.c' => {
10752        DelayEngine => 'off',
10753      },
10754
10755      'mod_sql.c' => {
10756        SQLEngine => 'log',
10757        SQLBackend => 'sqlite3',
10758        SQLConnectInfo => $db_file,
10759        SQLLogFile => $log_file,
10760        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
10761        SQLLog => 'ERR_RETR,RETR xfer_status',
10762      },
10763    },
10764  };
10765
10766  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10767
10768  # Open pipes, for use between the parent and child processes.  Specifically,
10769  # the child will indicate when it's done with its test by writing a message
10770  # to the parent.
10771  my ($rfh, $wfh);
10772  unless (pipe($rfh, $wfh)) {
10773    die("Can't open pipe: $!");
10774  }
10775
10776  my $ex;
10777
10778  # Fork child
10779  $self->handle_sigchld();
10780  defined(my $pid = fork()) or die("Can't fork: $!");
10781  if ($pid) {
10782    eval {
10783      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10784      $client->login($user, $passwd);
10785
10786      my $conn = $client->retr_raw('test.txt');
10787      unless ($conn) {
10788        die("RETR test.txt failed: " . $client->response_code() . " " .
10789          $client->response_msg());
10790      }
10791
10792      my $buf;
10793      $conn->read($buf, 4, 25);
10794      eval { $conn->abort() };
10795
10796      my $resp_code = $client->response_code();
10797      my $resp_msg = $client->response_msg();
10798
10799      $self->assert_transfer_ok($resp_code, $resp_msg, 1);
10800
10801      $client->quit();
10802    };
10803
10804    if ($@) {
10805      $ex = $@;
10806    }
10807
10808    $wfh->print("done\n");
10809    $wfh->flush();
10810
10811  } else {
10812    eval { server_wait($config_file, $rfh) };
10813    if ($@) {
10814      warn($@);
10815      exit 1;
10816    }
10817
10818    exit 0;
10819  }
10820
10821  # Stop server
10822  server_stop($pid_file);
10823
10824  $self->assert_child_ok($pid);
10825
10826  if ($ex) {
10827    test_append_logfile($log_file, $ex);
10828    unlink($log_file);
10829
10830    die($ex);
10831  }
10832
10833  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
10834
10835  my $expected;
10836
10837  $expected = $user;
10838  $self->assert($expected eq $login,
10839    test_msg("Expected user '$expected', got '$login'"));
10840
10841  $expected = '127.0.0.1';
10842  $self->assert($expected eq $ip_addr,
10843    test_msg("Expected IP address '$expected', got '$ip_addr'"));
10844
10845  $expected = 'cancelled';
10846  $self->assert($expected eq $xfer_status,
10847    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
10848
10849  if ($^O eq 'darwin') {
10850    # MacOSX-specific hack
10851    $test_file = '/private' . $test_file;
10852  }
10853
10854  $expected = $test_file;
10855  $self->assert($expected eq $xfer_path,
10856    test_msg("Expected file path '$expected', got '$xfer_path'"));
10857
10858  unlink($log_file);
10859}
10860
10861sub sql_sqllog_var_xfer_status_failed {
10862  my $self = shift;
10863  my $tmpdir = $self->{tmpdir};
10864
10865  my $config_file = "$tmpdir/sqlite.conf";
10866  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
10867  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
10868
10869  my $log_file = test_get_logfile();
10870
10871  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
10872  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
10873
10874  my $user = 'proftpd';
10875  my $passwd = 'test';
10876  my $group = 'ftpd';
10877  my $home_dir = File::Spec->rel2abs($tmpdir);
10878  my $uid = 500;
10879  my $gid = 500;
10880
10881  # Make sure that, if we're running as root, that the home directory has
10882  # permissions/privs set for the account we create
10883  if ($< == 0) {
10884    unless (chmod(0755, $home_dir)) {
10885      die("Can't set perms on $home_dir to 0755: $!");
10886    }
10887
10888    unless (chown($uid, $gid, $home_dir)) {
10889      die("Can't set owner of $home_dir to $uid/$gid: $!");
10890    }
10891  }
10892
10893  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
10894    '/bin/bash');
10895  auth_group_write($auth_group_file, $group, $gid, $user);
10896
10897  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
10898
10899  # Build up sqlite3 command to create users, groups tables and populate them
10900  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
10901
10902  if (open(my $fh, "> $db_script")) {
10903    print $fh <<EOS;
10904CREATE TABLE ftpsessions (
10905  user TEXT,
10906  ip_addr TEXT,
10907  xfer_status TEXT,
10908  xfer_path TEXT
10909);
10910EOS
10911
10912    unless (close($fh)) {
10913      die("Can't write $db_script: $!");
10914    }
10915
10916  } else {
10917    die("Can't open $db_script: $!");
10918  }
10919
10920  my $cmd = "sqlite3 $db_file < $db_script";
10921  build_db($cmd, $db_script);
10922
10923  # Make sure that, if we're running as root, the database file has
10924  # the permissions/privs set for use by proftpd
10925  if ($< == 0) {
10926    unless (chmod(0666, $db_file)) {
10927      die("Can't set perms on $db_file to 0666: $!");
10928    }
10929  }
10930
10931  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
10932  if (open(my $fh, "> $test_file")) {
10933    print $fh "ABCDefgh" x 32768;
10934
10935    unless (close($fh)) {
10936      die("Can't write $test_file: $!");
10937    }
10938
10939  } else {
10940    die("Can't open $test_file: $!");
10941  }
10942
10943  my $config = {
10944    PidFile => $pid_file,
10945    ScoreboardFile => $scoreboard_file,
10946    SystemLog => $log_file,
10947    TraceLog => $log_file,
10948    Trace => 'sql:20 command:20 netio:20',
10949
10950    AuthUserFile => $auth_user_file,
10951    AuthGroupFile => $auth_group_file,
10952
10953    # This is used to tickle the "failed" transfer status
10954    MaxRetrieveFileSize => '12 B',
10955
10956    IfModules => {
10957      'mod_delay.c' => {
10958        DelayEngine => 'off',
10959      },
10960
10961      'mod_sql.c' => {
10962        SQLEngine => 'log',
10963        SQLBackend => 'sqlite3',
10964        SQLConnectInfo => $db_file,
10965        SQLLogFile => $log_file,
10966        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
10967        SQLLog => 'ERR_RETR,RETR xfer_status',
10968      },
10969    },
10970  };
10971
10972  my ($port, $config_user, $config_group) = config_write($config_file, $config);
10973
10974  # Open pipes, for use between the parent and child processes.  Specifically,
10975  # the child will indicate when it's done with its test by writing a message
10976  # to the parent.
10977  my ($rfh, $wfh);
10978  unless (pipe($rfh, $wfh)) {
10979    die("Can't open pipe: $!");
10980  }
10981
10982  my $ex;
10983
10984  # Fork child
10985  $self->handle_sigchld();
10986  defined(my $pid = fork()) or die("Can't fork: $!");
10987  if ($pid) {
10988    eval {
10989      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
10990      $client->login($user, $passwd);
10991
10992      my $conn = $client->retr_raw('test.txt');
10993      unless ($conn) {
10994        die("RETR test.txt failed: " . $client->response_code() . " " .
10995          $client->response_msg());
10996      }
10997
10998      my $buf;
10999      $conn->read($buf, 4, 25);
11000      eval { $conn->close() };
11001
11002      # Make the control connection go away uncleanly
11003      $client->quit();
11004    };
11005
11006    if ($@) {
11007      $ex = $@;
11008    }
11009
11010    $wfh->print("done\n");
11011    $wfh->flush();
11012
11013  } else {
11014    eval { server_wait($config_file, $rfh) };
11015    if ($@) {
11016      warn($@);
11017      exit 1;
11018    }
11019
11020    exit 0;
11021  }
11022
11023  # Stop server
11024  server_stop($pid_file);
11025
11026  $self->assert_child_ok($pid);
11027
11028  if ($ex) {
11029    test_append_logfile($log_file, $ex);
11030    unlink($log_file);
11031
11032    die($ex);
11033  }
11034
11035  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
11036
11037  my $expected;
11038
11039  $expected = $user;
11040  $self->assert($expected eq $login,
11041    test_msg("Expected user '$expected', got '$login'"));
11042
11043  $expected = '127.0.0.1';
11044  $self->assert($expected eq $ip_addr,
11045    test_msg("Expected IP address '$expected', got '$ip_addr'"));
11046
11047  $expected = 'failed';
11048  $self->assert($expected eq $xfer_status,
11049    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
11050
11051  if ($^O eq 'darwin') {
11052    # MacOSX-specific hack
11053    $test_file = '/private' . $test_file;
11054  }
11055
11056  $expected = $test_file;
11057  $self->assert($expected eq $xfer_path,
11058    test_msg("Expected file path '$expected', got '$xfer_path'"));
11059
11060  unlink($log_file);
11061}
11062
11063sub sql_sqllog_var_xfer_status_timeout {
11064  my $self = shift;
11065  my $tmpdir = $self->{tmpdir};
11066
11067  my $config_file = "$tmpdir/sqlite.conf";
11068  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
11069  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
11070
11071  my $log_file = test_get_logfile();
11072
11073  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
11074  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
11075
11076  my $user = 'proftpd';
11077  my $passwd = 'test';
11078  my $group = 'ftpd';
11079  my $home_dir = File::Spec->rel2abs($tmpdir);
11080  my $uid = 500;
11081  my $gid = 500;
11082
11083  # Make sure that, if we're running as root, that the home directory has
11084  # permissions/privs set for the account we create
11085  if ($< == 0) {
11086    unless (chmod(0755, $home_dir)) {
11087      die("Can't set perms on $home_dir to 0755: $!");
11088    }
11089
11090    unless (chown($uid, $gid, $home_dir)) {
11091      die("Can't set owner of $home_dir to $uid/$gid: $!");
11092    }
11093  }
11094
11095  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
11096    '/bin/bash');
11097  auth_group_write($auth_group_file, $group, $gid, $user);
11098
11099  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
11100
11101  # Build up sqlite3 command to create users, groups tables and populate them
11102  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
11103
11104  if (open(my $fh, "> $db_script")) {
11105    print $fh <<EOS;
11106CREATE TABLE ftpsessions (
11107  user TEXT,
11108  ip_addr TEXT,
11109  xfer_status TEXT,
11110  xfer_path TEXT
11111);
11112EOS
11113
11114    unless (close($fh)) {
11115      die("Can't write $db_script: $!");
11116    }
11117
11118  } else {
11119    die("Can't open $db_script: $!");
11120  }
11121
11122  my $cmd = "sqlite3 $db_file < $db_script";
11123  build_db($cmd, $db_script);
11124
11125  # Make sure that, if we're running as root, the database file has
11126  # the permissions/privs set for use by proftpd
11127  if ($< == 0) {
11128    unless (chmod(0666, $db_file)) {
11129      die("Can't set perms on $db_file to 0666: $!");
11130    }
11131  }
11132
11133  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
11134  if (open(my $fh, "> $test_file")) {
11135    print $fh "ABCDefgh" x 32768;
11136
11137    unless (close($fh)) {
11138      die("Can't write $test_file: $!");
11139    }
11140
11141  } else {
11142    die("Can't open $test_file: $!");
11143  }
11144
11145  my $timeout_stalled = 1;
11146
11147  my $config = {
11148    PidFile => $pid_file,
11149    ScoreboardFile => $scoreboard_file,
11150    SystemLog => $log_file,
11151    TraceLog => $log_file,
11152    Trace => 'sql:20 command:20 netio:20',
11153
11154    AuthUserFile => $auth_user_file,
11155    AuthGroupFile => $auth_group_file,
11156
11157    # This is used to tickle the "timeout" transfer status
11158    TimeoutStalled => $timeout_stalled,
11159
11160    IfModules => {
11161      'mod_delay.c' => {
11162        DelayEngine => 'off',
11163      },
11164
11165      'mod_sql.c' => {
11166        SQLEngine => 'log',
11167        SQLBackend => 'sqlite3',
11168        SQLConnectInfo => $db_file,
11169        SQLLogFile => $log_file,
11170        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
11171        SQLLog => 'ERR_RETR,RETR,EXIT xfer_status',
11172      },
11173    },
11174  };
11175
11176  my ($port, $config_user, $config_group) = config_write($config_file, $config);
11177
11178  # Open pipes, for use between the parent and child processes.  Specifically,
11179  # the child will indicate when it's done with its test by writing a message
11180  # to the parent.
11181  my ($rfh, $wfh);
11182  unless (pipe($rfh, $wfh)) {
11183    die("Can't open pipe: $!");
11184  }
11185
11186  my $ex;
11187
11188  # Fork child
11189  $self->handle_sigchld();
11190  defined(my $pid = fork()) or die("Can't fork: $!");
11191  if ($pid) {
11192    eval {
11193      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
11194      $client->login($user, $passwd);
11195      $client->type('ascii');
11196
11197      my $conn = $client->retr_raw('test.txt');
11198      unless ($conn) {
11199        die("RETR test.xt failed: " . $client->response_code() . " " .
11200          $client->response_msg());
11201      }
11202
11203      sleep($timeout_stalled + 1);
11204
11205      my $buf;
11206      $conn->read($buf, 32, 25);
11207      sleep($timeout_stalled);
11208      eval { $conn->close() };
11209
11210      eval { $client->noop() };
11211      unless ($@) {
11212        die("NOOP succeeded unexpectedly");
11213      }
11214
11215      my $resp_code = $client->response_code();
11216      my $resp_msg = $client->response_msg();
11217
11218      my $expected;
11219
11220      # Perl's Net::Cmd module uses a very non-standard 599 code to
11221      # indicate that the connection is closed
11222      $expected = 599;
11223      $self->assert($expected == $resp_code,
11224        test_msg("Expected $expected, got $resp_code"));
11225
11226      $expected = "Connection closed";
11227      $self->assert($expected eq $resp_msg,
11228        test_msg("Expected '$expected', got '$resp_msg'"));
11229    };
11230
11231    if ($@) {
11232      $ex = $@;
11233    }
11234
11235    $wfh->print("done\n");
11236    $wfh->flush();
11237
11238  } else {
11239    eval { server_wait($config_file, $rfh) };
11240    if ($@) {
11241      warn($@);
11242      exit 1;
11243    }
11244
11245    exit 0;
11246  }
11247
11248  # Stop server
11249  server_stop($pid_file);
11250
11251  $self->assert_child_ok($pid);
11252
11253  if ($ex) {
11254    test_append_logfile($log_file, $ex);
11255    unlink($log_file);
11256
11257    die($ex);
11258  }
11259
11260  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
11261
11262  my $expected;
11263
11264  $expected = $user;
11265  $self->assert($expected eq $login,
11266    test_msg("Expected user '$expected', got '$login'"));
11267
11268  $expected = '127.0.0.1';
11269  $self->assert($expected eq $ip_addr,
11270    test_msg("Expected IP address '$expected', got '$ip_addr'"));
11271
11272  $expected = '^(timeout|failed)$';
11273  $self->assert(qr/$expected/, $xfer_status,
11274    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
11275
11276  if ($xfer_status eq 'timeout') {
11277    if ($^O eq 'darwin') {
11278      # MacOSX-specific hack
11279      $test_file = '/private' . $test_file;
11280    }
11281
11282    $expected = $test_file;
11283    $self->assert($expected eq $xfer_path,
11284      test_msg("Expected file path '$expected', got '$xfer_path'"));
11285  }
11286
11287  unlink($log_file);
11288}
11289
11290sub sql_sqllog_var_xfer_status_filtered {
11291  my $self = shift;
11292  my $tmpdir = $self->{tmpdir};
11293
11294  my $config_file = "$tmpdir/sqlite.conf";
11295  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
11296  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
11297
11298  my $log_file = test_get_logfile();
11299
11300  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
11301  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
11302
11303  my $user = 'proftpd';
11304  my $passwd = 'test';
11305  my $group = 'ftpd';
11306  my $home_dir = File::Spec->rel2abs($tmpdir);
11307  my $uid = 500;
11308  my $gid = 500;
11309
11310  # Make sure that, if we're running as root, that the home directory has
11311  # permissions/privs set for the account we create
11312  if ($< == 0) {
11313    unless (chmod(0755, $home_dir)) {
11314      die("Can't set perms on $home_dir to 0755: $!");
11315    }
11316
11317    unless (chown($uid, $gid, $home_dir)) {
11318      die("Can't set owner of $home_dir to $uid/$gid: $!");
11319    }
11320  }
11321
11322  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
11323    '/bin/bash');
11324  auth_group_write($auth_group_file, $group, $gid, $user);
11325
11326  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
11327
11328  # Build up sqlite3 command to create users, groups tables and populate them
11329  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
11330
11331  if (open(my $fh, "> $db_script")) {
11332    print $fh <<EOS;
11333CREATE TABLE ftpsessions (
11334  user TEXT,
11335  ip_addr TEXT,
11336  xfer_status TEXT,
11337  xfer_path TEXT
11338);
11339EOS
11340
11341    unless (close($fh)) {
11342      die("Can't write $db_script: $!");
11343    }
11344
11345  } else {
11346    die("Can't open $db_script: $!");
11347  }
11348
11349  my $cmd = "sqlite3 $db_file < $db_script";
11350  build_db($cmd, $db_script);
11351
11352  # Make sure that, if we're running as root, the database file has
11353  # the permissions/privs set for use by proftpd
11354  if ($< == 0) {
11355    unless (chmod(0666, $db_file)) {
11356      die("Can't set perms on $db_file to 0666: $!");
11357    }
11358  }
11359
11360  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
11361  if (open(my $fh, "> $test_file")) {
11362    print $fh "ABCDefgh" x 32768;
11363
11364    unless (close($fh)) {
11365      die("Can't write $test_file: $!");
11366    }
11367
11368  } else {
11369    die("Can't open $test_file: $!");
11370  }
11371
11372  my $config = {
11373    PidFile => $pid_file,
11374    ScoreboardFile => $scoreboard_file,
11375    SystemLog => $log_file,
11376    TraceLog => $log_file,
11377    Trace => 'sql:20 command:20 netio:20',
11378
11379    AuthUserFile => $auth_user_file,
11380    AuthGroupFile => $auth_group_file,
11381
11382    PathDenyFilter => '^.*\.csv$',
11383
11384    IfModules => {
11385      'mod_delay.c' => {
11386        DelayEngine => 'off',
11387      },
11388
11389      'mod_sql.c' => {
11390        SQLEngine => 'log',
11391        SQLBackend => 'sqlite3',
11392        SQLConnectInfo => $db_file,
11393        SQLLogFile => $log_file,
11394        SQLNamedQuery => 'xfer_status FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%f\')"',
11395        SQLLog => 'ERR_STOR,STOR,EXIT xfer_status',
11396      },
11397    },
11398  };
11399
11400  my ($port, $config_user, $config_group) = config_write($config_file, $config);
11401
11402  # Open pipes, for use between the parent and child processes.  Specifically,
11403  # the child will indicate when it's done with its test by writing a message
11404  # to the parent.
11405  my ($rfh, $wfh);
11406  unless (pipe($rfh, $wfh)) {
11407    die("Can't open pipe: $!");
11408  }
11409
11410  my $ex;
11411
11412  # Fork child
11413  $self->handle_sigchld();
11414  defined(my $pid = fork()) or die("Can't fork: $!");
11415  if ($pid) {
11416    eval {
11417      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
11418      $client->login($user, $passwd);
11419      $client->type('ascii');
11420
11421      my $conn = $client->stor_raw('test.csv');
11422      if ($conn) {
11423        die("RETR test.csv succeeded unexpectedly");
11424      }
11425
11426      my $resp_code = $client->response_code();
11427      my $resp_msg = $client->response_msg();
11428      $client->quit();
11429
11430      my $expected = 550;
11431      $self->assert($expected == $resp_code,
11432        "Expected response code $expected, got $resp_code");
11433
11434      $expected = "test.csv: Forbidden filename";
11435      $self->assert($expected eq $resp_msg,
11436        "Expected '$expected', got '$resp_msg'");
11437    };
11438
11439    if ($@) {
11440      $ex = $@;
11441    }
11442
11443    $wfh->print("done\n");
11444    $wfh->flush();
11445
11446  } else {
11447    eval { server_wait($config_file, $rfh) };
11448    if ($@) {
11449      warn($@);
11450      exit 1;
11451    }
11452
11453    exit 0;
11454  }
11455
11456  # Stop server
11457  server_stop($pid_file);
11458
11459  $self->assert_child_ok($pid);
11460
11461  if ($ex) {
11462    test_append_logfile($log_file, $ex);
11463    unlink($log_file);
11464
11465    die($ex);
11466  }
11467
11468  my ($login, $ip_addr, $xfer_status, $xfer_path) = get_xfer_status($db_file, "user = \'$user\'");
11469
11470  my $expected;
11471
11472  $expected = $user;
11473  $self->assert($expected eq $login,
11474    test_msg("Expected user '$expected', got '$login'"));
11475
11476  $expected = '127.0.0.1';
11477  $self->assert($expected eq $ip_addr,
11478    test_msg("Expected IP address '$expected', got '$ip_addr'"));
11479
11480  $expected = '^(timeout|failed)$';
11481  $self->assert(qr/$expected/, $xfer_status,
11482    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
11483
11484  if ($xfer_status eq 'timeout') {
11485    if ($^O eq 'darwin') {
11486      # MacOSX-specific hack
11487      $test_file = '/private' . $test_file;
11488    }
11489
11490    $expected = $test_file;
11491    $self->assert($expected eq $xfer_path,
11492      test_msg("Expected file path '$expected', got '$xfer_path'"));
11493  }
11494
11495  unlink($log_file);
11496}
11497
11498sub get_xfer_failure {
11499  my $db_file = shift;
11500  my $where = shift;
11501
11502  my $sql = "SELECT user, ip_addr, xfer_status, xfer_failure, xfer_path FROM ftpsessions";
11503  if ($where) {
11504    $sql .= " WHERE $where";
11505  }
11506  $sql .= " LIMIT 1";
11507
11508  my $cmd = "sqlite3 $db_file \"$sql\"";
11509
11510  if ($ENV{TEST_VERBOSE}) {
11511    print STDERR "Executing sqlite3: $cmd\n";
11512  }
11513
11514  my $res = join('', `$cmd`);
11515  chomp($res);
11516
11517  # The default sqlite3 delimiter is '|'
11518  return split(/\|/, $res);
11519}
11520
11521sub sql_sqllog_var_xfer_failure_none {
11522  my $self = shift;
11523  my $tmpdir = $self->{tmpdir};
11524
11525  my $config_file = "$tmpdir/sqlite.conf";
11526  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
11527  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
11528
11529  my $log_file = test_get_logfile();
11530
11531  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
11532  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
11533
11534  my $user = 'proftpd';
11535  my $passwd = 'test';
11536  my $group = 'ftpd';
11537  my $home_dir = File::Spec->rel2abs($tmpdir);
11538  my $uid = 500;
11539  my $gid = 500;
11540
11541  # Make sure that, if we're running as root, that the home directory has
11542  # permissions/privs set for the account we create
11543  if ($< == 0) {
11544    unless (chmod(0755, $home_dir)) {
11545      die("Can't set perms on $home_dir to 0755: $!");
11546    }
11547
11548    unless (chown($uid, $gid, $home_dir)) {
11549      die("Can't set owner of $home_dir to $uid/$gid: $!");
11550    }
11551  }
11552
11553  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
11554    '/bin/bash');
11555  auth_group_write($auth_group_file, $group, $gid, $user);
11556
11557  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
11558
11559  # Build up sqlite3 command to create users, groups tables and populate them
11560  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
11561
11562  if (open(my $fh, "> $db_script")) {
11563    print $fh <<EOS;
11564CREATE TABLE ftpsessions (
11565  user TEXT,
11566  ip_addr TEXT,
11567  xfer_status TEXT,
11568  xfer_failure TEXT,
11569  xfer_path TEXT
11570);
11571EOS
11572
11573    unless (close($fh)) {
11574      die("Can't write $db_script: $!");
11575    }
11576
11577  } else {
11578    die("Can't open $db_script: $!");
11579  }
11580
11581  my $cmd = "sqlite3 $db_file < $db_script";
11582  build_db($cmd, $db_script);
11583
11584  # Make sure that, if we're running as root, the database file has
11585  # the permissions/privs set for use by proftpd
11586  if ($< == 0) {
11587    unless (chmod(0666, $db_file)) {
11588      die("Can't set perms on $db_file to 0666: $!");
11589    }
11590  }
11591
11592  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
11593  if (open(my $fh, "> $test_file")) {
11594    close($fh);
11595
11596  } else {
11597    die("Can't open $test_file: $!");
11598  }
11599
11600  my $config = {
11601    PidFile => $pid_file,
11602    ScoreboardFile => $scoreboard_file,
11603    SystemLog => $log_file,
11604    TraceLog => $log_file,
11605    Trace => 'sql:20 command:20 netio:20',
11606
11607    AuthUserFile => $auth_user_file,
11608    AuthGroupFile => $auth_group_file,
11609
11610    IfModules => {
11611      'mod_delay.c' => {
11612        DelayEngine => 'off',
11613      },
11614
11615      'mod_sql.c' => {
11616        SQLEngine => 'log',
11617        SQLBackend => 'sqlite3',
11618        SQLConnectInfo => $db_file,
11619        SQLLogFile => $log_file,
11620        SQLNamedQuery => 'xfer_reason FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_failure, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%{transfer-failure}\', \'%f\')"',
11621        SQLLog => 'ERR_RETR,RETR xfer_reason',
11622      },
11623    },
11624  };
11625
11626  my ($port, $config_user, $config_group) = config_write($config_file, $config);
11627
11628  # Open pipes, for use between the parent and child processes.  Specifically,
11629  # the child will indicate when it's done with its test by writing a message
11630  # to the parent.
11631  my ($rfh, $wfh);
11632  unless (pipe($rfh, $wfh)) {
11633    die("Can't open pipe: $!");
11634  }
11635
11636  my $ex;
11637
11638  # Fork child
11639  $self->handle_sigchld();
11640  defined(my $pid = fork()) or die("Can't fork: $!");
11641  if ($pid) {
11642    eval {
11643      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
11644      $client->login($user, $passwd);
11645
11646      my $conn = $client->retr_raw('test.txt');
11647      unless ($conn) {
11648        die("RETR test.txt failed: " . $client->response_code() . " " .
11649          $client->response_msg());
11650      }
11651
11652      my $buf;
11653      $conn->read($buf, 8192, 25);
11654      eval { $conn->close() };
11655
11656      my $resp_code = $client->response_code();
11657      my $resp_msg = $client->response_msg();
11658
11659      $self->assert_transfer_ok($resp_code, $resp_msg);
11660
11661      $client->quit();
11662    };
11663
11664    if ($@) {
11665      $ex = $@;
11666    }
11667
11668    $wfh->print("done\n");
11669    $wfh->flush();
11670
11671  } else {
11672    eval { server_wait($config_file, $rfh) };
11673    if ($@) {
11674      warn($@);
11675      exit 1;
11676    }
11677
11678    exit 0;
11679  }
11680
11681  # Stop server
11682  server_stop($pid_file);
11683
11684  $self->assert_child_ok($pid);
11685
11686  if ($ex) {
11687    test_append_logfile($log_file, $ex);
11688    unlink($log_file);
11689
11690    die($ex);
11691  }
11692
11693  my ($login, $ip_addr, $xfer_status, $xfer_failure, $xfer_path) = get_xfer_failure($db_file, "user = \'$user\'");
11694
11695  my $expected;
11696
11697  $expected = $user;
11698  $self->assert($expected eq $login,
11699    test_msg("Expected user '$expected', got '$login'"));
11700
11701  $expected = '127.0.0.1';
11702  $self->assert($expected eq $ip_addr,
11703    test_msg("Expected IP address '$expected', got '$ip_addr'"));
11704
11705  $expected = 'success';
11706  $self->assert($expected eq $xfer_status,
11707    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
11708
11709  $expected = '-';
11710  $self->assert($expected eq $xfer_failure,
11711    test_msg("Expected transfer failure '$expected', got '$xfer_failure'"));
11712
11713  if ($^O eq 'darwin') {
11714    # MacOSX-specific hack
11715    $test_file = '/private' . $test_file;
11716  }
11717
11718  $expected = $test_file;
11719  $self->assert($expected eq $xfer_path,
11720    test_msg("Expected file path '$expected', got '$xfer_path'"));
11721
11722  unlink($log_file);
11723}
11724
11725sub sql_sqllog_var_xfer_failure_reason {
11726  my $self = shift;
11727  my $tmpdir = $self->{tmpdir};
11728
11729  my $config_file = "$tmpdir/sqlite.conf";
11730  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
11731  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
11732
11733  my $log_file = test_get_logfile();
11734
11735  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
11736  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
11737
11738  my $user = 'proftpd';
11739  my $passwd = 'test';
11740  my $group = 'ftpd';
11741  my $home_dir = File::Spec->rel2abs($tmpdir);
11742  my $uid = 500;
11743  my $gid = 500;
11744
11745  # Make sure that, if we're running as root, that the home directory has
11746  # permissions/privs set for the account we create
11747  if ($< == 0) {
11748    unless (chmod(0755, $home_dir)) {
11749      die("Can't set perms on $home_dir to 0755: $!");
11750    }
11751
11752    unless (chown($uid, $gid, $home_dir)) {
11753      die("Can't set owner of $home_dir to $uid/$gid: $!");
11754    }
11755  }
11756
11757  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
11758    '/bin/bash');
11759  auth_group_write($auth_group_file, $group, $gid, $user);
11760
11761  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
11762
11763  # Build up sqlite3 command to create users, groups tables and populate them
11764  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
11765
11766  if (open(my $fh, "> $db_script")) {
11767    print $fh <<EOS;
11768CREATE TABLE ftpsessions (
11769  user TEXT,
11770  ip_addr TEXT,
11771  xfer_status TEXT,
11772  xfer_failure TEXT,
11773  xfer_path TEXT
11774);
11775EOS
11776
11777    unless (close($fh)) {
11778      die("Can't write $db_script: $!");
11779    }
11780
11781  } else {
11782    die("Can't open $db_script: $!");
11783  }
11784
11785  my $cmd = "sqlite3 $db_file < $db_script";
11786  build_db($cmd, $db_script);
11787
11788  # Make sure that, if we're running as root, the database file has
11789  # the permissions/privs set for use by proftpd
11790  if ($< == 0) {
11791    unless (chmod(0666, $db_file)) {
11792      die("Can't set perms on $db_file to 0666: $!");
11793    }
11794  }
11795
11796  my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
11797  if (open(my $fh, "> $test_file")) {
11798    print $fh "ABCD" x 1024;
11799    close($fh);
11800
11801  } else {
11802    die("Can't open $test_file: $!");
11803  }
11804
11805  my $config = {
11806    PidFile => $pid_file,
11807    ScoreboardFile => $scoreboard_file,
11808    SystemLog => $log_file,
11809    TraceLog => $log_file,
11810    Trace => 'sql:20 command:20 netio:20',
11811
11812    AuthUserFile => $auth_user_file,
11813    AuthGroupFile => $auth_group_file,
11814
11815    # This is used to tickle the "failed" transfer status
11816    MaxRetrieveFileSize => '12 B',
11817
11818    IfModules => {
11819      'mod_delay.c' => {
11820        DelayEngine => 'off',
11821      },
11822
11823      'mod_sql.c' => {
11824        SQLEngine => 'log',
11825        SQLBackend => 'sqlite3',
11826        SQLConnectInfo => $db_file,
11827        SQLLogFile => $log_file,
11828        SQLNamedQuery => 'xfer_reason FREEFORM "INSERT INTO ftpsessions (user, ip_addr, xfer_status, xfer_failure, xfer_path) VALUES (\'%u\', \'%L\', \'%{transfer-status}\', \'%{transfer-failure}\', \'%f\')"',
11829        SQLLog => 'ERR_RETR,RETR xfer_reason',
11830      },
11831    },
11832  };
11833
11834  my ($port, $config_user, $config_group) = config_write($config_file, $config);
11835
11836  # Open pipes, for use between the parent and child processes.  Specifically,
11837  # the child will indicate when it's done with its test by writing a message
11838  # to the parent.
11839  my ($rfh, $wfh);
11840  unless (pipe($rfh, $wfh)) {
11841    die("Can't open pipe: $!");
11842  }
11843
11844  my $ex;
11845
11846  # Fork child
11847  $self->handle_sigchld();
11848  defined(my $pid = fork()) or die("Can't fork: $!");
11849  if ($pid) {
11850    eval {
11851      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
11852      $client->login($user, $passwd);
11853
11854      my $conn = $client->retr_raw('test.txt');
11855      unless ($conn) {
11856        die("RETR test.txt failed: " . $client->response_code() . " " .
11857          $client->response_msg());
11858      }
11859
11860      my $buf;
11861      $conn->read($buf, 8192, 25);
11862      eval { $conn->close() };
11863
11864      my $resp_code = $client->response_code();
11865      my $resp_msg = $client->response_msg();
11866
11867      my $expected = 426;
11868      $self->assert($expected == $resp_code,
11869        test_msg("Expected response code $expected, got $resp_code"));
11870
11871      $expected = 'Transfer aborted';
11872      $self->assert(qr/$expected/, $resp_msg,
11873        test_msg("Expected response message '$expected', got '$resp_msg'"));
11874
11875      $client->quit();
11876    };
11877
11878    if ($@) {
11879      $ex = $@;
11880    }
11881
11882    $wfh->print("done\n");
11883    $wfh->flush();
11884
11885  } else {
11886    eval { server_wait($config_file, $rfh) };
11887    if ($@) {
11888      warn($@);
11889      exit 1;
11890    }
11891
11892    exit 0;
11893  }
11894
11895  # Stop server
11896  server_stop($pid_file);
11897
11898  $self->assert_child_ok($pid);
11899
11900  if ($ex) {
11901    test_append_logfile($log_file, $ex);
11902    unlink($log_file);
11903
11904    die($ex);
11905  }
11906
11907  my ($login, $ip_addr, $xfer_status, $xfer_failure, $xfer_path) = get_xfer_failure($db_file, "user = \'$user\'");
11908
11909  my $expected;
11910
11911  $expected = $user;
11912  $self->assert($expected eq $login,
11913    test_msg("Expected user '$expected', got '$login'"));
11914
11915  $expected = '127.0.0.1';
11916  $self->assert($expected eq $ip_addr,
11917    test_msg("Expected IP address '$expected', got '$ip_addr'"));
11918
11919  $expected = 'failed';
11920  $self->assert($expected eq $xfer_status,
11921    test_msg("Expected transfer status '$expected', got '$xfer_status'"));
11922
11923  $expected = 'Operation not permitted';
11924  $self->assert($expected eq $xfer_failure,
11925    test_msg("Expected transfer failure '$expected', got '$xfer_failure'"));
11926
11927  if ($^O eq 'darwin') {
11928    # MacOSX-specific hack
11929    $test_file = '/private' . $test_file;
11930  }
11931
11932  $expected = $test_file;
11933  $self->assert($expected eq $xfer_path,
11934    test_msg("Expected file path '$expected', got '$xfer_path'"));
11935
11936  unlink($log_file);
11937}
11938
11939sub sql_sqlshowinfo_pass_failed_bug3782 {
11940  my $self = shift;
11941  my $tmpdir = $self->{tmpdir};
11942  my $setup = test_setup($tmpdir, 'sqlite');
11943
11944  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
11945
11946  # Build up sqlite3 command to create users, groups tables and populate them
11947  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
11948
11949  if (open(my $fh, "> $db_script")) {
11950    print $fh <<EOS;
11951CREATE TABLE users (
11952  userid TEXT,
11953  passwd TEXT,
11954  uid INTEGER,
11955  gid INTEGER,
11956  homedir TEXT,
11957  shell TEXT,
11958  secure_only INTEGER
11959);
11960INSERT INTO users (userid, passwd, uid, gid, homedir, shell, secure_only) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash', 1);
11961
11962CREATE TABLE groups (
11963  groupname TEXT,
11964  gid INTEGER,
11965  members TEXT
11966);
11967INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
11968
11969CREATE TABLE login_responses (
11970  secure_only INTEGER,
11971  response TEXT
11972);
11973
11974INSERT INTO login_responses (secure_only, response) VALUES (0, "Login failed.");
11975INSERT INTO login_responses (secure_only, response) VALUES (1, "You may not log into this account via FTP. Please use SFTP instead.");
11976EOS
11977
11978    unless (close($fh)) {
11979      die("Can't write $db_script: $!");
11980    }
11981
11982  } else {
11983    die("Can't open $db_script: $!");
11984  }
11985
11986  my $cmd = "sqlite3 $db_file < $db_script";
11987  build_db($cmd, $db_script);
11988
11989  # Make sure that, if we're running as root, the database file has
11990  # the permissions/privs set for use by proftpd
11991  if ($< == 0) {
11992    unless (chmod(0666, $db_file)) {
11993      die("Can't set perms on $db_file to 0666: $!");
11994    }
11995  }
11996
11997  my $config = {
11998    PidFile => $setup->{pid_file},
11999    ScoreboardFile => $setup->{scoreboard_file},
12000    SystemLog => $setup->{log_file},
12001    TraceLog => $setup->{log_file},
12002    Trace => 'jot:20 response:20 sql:20',
12003
12004    IfModules => {
12005      'mod_delay.c' => {
12006        DelayEngine => 'off',
12007      },
12008
12009      'mod_sql.c' => [
12010        'AuthOrder mod_sql.c',
12011
12012        'SQLAuthenticate users groups',
12013        'SQLAuthTypes plaintext',
12014        'SQLBackend sqlite3',
12015        "SQLConnectInfo $db_file",
12016        "SQLLogFile $setup->{log_file}",
12017        'SQLMinID 100',
12018
12019        'SQLUserWhereClause "secure_only != 1"',
12020        'SQLNamedQuery login_failure SELECT "response FROM users u, login_responses lr WHERE u.userid = \'%U\' AND u.secure_only = lr.secure_only"',
12021
12022        # Configure the equivalent of a multiline DisplayLogin file
12023        # using the SQLShowInfo directive
12024
12025        'SQLShowInfo ERR_PASS 530 "Sorry, %U"',
12026        'SQLShowInfo ERR_PASS 530 "%{login_failure}"',
12027        'SQLShowInfo ERR_PASS 530 " "',
12028      ],
12029    },
12030  };
12031
12032  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
12033    $config);
12034
12035  # Open pipes, for use between the parent and child processes.  Specifically,
12036  # the child will indicate when it's done with its test by writing a message
12037  # to the parent.
12038  my ($rfh, $wfh);
12039  unless (pipe($rfh, $wfh)) {
12040    die("Can't open pipe: $!");
12041  }
12042
12043  my $ex;
12044
12045  # Fork child
12046  $self->handle_sigchld();
12047  defined(my $pid = fork()) or die("Can't fork: $!");
12048  if ($pid) {
12049    eval {
12050      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12051      eval { $client->login($setup->{user}, $setup->{passwd}) };
12052      unless ($@) {
12053        die("Login succeeded unexpectedly");
12054      }
12055
12056      my $resp_msgs = $client->response_msgs();
12057      my $nmsgs = scalar(@$resp_msgs);
12058
12059      my $expected = 4;
12060      $self->assert($expected == $nmsgs, "Expected $expected, got $nmsgs");
12061
12062      $expected = "Login incorrect.";
12063      $self->assert($expected eq $resp_msgs->[0],
12064        "Expected response '$expected', got '$resp_msgs->[0]'");
12065
12066      $expected = " Sorry, $setup->{user}";
12067      $self->assert($expected eq $resp_msgs->[1],
12068        "Expected response '$expected', got '$resp_msgs->[1]'");
12069
12070      $expected = " You may not log into this account via FTP. Please use SFTP instead.";
12071      $self->assert($expected eq $resp_msgs->[2],
12072        "Expected response '$expected', got '$resp_msgs->[2]'");
12073
12074      $client->quit();
12075    };
12076    if ($@) {
12077      $ex = $@;
12078    }
12079
12080    $wfh->print("done\n");
12081    $wfh->flush();
12082
12083  } else {
12084    eval { server_wait($setup->{config_file}, $rfh) };
12085    if ($@) {
12086      warn($@);
12087      exit 1;
12088    }
12089
12090    exit 0;
12091  }
12092
12093  # Stop server
12094  server_stop($setup->{pid_file});
12095  $self->assert_child_ok($pid);
12096
12097  test_cleanup($setup->{log_file}, $ex);
12098}
12099
12100sub sql_sqllog_preauth_var_U_bug3822 {
12101  my $self = shift;
12102  my $tmpdir = $self->{tmpdir};
12103
12104  my $config_file = "$tmpdir/sqlite.conf";
12105  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
12106  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
12107
12108  my $log_file = test_get_logfile();
12109
12110  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
12111  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
12112
12113  my $user = 'proftpd';
12114  my $passwd = 'test';
12115  my $group = 'ftpd';
12116  my $home_dir = File::Spec->rel2abs($tmpdir);
12117  my $uid = 500;
12118  my $gid = 500;
12119
12120  # Make sure that, if we're running as root, that the home directory has
12121  # permissions/privs set for the account we create
12122  if ($< == 0) {
12123    unless (chmod(0755, $home_dir)) {
12124      die("Can't set perms on $home_dir to 0755: $!");
12125    }
12126
12127    unless (chown($uid, $gid, $home_dir)) {
12128      die("Can't set owner of $home_dir to $uid/$gid: $!");
12129    }
12130  }
12131
12132  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
12133    '/bin/bash');
12134  auth_group_write($auth_group_file, $group, $gid, $user);
12135
12136  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
12137
12138  # Build up sqlite3 command to create users, groups tables and populate them
12139  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
12140
12141  if (open(my $fh, "> $db_script")) {
12142    print $fh <<EOS;
12143CREATE TABLE ftpsessions (
12144  user TEXT,
12145  ip_addr TEXT,
12146  resp_mesg TEXT
12147);
12148EOS
12149
12150    unless (close($fh)) {
12151      die("Can't write $db_script: $!");
12152    }
12153
12154  } else {
12155    die("Can't open $db_script: $!");
12156  }
12157
12158  my $cmd = "sqlite3 $db_file < $db_script";
12159  build_db($cmd, $db_script);
12160
12161  # Make sure that, if we're running as root, the database file has
12162  # the permissions/privs set for use by proftpd
12163  if ($< == 0) {
12164    unless (chmod(0666, $db_file)) {
12165      die("Can't set perms on $db_file to 0666: $!");
12166    }
12167  }
12168
12169  my $config = {
12170    PidFile => $pid_file,
12171    ScoreboardFile => $scoreboard_file,
12172    SystemLog => $log_file,
12173
12174    AuthUserFile => $auth_user_file,
12175    AuthGroupFile => $auth_group_file,
12176
12177    IfModules => {
12178      'mod_delay.c' => {
12179        DelayEngine => 'off',
12180      },
12181
12182      'mod_sql.c' => {
12183        SQLEngine => 'log',
12184        SQLBackend => 'sqlite3',
12185        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
12186        SQLLogFile => $log_file,
12187        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, ip_addr, resp_mesg) VALUES (\'%U\', \'%L\', \'%S\')"',
12188        SQLLog => 'ERR_PWD info',
12189      },
12190    },
12191  };
12192
12193  my ($port, $config_user, $config_group) = config_write($config_file, $config);
12194
12195  # Open pipes, for use between the parent and child processes.  Specifically,
12196  # the child will indicate when it's done with its test by writing a message
12197  # to the parent.
12198  my ($rfh, $wfh);
12199  unless (pipe($rfh, $wfh)) {
12200    die("Can't open pipe: $!");
12201  }
12202
12203  my $ex;
12204
12205  # Fork child
12206  $self->handle_sigchld();
12207  defined(my $pid = fork()) or die("Can't fork: $!");
12208  if ($pid) {
12209    eval {
12210      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12211      eval { $client->pwd() };
12212      unless ($@) {
12213        die("PWD succeeded unexpectedly");
12214      }
12215
12216      $client->login($user, $passwd);
12217      $client->quit();
12218    };
12219
12220    if ($@) {
12221      $ex = $@;
12222    }
12223
12224    $wfh->print("done\n");
12225    $wfh->flush();
12226
12227  } else {
12228    eval { server_wait($config_file, $rfh) };
12229    if ($@) {
12230      warn($@);
12231      exit 1;
12232    }
12233
12234    exit 0;
12235  }
12236
12237  # Stop server
12238  server_stop($pid_file);
12239
12240  $self->assert_child_ok($pid);
12241
12242  if ($ex) {
12243    test_append_logfile($log_file, $ex);
12244    unlink($log_file);
12245
12246    die($ex);
12247  }
12248
12249  my ($login, $ip_addr, $resp_mesg) = get_resp_mesgs($db_file,
12250    "user = '-'", "user, ip_addr, resp_mesg");
12251
12252  my $expected;
12253
12254  $expected = '-';
12255  $self->assert($expected eq $login,
12256    test_msg("Expected '$expected', got '$login'"));
12257
12258  $expected = '127.0.0.1';
12259  $self->assert($expected eq $ip_addr,
12260    test_msg("Expected '$expected', got '$ip_addr'"));
12261
12262  $expected = 'Please login with USER and PASS';
12263  $self->assert($expected eq $resp_mesg,
12264    test_msg("Expected '$expected', got '$resp_mesg'"));
12265
12266  unlink($log_file);
12267}
12268
12269sub sql_sqllog_preauth_var_u_bug3822 {
12270  my $self = shift;
12271  my $tmpdir = $self->{tmpdir};
12272
12273  my $config_file = "$tmpdir/sqlite.conf";
12274  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
12275  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
12276
12277  my $log_file = test_get_logfile();
12278
12279  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
12280  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
12281
12282  my $user = 'proftpd';
12283  my $passwd = 'test';
12284  my $group = 'ftpd';
12285  my $home_dir = File::Spec->rel2abs($tmpdir);
12286  my $uid = 500;
12287  my $gid = 500;
12288
12289  # Make sure that, if we're running as root, that the home directory has
12290  # permissions/privs set for the account we create
12291  if ($< == 0) {
12292    unless (chmod(0755, $home_dir)) {
12293      die("Can't set perms on $home_dir to 0755: $!");
12294    }
12295
12296    unless (chown($uid, $gid, $home_dir)) {
12297      die("Can't set owner of $home_dir to $uid/$gid: $!");
12298    }
12299  }
12300
12301  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
12302    '/bin/bash');
12303  auth_group_write($auth_group_file, $group, $gid, $user);
12304
12305  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
12306
12307  # Build up sqlite3 command to create users, groups tables and populate them
12308  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
12309
12310  if (open(my $fh, "> $db_script")) {
12311    print $fh <<EOS;
12312CREATE TABLE ftpsessions (
12313  user TEXT,
12314  ip_addr TEXT,
12315  resp_mesg TEXT
12316);
12317EOS
12318
12319    unless (close($fh)) {
12320      die("Can't write $db_script: $!");
12321    }
12322
12323  } else {
12324    die("Can't open $db_script: $!");
12325  }
12326
12327  my $cmd = "sqlite3 $db_file < $db_script";
12328  build_db($cmd, $db_script);
12329
12330  # Make sure that, if we're running as root, the database file has
12331  # the permissions/privs set for use by proftpd
12332  if ($< == 0) {
12333    unless (chmod(0666, $db_file)) {
12334      die("Can't set perms on $db_file to 0666: $!");
12335    }
12336  }
12337
12338  my $config = {
12339    PidFile => $pid_file,
12340    ScoreboardFile => $scoreboard_file,
12341    SystemLog => $log_file,
12342
12343    AuthUserFile => $auth_user_file,
12344    AuthGroupFile => $auth_group_file,
12345
12346    IfModules => {
12347      'mod_delay.c' => {
12348        DelayEngine => 'off',
12349      },
12350
12351      'mod_sql.c' => {
12352        SQLEngine => 'log',
12353        SQLBackend => 'sqlite3',
12354        SQLConnectInfo => "$db_file foo bar PERCONNECTION",
12355        SQLLogFile => $log_file,
12356        SQLNamedQuery => 'info FREEFORM "INSERT INTO ftpsessions (user, ip_addr, resp_mesg) VALUES (\'%u\', \'%L\', \'%S\')"',
12357        SQLLog => 'ERR_PWD info',
12358      },
12359    },
12360  };
12361
12362  my ($port, $config_user, $config_group) = config_write($config_file, $config);
12363
12364  # Open pipes, for use between the parent and child processes.  Specifically,
12365  # the child will indicate when it's done with its test by writing a message
12366  # to the parent.
12367  my ($rfh, $wfh);
12368  unless (pipe($rfh, $wfh)) {
12369    die("Can't open pipe: $!");
12370  }
12371
12372  my $ex;
12373
12374  # Fork child
12375  $self->handle_sigchld();
12376  defined(my $pid = fork()) or die("Can't fork: $!");
12377  if ($pid) {
12378    eval {
12379      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12380      eval { $client->pwd() };
12381      unless ($@) {
12382        die("PWD succeeded unexpectedly");
12383      }
12384
12385      $client->login($user, $passwd);
12386      $client->quit();
12387    };
12388
12389    if ($@) {
12390      $ex = $@;
12391    }
12392
12393    $wfh->print("done\n");
12394    $wfh->flush();
12395
12396  } else {
12397    eval { server_wait($config_file, $rfh) };
12398    if ($@) {
12399      warn($@);
12400      exit 1;
12401    }
12402
12403    exit 0;
12404  }
12405
12406  # Stop server
12407  server_stop($pid_file);
12408
12409  $self->assert_child_ok($pid);
12410
12411  if ($ex) {
12412    test_append_logfile($log_file, $ex);
12413    unlink($log_file);
12414
12415    die($ex);
12416  }
12417
12418  my ($login, $ip_addr, $resp_mesg) = get_resp_mesgs($db_file,
12419    "user = '-'", "user, ip_addr, resp_mesg");
12420
12421  my $expected;
12422
12423  $expected = '-';
12424  $self->assert($expected eq $login,
12425    test_msg("Expected '$expected', got '$login'"));
12426
12427  $expected = '127.0.0.1';
12428  $self->assert($expected eq $ip_addr,
12429    test_msg("Expected '$expected', got '$ip_addr'"));
12430
12431  $expected = 'Please login with USER and PASS';
12432  $self->assert($expected eq $resp_mesg,
12433    test_msg("Expected '$expected', got '$resp_mesg'"));
12434
12435  unlink($log_file);
12436}
12437
12438sub sql_sqlite_maxhostsperuser {
12439  my $self = shift;
12440  my $tmpdir = $self->{tmpdir};
12441
12442  my $config_file = "$tmpdir/sqlite.conf";
12443  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
12444  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
12445
12446  my $log_file = test_get_logfile();
12447
12448  my $user = 'proftpd';
12449  my $passwd = 'test';
12450  my $group = 'ftpd';
12451  my $home_dir = File::Spec->rel2abs($tmpdir);
12452  my $uid = 500;
12453  my $gid = 500;
12454
12455  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
12456
12457  # Build up sqlite3 command to create users, groups tables and populate them
12458  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
12459
12460  if (open(my $fh, "> $db_script")) {
12461    print $fh <<EOS;
12462CREATE TABLE users (
12463  userid TEXT,
12464  passwd TEXT,
12465  uid INTEGER,
12466  gid INTEGER,
12467  homedir TEXT,
12468  shell TEXT
12469);
12470CREATE INDEX i_users_userid ON users.userid;
12471CREATE INDEX i_users_uid ON users.uid;
12472INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
12473
12474CREATE TABLE groups (
12475  groupname TEXT,
12476  gid INTEGER,
12477  members TEXT
12478);
12479CREATE INDEX i_groups_gid ON groups.gid;
12480INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
12481
12482CREATE TABLE user_hosts (
12483  session_id TEXT,
12484  userid TEXT,
12485  host TEXT
12486);
12487CREATE INDEX i_user_hosts_userid ON user_hosts.userid;
12488INSERT INTO user_hosts (session_id, userid, host) VALUES ('abc', '$user', '127.0.0.1');
12489
12490EOS
12491
12492    unless (close($fh)) {
12493      die("Can't write $db_script: $!");
12494    }
12495
12496  } else {
12497    die("Can't open $db_script: $!");
12498  }
12499
12500  my $cmd = "sqlite3 $db_file < $db_script";
12501  build_db($cmd, $db_script);
12502
12503  # Make sure that, if we're running as root, the database file has
12504  # the permissions/privs set for use by proftpd
12505  if ($< == 0) {
12506    unless (chmod(0666, $db_file)) {
12507      die("Can't set perms on $db_file to 0666: $!");
12508    }
12509  }
12510
12511  my $config = {
12512    PidFile => $pid_file,
12513    ScoreboardFile => $scoreboard_file,
12514    SystemLog => $log_file,
12515    TraceLog => $log_file,
12516    Trace => 'response:20 sql:20',
12517
12518    IfModules => {
12519      'mod_delay.c' => {
12520        DelayEngine => 'off',
12521      },
12522
12523      'mod_sql.c' => [
12524        'AuthOrder mod_sql.c',
12525
12526        'SQLAuthenticate users groups',
12527        'SQLAuthTypes plaintext',
12528        'SQLBackend sqlite3',
12529        "SQLConnectInfo $db_file",
12530        "SQLLogFile $log_file",
12531        'SQLMinID 100',
12532
12533        # Here, the MaxHostsPerUser limit is 3; it has to be hardcoded into
12534        # the SQL query, unfortunately.  (Or maybe use a config file variable?)
12535        #
12536        # The LEFT JOIN is necessary for dealing with the case where the
12537        # user_hosts table has no matching rows for the user logging in.
12538        'SQLNamedQuery get-user-by-name SELECT "u.userid, u.passwd, u.uid, u.gid, u.homedir, u.shell, COUNT(uh.session_id) AS count FROM users u JOIN user_hosts uh ON u.userid = \"%U\" GROUP BY u.userid HAVING (count >= 0 AND count < 4)"',
12539        'SQLNamedQuery get-user-by-id SELECT "userid, passwd, uid, gid, homedir, shell FROM users WHERE uid = %{0}"',
12540        'SQLUserInfo custom:/get-user-by-name/get-user-by-id',
12541
12542        'SQLNamedQuery add-user-host FREEFORM "INSERT INTO user_hosts (session_id, userid, host) VALUES (\'%{env:UNIQUE_ID}\', \'%u\', \'%a\')"',
12543        'SQLLog PASS add-user-host',
12544
12545        'SQLNamedQuery remove-user-host FREEFORM "DELETE FROM user_hosts WHERE session_id = \"%{env:UNIQUE_ID}\"',
12546        'SQLLog EXIT remove-user-host',
12547
12548        'SQLShowInfo ERR_PASS 530 "Sorry, the maximum number of hosts for this user are already connected."',
12549      ],
12550    },
12551  };
12552
12553  my ($port, $config_user, $config_group) = config_write($config_file, $config);
12554
12555  # Open pipes, for use between the parent and child processes.  Specifically,
12556  # the child will indicate when it's done with its test by writing a message
12557  # to the parent.
12558  my ($rfh, $wfh);
12559  unless (pipe($rfh, $wfh)) {
12560    die("Can't open pipe: $!");
12561  }
12562
12563  my $ex;
12564
12565  # Fork child
12566  $self->handle_sigchld();
12567  defined(my $pid = fork()) or die("Can't fork: $!");
12568  if ($pid) {
12569    eval {
12570      my $client1 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12571      $client1->login($user, $passwd);
12572
12573      my $client2 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12574      $client2->login($user, $passwd);
12575
12576      my $client3 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12577      $client3->login($user, $passwd);
12578
12579      my $client4 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12580      eval { $client4->login($user, $passwd) };
12581      unless ($@) {
12582        die("Fourth login succeeded unexpectedly");
12583      }
12584
12585      my $resp_code = $client4->response_code();
12586      my $resp_msgs = $client4->response_msgs();
12587
12588      my $expected = 530;
12589      $self->assert($expected == $resp_code,
12590        test_msg("Expected response code $expected, got $resp_code"));
12591
12592      $expected = "Login incorrect.";
12593      $self->assert($expected eq $resp_msgs->[0],
12594        test_msg("Expected response '$expected', got '$resp_msgs->[0]'"));
12595
12596      $expected = "Sorry, the maximum number of hosts for this user are already connected.";
12597      $self->assert($expected eq $resp_msgs->[1],
12598        test_msg("Expected response '$expected', got '$resp_msgs->[1]'"));
12599
12600      $client4->quit();
12601
12602      $client3->quit();
12603
12604      $client4 = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12605      $client4->login($user, $passwd);
12606      $client4->quit();
12607
12608      $client2->quit();
12609      $client1->quit();
12610    };
12611
12612    if ($@) {
12613      $ex = $@;
12614    }
12615
12616    $wfh->print("done\n");
12617    $wfh->flush();
12618
12619  } else {
12620    eval { server_wait($config_file, $rfh) };
12621    if ($@) {
12622      warn($@);
12623      exit 1;
12624    }
12625
12626    exit 0;
12627  }
12628
12629  # Stop server
12630  server_stop($pid_file);
12631
12632  $self->assert_child_ok($pid);
12633
12634  if ($ex) {
12635    test_append_logfile($log_file, $ex);
12636    unlink($log_file);
12637
12638    die($ex);
12639  }
12640
12641  unlink($log_file);
12642}
12643
12644sub get_logins {
12645  my $db_file = shift;
12646  my $where = shift;
12647
12648  my $sql = "SELECT user, ip_addr, ts FROM ftpsessions";
12649  if ($where) {
12650    $sql .= " WHERE $where";
12651  }
12652
12653  my $cmd = "sqlite3 $db_file \"$sql\"";
12654
12655  if ($ENV{TEST_VERBOSE}) {
12656    print STDERR "Executing sqlite3: $cmd\n";
12657  }
12658
12659  my $res = join('', `$cmd`);
12660  chomp($res);
12661
12662  # The default sqlite3 delimiter is '|'
12663  return split(/\|/, $res);
12664}
12665
12666sub sql_sqllog_var_micros_ts_bug3889 {
12667  my $self = shift;
12668  my $tmpdir = $self->{tmpdir};
12669
12670  my $config_file = "$tmpdir/sqlite.conf";
12671  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
12672  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
12673
12674  my $log_file = test_get_logfile();
12675
12676  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
12677  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
12678
12679  my $user = 'proftpd';
12680  my $passwd = 'test';
12681  my $group = 'ftpd';
12682  my $home_dir = File::Spec->rel2abs($tmpdir);
12683  my $uid = 500;
12684  my $gid = 500;
12685
12686  # Make sure that, if we're running as root, that the home directory has
12687  # permissions/privs set for the account we create
12688  if ($< == 0) {
12689    unless (chmod(0755, $home_dir)) {
12690      die("Can't set perms on $home_dir to 0755: $!");
12691    }
12692
12693    unless (chown($uid, $gid, $home_dir)) {
12694      die("Can't set owner of $home_dir to $uid/$gid: $!");
12695    }
12696  }
12697
12698  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
12699    '/bin/bash');
12700  auth_group_write($auth_group_file, $group, $gid, $user);
12701
12702  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
12703
12704  # Build up sqlite3 command to create users, groups tables and populate them
12705  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
12706
12707  if (open(my $fh, "> $db_script")) {
12708    print $fh <<EOS;
12709CREATE TABLE ftpsessions (
12710  user TEXT,
12711  ip_addr TEXT,
12712  ts TEXT
12713);
12714EOS
12715
12716    unless (close($fh)) {
12717      die("Can't write $db_script: $!");
12718    }
12719
12720  } else {
12721    die("Can't open $db_script: $!");
12722  }
12723
12724  my $cmd = "sqlite3 $db_file < $db_script";
12725  build_db($cmd, $db_script);
12726
12727  # Make sure that, if we're running as root, the database file has
12728  # the permissions/privs set for use by proftpd
12729  if ($< == 0) {
12730    unless (chmod(0666, $db_file)) {
12731      die("Can't set perms on $db_file to 0666: $!");
12732    }
12733  }
12734
12735  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
12736  if (open(my $fh, "> $src_file")) {
12737    close($fh);
12738
12739  } else {
12740    die("Can't open $src_file: $!");
12741  }
12742
12743  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
12744
12745  my $config = {
12746    PidFile => $pid_file,
12747    ScoreboardFile => $scoreboard_file,
12748    SystemLog => $log_file,
12749
12750    AuthUserFile => $auth_user_file,
12751    AuthGroupFile => $auth_group_file,
12752
12753    IfModules => {
12754      'mod_delay.c' => {
12755        DelayEngine => 'off',
12756      },
12757
12758      'mod_sql.c' => {
12759        SQLEngine => 'log',
12760        SQLBackend => 'sqlite3',
12761        SQLConnectInfo => $db_file,
12762        SQLLogFile => $log_file,
12763        SQLNamedQuery => 'login FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{microsecs}\')"',
12764        SQLLog => 'PASS login',
12765      },
12766    },
12767  };
12768
12769  my ($port, $config_user, $config_group) = config_write($config_file, $config);
12770
12771  # Open pipes, for use between the parent and child processes.  Specifically,
12772  # the child will indicate when it's done with its test by writing a message
12773  # to the parent.
12774  my ($rfh, $wfh);
12775  unless (pipe($rfh, $wfh)) {
12776    die("Can't open pipe: $!");
12777  }
12778
12779  my $ex;
12780
12781  # Fork child
12782  $self->handle_sigchld();
12783  defined(my $pid = fork()) or die("Can't fork: $!");
12784  if ($pid) {
12785    eval {
12786      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12787      $client->login($user, $passwd);
12788      $client->quit();
12789    };
12790
12791    if ($@) {
12792      $ex = $@;
12793    }
12794
12795    $wfh->print("done\n");
12796    $wfh->flush();
12797
12798  } else {
12799    eval { server_wait($config_file, $rfh) };
12800    if ($@) {
12801      warn($@);
12802      exit 1;
12803    }
12804
12805    exit 0;
12806  }
12807
12808  # Stop server
12809  server_stop($pid_file);
12810
12811  $self->assert_child_ok($pid);
12812
12813  if ($ex) {
12814    test_append_logfile($log_file, $ex);
12815    unlink($log_file);
12816
12817    die($ex);
12818  }
12819
12820  my ($login, $ip_addr, $ts) = get_logins($db_file, "user = \'$user\'");
12821
12822  my $expected;
12823
12824  $expected = $user;
12825  $self->assert($expected eq $login,
12826    test_msg("Expected login user '$expected', got '$login'"));
12827
12828  $expected = '127.0.0.1';
12829  $self->assert($expected eq $ip_addr,
12830    test_msg("Expected IP address '$expected', got '$ip_addr'"));
12831
12832  $expected = '\d{6}';
12833  $self->assert(qr/$expected/, $ts,
12834    test_msg("Expected timestamp '$expected', got '$ts'"));
12835
12836  unlink($log_file);
12837}
12838
12839sub sql_sqllog_var_millis_ts_bug3889 {
12840  my $self = shift;
12841  my $tmpdir = $self->{tmpdir};
12842
12843  my $config_file = "$tmpdir/sqlite.conf";
12844  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
12845  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
12846
12847  my $log_file = test_get_logfile();
12848
12849  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
12850  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
12851
12852  my $user = 'proftpd';
12853  my $passwd = 'test';
12854  my $group = 'ftpd';
12855  my $home_dir = File::Spec->rel2abs($tmpdir);
12856  my $uid = 500;
12857  my $gid = 500;
12858
12859  # Make sure that, if we're running as root, that the home directory has
12860  # permissions/privs set for the account we create
12861  if ($< == 0) {
12862    unless (chmod(0755, $home_dir)) {
12863      die("Can't set perms on $home_dir to 0755: $!");
12864    }
12865
12866    unless (chown($uid, $gid, $home_dir)) {
12867      die("Can't set owner of $home_dir to $uid/$gid: $!");
12868    }
12869  }
12870
12871  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
12872    '/bin/bash');
12873  auth_group_write($auth_group_file, $group, $gid, $user);
12874
12875  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
12876
12877  # Build up sqlite3 command to create users, groups tables and populate them
12878  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
12879
12880  if (open(my $fh, "> $db_script")) {
12881    print $fh <<EOS;
12882CREATE TABLE ftpsessions (
12883  user TEXT,
12884  ip_addr TEXT,
12885  ts TEXT
12886);
12887EOS
12888
12889    unless (close($fh)) {
12890      die("Can't write $db_script: $!");
12891    }
12892
12893  } else {
12894    die("Can't open $db_script: $!");
12895  }
12896
12897  my $cmd = "sqlite3 $db_file < $db_script";
12898  build_db($cmd, $db_script);
12899
12900  # Make sure that, if we're running as root, the database file has
12901  # the permissions/privs set for use by proftpd
12902  if ($< == 0) {
12903    unless (chmod(0666, $db_file)) {
12904      die("Can't set perms on $db_file to 0666: $!");
12905    }
12906  }
12907
12908  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
12909  if (open(my $fh, "> $src_file")) {
12910    close($fh);
12911
12912  } else {
12913    die("Can't open $src_file: $!");
12914  }
12915
12916  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
12917
12918  my $config = {
12919    PidFile => $pid_file,
12920    ScoreboardFile => $scoreboard_file,
12921    SystemLog => $log_file,
12922
12923    AuthUserFile => $auth_user_file,
12924    AuthGroupFile => $auth_group_file,
12925
12926    IfModules => {
12927      'mod_delay.c' => {
12928        DelayEngine => 'off',
12929      },
12930
12931      'mod_sql.c' => {
12932        SQLEngine => 'log',
12933        SQLBackend => 'sqlite3',
12934        SQLConnectInfo => $db_file,
12935        SQLLogFile => $log_file,
12936        SQLNamedQuery => 'login FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{millisecs}\')"',
12937        SQLLog => 'PASS login',
12938      },
12939    },
12940  };
12941
12942  my ($port, $config_user, $config_group) = config_write($config_file, $config);
12943
12944  # Open pipes, for use between the parent and child processes.  Specifically,
12945  # the child will indicate when it's done with its test by writing a message
12946  # to the parent.
12947  my ($rfh, $wfh);
12948  unless (pipe($rfh, $wfh)) {
12949    die("Can't open pipe: $!");
12950  }
12951
12952  my $ex;
12953
12954  # Fork child
12955  $self->handle_sigchld();
12956  defined(my $pid = fork()) or die("Can't fork: $!");
12957  if ($pid) {
12958    eval {
12959      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
12960      $client->login($user, $passwd);
12961      $client->quit();
12962    };
12963
12964    if ($@) {
12965      $ex = $@;
12966    }
12967
12968    $wfh->print("done\n");
12969    $wfh->flush();
12970
12971  } else {
12972    eval { server_wait($config_file, $rfh) };
12973    if ($@) {
12974      warn($@);
12975      exit 1;
12976    }
12977
12978    exit 0;
12979  }
12980
12981  # Stop server
12982  server_stop($pid_file);
12983
12984  $self->assert_child_ok($pid);
12985
12986  if ($ex) {
12987    test_append_logfile($log_file, $ex);
12988    unlink($log_file);
12989
12990    die($ex);
12991  }
12992
12993  my ($login, $ip_addr, $ts) = get_logins($db_file, "user = \'$user\'");
12994
12995  my $expected;
12996
12997  $expected = $user;
12998  $self->assert($expected eq $login,
12999    test_msg("Expected login user '$expected', got '$login'"));
13000
13001  $expected = '127.0.0.1';
13002  $self->assert($expected eq $ip_addr,
13003    test_msg("Expected IP address '$expected', got '$ip_addr'"));
13004
13005  $expected = '\d{3}';
13006  $self->assert(qr/$expected/, $ts,
13007    test_msg("Expected timestamp '$expected', got '$ts'"));
13008
13009  unlink($log_file);
13010}
13011
13012sub sql_sqllog_var_iso8601_ts_bug3889 {
13013  my $self = shift;
13014  my $tmpdir = $self->{tmpdir};
13015
13016  my $config_file = "$tmpdir/sqlite.conf";
13017  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
13018  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
13019
13020  my $log_file = test_get_logfile();
13021
13022  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
13023  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
13024
13025  my $user = 'proftpd';
13026  my $passwd = 'test';
13027  my $group = 'ftpd';
13028  my $home_dir = File::Spec->rel2abs($tmpdir);
13029  my $uid = 500;
13030  my $gid = 500;
13031
13032  # Make sure that, if we're running as root, that the home directory has
13033  # permissions/privs set for the account we create
13034  if ($< == 0) {
13035    unless (chmod(0755, $home_dir)) {
13036      die("Can't set perms on $home_dir to 0755: $!");
13037    }
13038
13039    unless (chown($uid, $gid, $home_dir)) {
13040      die("Can't set owner of $home_dir to $uid/$gid: $!");
13041    }
13042  }
13043
13044  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
13045    '/bin/bash');
13046  auth_group_write($auth_group_file, $group, $gid, $user);
13047
13048  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13049
13050  # Build up sqlite3 command to create users, groups tables and populate them
13051  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13052
13053  if (open(my $fh, "> $db_script")) {
13054    print $fh <<EOS;
13055CREATE TABLE ftpsessions (
13056  user TEXT,
13057  ip_addr TEXT,
13058  ts TEXT
13059);
13060EOS
13061
13062    unless (close($fh)) {
13063      die("Can't write $db_script: $!");
13064    }
13065
13066  } else {
13067    die("Can't open $db_script: $!");
13068  }
13069
13070  my $cmd = "sqlite3 $db_file < $db_script";
13071  build_db($cmd, $db_script);
13072
13073  # Make sure that, if we're running as root, the database file has
13074  # the permissions/privs set for use by proftpd
13075  if ($< == 0) {
13076    unless (chmod(0666, $db_file)) {
13077      die("Can't set perms on $db_file to 0666: $!");
13078    }
13079  }
13080
13081  my $src_file = File::Spec->rel2abs("$tmpdir/test.txt");
13082  if (open(my $fh, "> $src_file")) {
13083    close($fh);
13084
13085  } else {
13086    die("Can't open $src_file: $!");
13087  }
13088
13089  my $dst_file = File::Spec->rel2abs("$tmpdir/foo.txt");
13090
13091  my $config = {
13092    PidFile => $pid_file,
13093    ScoreboardFile => $scoreboard_file,
13094    SystemLog => $log_file,
13095
13096    AuthUserFile => $auth_user_file,
13097    AuthGroupFile => $auth_group_file,
13098
13099    IfModules => {
13100      'mod_delay.c' => {
13101        DelayEngine => 'off',
13102      },
13103
13104      'mod_sql.c' => {
13105        SQLEngine => 'log',
13106        SQLBackend => 'sqlite3',
13107        SQLConnectInfo => $db_file,
13108        SQLLogFile => $log_file,
13109        SQLNamedQuery => 'login FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ts) VALUES (\'%u\', \'%L\', \'%{iso8601}\')"',
13110        SQLLog => 'PASS login',
13111      },
13112    },
13113  };
13114
13115  my ($port, $config_user, $config_group) = config_write($config_file, $config);
13116
13117  # Open pipes, for use between the parent and child processes.  Specifically,
13118  # the child will indicate when it's done with its test by writing a message
13119  # to the parent.
13120  my ($rfh, $wfh);
13121  unless (pipe($rfh, $wfh)) {
13122    die("Can't open pipe: $!");
13123  }
13124
13125  my $ex;
13126
13127  # Fork child
13128  $self->handle_sigchld();
13129  defined(my $pid = fork()) or die("Can't fork: $!");
13130  if ($pid) {
13131    eval {
13132      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13133      $client->login($user, $passwd);
13134      $client->quit();
13135    };
13136
13137    if ($@) {
13138      $ex = $@;
13139    }
13140
13141    $wfh->print("done\n");
13142    $wfh->flush();
13143
13144  } else {
13145    eval { server_wait($config_file, $rfh) };
13146    if ($@) {
13147      warn($@);
13148      exit 1;
13149    }
13150
13151    exit 0;
13152  }
13153
13154  # Stop server
13155  server_stop($pid_file);
13156
13157  $self->assert_child_ok($pid);
13158
13159  if ($ex) {
13160    test_append_logfile($log_file, $ex);
13161    unlink($log_file);
13162
13163    die($ex);
13164  }
13165
13166  my ($login, $ip_addr, $ts) = get_logins($db_file, "user = \'$user\'");
13167
13168  my $expected;
13169
13170  $expected = $user;
13171  $self->assert($expected eq $login,
13172    test_msg("Expected login user '$expected', got '$login'"));
13173
13174  $expected = '127.0.0.1';
13175  $self->assert($expected eq $ip_addr,
13176    test_msg("Expected IP address '$expected', got '$ip_addr'"));
13177
13178  $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2},\d{3}';
13179  $self->assert(qr/$expected/, $ts,
13180    test_msg("Expected timestamp '$expected', got '$ts'"));
13181
13182  unlink($log_file);
13183}
13184
13185sub sql_sqllogonevent_bug3893 {
13186  my $self = shift;
13187  my $tmpdir = $self->{tmpdir};
13188  my $setup = test_setup($tmpdir, 'sqlite');
13189
13190  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13191
13192  # Build up sqlite3 command to create users, groups tables and populate them
13193  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13194
13195  if (open(my $fh, "> $db_script")) {
13196    print $fh <<EOS;
13197CREATE TABLE ftpsessions (
13198  user TEXT,
13199  ip_addr TEXT,
13200  timestamp TEXT
13201);
13202EOS
13203
13204    unless (close($fh)) {
13205      die("Can't write $db_script: $!");
13206    }
13207
13208  } else {
13209    die("Can't open $db_script: $!");
13210  }
13211
13212  my $cmd = "sqlite3 $db_file < $db_script";
13213  build_db($cmd, $db_script);
13214
13215  # Make sure that, if we're running as root, the database file has
13216  # the permissions/privs set for use by proftpd
13217  if ($< == 0) {
13218    unless (chmod(0666, $db_file)) {
13219      die("Can't set perms on $db_file to 0666: $!");
13220    }
13221  }
13222
13223  my $max_login_attempts = 2;
13224
13225  my $config = {
13226    PidFile => $setup->{pid_file},
13227    ScoreboardFile => $setup->{scoreboard_file},
13228    SystemLog => $setup->{log_file},
13229    TraceLog => $setup->{log_file},
13230    Trace => 'jot:20 sql:20 sql.sqlite:20',
13231
13232    AuthOrder => 'mod_auth_file.c',
13233    AuthUserFile => $setup->{auth_user_file},
13234    AuthGroupFile => $setup->{auth_group_file},
13235    MaxLoginAttempts => $max_login_attempts,
13236
13237    IfModules => {
13238      'mod_delay.c' => {
13239        DelayEngine => 'off',
13240      },
13241
13242      'mod_sql.c' => {
13243        SQLEngine => 'log',
13244        SQLBackend => 'sqlite3',
13245        SQLConnectInfo => $db_file,
13246        SQLLogFile => $setup->{log_file},
13247        SQLNamedQuery => 'max_logins_exceeded FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%U\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
13248        SQLLogOnEvent => 'MaxLoginAttempts max_logins_exceeded',
13249      },
13250    },
13251  };
13252
13253  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
13254    $config);
13255
13256  # Open pipes, for use between the parent and child processes.  Specifically,
13257  # the child will indicate when it's done with its test by writing a message
13258  # to the parent.
13259  my ($rfh, $wfh);
13260  unless (pipe($rfh, $wfh)) {
13261    die("Can't open pipe: $!");
13262  }
13263
13264  my $ex;
13265
13266  # Fork child
13267  $self->handle_sigchld();
13268  defined(my $pid = fork()) or die("Can't fork: $!");
13269  if ($pid) {
13270    eval {
13271      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13272      eval { $client->login($setup->{user}, 'badpassword') };
13273      unless ($@) {
13274        die("Login succeeded unexpectedly");
13275      }
13276
13277      eval { $client->login($setup->{user}, 'badpassword') };
13278      unless ($@) {
13279        die("Login succeeded unexpectedly");
13280      }
13281    };
13282    if ($@) {
13283      $ex = $@;
13284    }
13285
13286    $wfh->print("done\n");
13287    $wfh->flush();
13288
13289  } else {
13290    eval { server_wait($setup->{config_file}, $rfh) };
13291    if ($@) {
13292      warn($@);
13293      exit 1;
13294    }
13295
13296    exit 0;
13297  }
13298
13299  # Stop server
13300  server_stop($setup->{pid_file});
13301  $self->assert_child_ok($pid);
13302
13303  if ($ex) {
13304    test_cleanup($setup->{log_file}, $ex);
13305  }
13306
13307  eval {
13308    my ($login, $ip_addr, $timestamp) = get_sessions($db_file,
13309      "user = \'$setup->{user}\'");
13310
13311    my $expected = $setup->{user};
13312    $self->assert($expected eq $login,
13313      test_msg("Expected '$expected', got '$login'"));
13314
13315    $expected = '127.0.0.1';
13316    $self->assert($expected eq $ip_addr,
13317      test_msg("Expected '$expected', got '$ip_addr'"));
13318
13319    $expected = '\d{4}\-\d{2}\-\d{2} \d{2}:\d{2}:\d{2}';
13320    $self->assert(qr/$expected/, $timestamp,
13321      test_msg("Expected '$expected', got '$timestamp'"));
13322  };
13323  if ($@) {
13324    $ex = $@;
13325  }
13326
13327  test_cleanup($setup->{log_file}, $ex);
13328}
13329
13330sub get_session_with_primary_key {
13331  my $db_file = shift;
13332  my $where = shift;
13333
13334  my $sql = "SELECT name, ip_addr, primary_key FROM sessions";
13335  if ($where) {
13336    $sql .= " WHERE $where";
13337  }
13338
13339  my $cmd = "sqlite3 $db_file \"$sql\"";
13340
13341  if ($ENV{TEST_VERBOSE}) {
13342    print STDERR "Executing sqlite3: $cmd\n";
13343  }
13344
13345  my $res = join('', `$cmd`);
13346  chomp($res);
13347
13348  # The default sqlite3 delimiter is '|'
13349  return split(/\|/, $res);
13350}
13351
13352sub sql_userprimarykey_bug3864 {
13353  my $self = shift;
13354  my $tmpdir = $self->{tmpdir};
13355
13356  my $config_file = "$tmpdir/sqlite.conf";
13357  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
13358  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
13359
13360  my $log_file = test_get_logfile();
13361
13362  my $user = 'proftpd';
13363  my $passwd = 'test';
13364  my $group = 'ftpd';
13365  my $home_dir = File::Spec->rel2abs($tmpdir);
13366  my $uid = 500;
13367  my $gid = 500;
13368
13369  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13370
13371  # Build up sqlite3 command to create users, groups tables and populate them
13372  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13373
13374  if (open(my $fh, "> $db_script")) {
13375    print $fh <<EOS;
13376CREATE TABLE users (
13377  userid TEXT,
13378  passwd TEXT,
13379  uid INTEGER,
13380  gid INTEGER,
13381  homedir TEXT,
13382  shell TEXT
13383);
13384INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
13385
13386CREATE TABLE groups (
13387  groupname TEXT,
13388  gid INTEGER,
13389  members TEXT
13390);
13391INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
13392
13393CREATE TABLE sessions (
13394  name TEXT,
13395  ip_addr TEXT,
13396  primary_key INTEGER
13397);
13398EOS
13399
13400    unless (close($fh)) {
13401      die("Can't write $db_script: $!");
13402    }
13403
13404  } else {
13405    die("Can't open $db_script: $!");
13406  }
13407
13408  my $cmd = "sqlite3 $db_file < $db_script";
13409  build_db($cmd, $db_script);
13410
13411  # Make sure that, if we're running as root, the database file has
13412  # the permissions/privs set for use by proftpd
13413  if ($< == 0) {
13414    unless (chmod(0666, $db_file)) {
13415      die("Can't set perms on $db_file to 0666: $!");
13416    }
13417  }
13418
13419  my $config = {
13420    PidFile => $pid_file,
13421    ScoreboardFile => $scoreboard_file,
13422    SystemLog => $log_file,
13423    TraceLog => $log_file,
13424    Trace => 'sql:20',
13425
13426    IfModules => {
13427      'mod_delay.c' => {
13428        DelayEngine => 'off',
13429      },
13430
13431      'mod_sql.c' => {
13432        SQLAuthTypes => 'plaintext',
13433        SQLBackend => 'sqlite3',
13434        SQLConnectInfo => $db_file,
13435        SQLLogFile => $log_file,
13436        SQLMinID => 200,
13437
13438        SQLUserPrimaryKey => 'uid',
13439
13440        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO sessions (name, ip_addr, primary_key) VALUES (\'%u\', \'%L\', %{note:sql.user-primary-key})"',
13441        SQLLog => 'PASS session_start',
13442      },
13443    },
13444  };
13445
13446  my ($port, $config_user, $config_group) = config_write($config_file, $config);
13447
13448  # Open pipes, for use between the parent and child processes.  Specifically,
13449  # the child will indicate when it's done with its test by writing a message
13450  # to the parent.
13451  my ($rfh, $wfh);
13452  unless (pipe($rfh, $wfh)) {
13453    die("Can't open pipe: $!");
13454  }
13455
13456  my $ex;
13457
13458  # Fork child
13459  $self->handle_sigchld();
13460  defined(my $pid = fork()) or die("Can't fork: $!");
13461  if ($pid) {
13462    eval {
13463      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13464      $client->login($user, $passwd);
13465      $client->quit();
13466    };
13467
13468    if ($@) {
13469      $ex = $@;
13470    }
13471
13472    $wfh->print("done\n");
13473    $wfh->flush();
13474
13475  } else {
13476    eval { server_wait($config_file, $rfh) };
13477    if ($@) {
13478      warn($@);
13479      exit 1;
13480    }
13481
13482    exit 0;
13483  }
13484
13485  # Stop server
13486  server_stop($pid_file);
13487
13488  $self->assert_child_ok($pid);
13489
13490  if ($ex) {
13491    test_append_logfile($log_file, $ex);
13492    unlink($log_file);
13493
13494    die($ex);
13495  }
13496
13497  eval {
13498    my ($name, $ip_addr, $primary_key) = get_session_with_primary_key($db_file,
13499      "name = \'$user\'");
13500
13501    my $expected;
13502
13503    $expected = $user;
13504    $self->assert($expected eq $name,
13505      test_msg("Expected name '$expected', got '$name'"));
13506
13507    $expected = '127.0.0.1';
13508    $self->assert($expected eq $ip_addr,
13509      test_msg("Expected IP address '$expected', got '$ip_addr'"));
13510
13511    $expected = $uid;
13512    $self->assert($expected == $primary_key,
13513      test_msg("Expected primary key $expected, got $primary_key"));
13514  };
13515  if ($@) {
13516    $ex = $@;
13517  }
13518
13519  if ($ex) {
13520    test_append_logfile($log_file, $ex);
13521    unlink($log_file);
13522
13523    die($ex);
13524  }
13525
13526  unlink($log_file);
13527}
13528
13529sub sql_userprimarykey_custom_bug3864 {
13530  my $self = shift;
13531  my $tmpdir = $self->{tmpdir};
13532
13533  my $config_file = "$tmpdir/sqlite.conf";
13534  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
13535  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
13536
13537  my $log_file = test_get_logfile();
13538
13539  my $user = 'proftpd';
13540  my $passwd = 'test';
13541  my $group = 'ftpd';
13542  my $home_dir = File::Spec->rel2abs($tmpdir);
13543  my $uid = 500;
13544  my $gid = 500;
13545
13546  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13547
13548  # Build up sqlite3 command to create users, groups tables and populate them
13549  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13550
13551  if (open(my $fh, "> $db_script")) {
13552    print $fh <<EOS;
13553CREATE TABLE users (
13554  userid TEXT,
13555  passwd TEXT,
13556  uid INTEGER,
13557  gid INTEGER,
13558  homedir TEXT,
13559  shell TEXT,
13560  primary_key INTEGER
13561);
13562INSERT INTO users (userid, passwd, uid, gid, homedir, shell, primary_key) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash', $uid);
13563
13564CREATE TABLE groups (
13565  groupname TEXT,
13566  gid INTEGER,
13567  members TEXT
13568);
13569INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
13570
13571CREATE TABLE sessions (
13572  name TEXT,
13573  ip_addr TEXT,
13574  primary_key INTEGER
13575);
13576EOS
13577
13578    unless (close($fh)) {
13579      die("Can't write $db_script: $!");
13580    }
13581
13582  } else {
13583    die("Can't open $db_script: $!");
13584  }
13585
13586  my $cmd = "sqlite3 $db_file < $db_script";
13587  build_db($cmd, $db_script);
13588
13589  # Make sure that, if we're running as root, the database file has
13590  # the permissions/privs set for use by proftpd
13591  if ($< == 0) {
13592    unless (chmod(0666, $db_file)) {
13593      die("Can't set perms on $db_file to 0666: $!");
13594    }
13595  }
13596
13597  my $config = {
13598    PidFile => $pid_file,
13599    ScoreboardFile => $scoreboard_file,
13600    SystemLog => $log_file,
13601    TraceLog => $log_file,
13602    Trace => 'sql:20',
13603
13604    IfModules => {
13605      'mod_delay.c' => {
13606        DelayEngine => 'off',
13607      },
13608
13609      'mod_sql.c' => [
13610        'SQLAuthTypes plaintext',
13611        'SQLBackend sqlite3',
13612        "SQLConnectInfo $db_file",
13613        "SQLLogFile $log_file",
13614        'SQLMinID 200',
13615
13616        'SQLNamedQuery get-user-primary-key SELECT "primary_key FROM users WHERE userid = \'%{0}\'"',
13617        'SQLUserPrimaryKey custom:/get-user-primary-key',
13618
13619        'SQLNamedQuery session_start FREEFORM "INSERT INTO sessions (name, ip_addr, primary_key) VALUES (\'%u\', \'%L\', %{note:sql.user-primary-key})"',
13620        'SQLLog PASS session_start',
13621      ],
13622    },
13623  };
13624
13625  my ($port, $config_user, $config_group) = config_write($config_file, $config);
13626
13627  # Open pipes, for use between the parent and child processes.  Specifically,
13628  # the child will indicate when it's done with its test by writing a message
13629  # to the parent.
13630  my ($rfh, $wfh);
13631  unless (pipe($rfh, $wfh)) {
13632    die("Can't open pipe: $!");
13633  }
13634
13635  my $ex;
13636
13637  # Fork child
13638  $self->handle_sigchld();
13639  defined(my $pid = fork()) or die("Can't fork: $!");
13640  if ($pid) {
13641    eval {
13642      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13643      $client->login($user, $passwd);
13644      $client->quit();
13645    };
13646
13647    if ($@) {
13648      $ex = $@;
13649    }
13650
13651    $wfh->print("done\n");
13652    $wfh->flush();
13653
13654  } else {
13655    eval { server_wait($config_file, $rfh) };
13656    if ($@) {
13657      warn($@);
13658      exit 1;
13659    }
13660
13661    exit 0;
13662  }
13663
13664  # Stop server
13665  server_stop($pid_file);
13666
13667  $self->assert_child_ok($pid);
13668
13669  if ($ex) {
13670    test_append_logfile($log_file, $ex);
13671    unlink($log_file);
13672
13673    die($ex);
13674  }
13675
13676  eval {
13677    my ($name, $ip_addr, $primary_key) = get_session_with_primary_key($db_file,
13678      "name = \'$user\'");
13679
13680    my $expected;
13681
13682    $expected = $user;
13683    $self->assert($expected eq $name,
13684      test_msg("Expected name '$expected', got '$name'"));
13685
13686    $expected = '127.0.0.1';
13687    $self->assert($expected eq $ip_addr,
13688      test_msg("Expected IP address '$expected', got '$ip_addr'"));
13689
13690    $expected = $uid;
13691    $self->assert($expected == $primary_key,
13692      test_msg("Expected primary key $expected, got $primary_key"));
13693  };
13694  if ($@) {
13695    $ex = $@;
13696  }
13697
13698  if ($ex) {
13699    test_append_logfile($log_file, $ex);
13700    unlink($log_file);
13701
13702    die($ex);
13703  }
13704
13705  unlink($log_file);
13706}
13707
13708sub sql_groupprimarykey_bug3864 {
13709  my $self = shift;
13710  my $tmpdir = $self->{tmpdir};
13711
13712  my $config_file = "$tmpdir/sqlite.conf";
13713  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
13714  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
13715
13716  my $log_file = test_get_logfile();
13717
13718  my $user = 'proftpd';
13719  my $passwd = 'test';
13720  my $group = 'ftpd';
13721  my $home_dir = File::Spec->rel2abs($tmpdir);
13722  my $uid = 500;
13723  my $gid = 500;
13724
13725  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13726
13727  # Build up sqlite3 command to create users, groups tables and populate them
13728  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13729
13730  if (open(my $fh, "> $db_script")) {
13731    print $fh <<EOS;
13732CREATE TABLE users (
13733  userid TEXT,
13734  passwd TEXT,
13735  uid INTEGER,
13736  gid INTEGER,
13737  homedir TEXT,
13738  shell TEXT
13739);
13740INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
13741
13742CREATE TABLE groups (
13743  groupname TEXT,
13744  gid INTEGER,
13745  members TEXT
13746);
13747INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
13748
13749CREATE TABLE sessions (
13750  name TEXT,
13751  ip_addr TEXT,
13752  primary_key INTEGER
13753);
13754EOS
13755
13756    unless (close($fh)) {
13757      die("Can't write $db_script: $!");
13758    }
13759
13760  } else {
13761    die("Can't open $db_script: $!");
13762  }
13763
13764  my $cmd = "sqlite3 $db_file < $db_script";
13765  build_db($cmd, $db_script);
13766
13767  # Make sure that, if we're running as root, the database file has
13768  # the permissions/privs set for use by proftpd
13769  if ($< == 0) {
13770    unless (chmod(0666, $db_file)) {
13771      die("Can't set perms on $db_file to 0666: $!");
13772    }
13773  }
13774
13775  my $config = {
13776    PidFile => $pid_file,
13777    ScoreboardFile => $scoreboard_file,
13778    SystemLog => $log_file,
13779    TraceLog => $log_file,
13780    Trace => 'sql:20',
13781
13782    IfModules => {
13783      'mod_delay.c' => {
13784        DelayEngine => 'off',
13785      },
13786
13787      'mod_sql.c' => {
13788        SQLAuthTypes => 'plaintext',
13789        SQLBackend => 'sqlite3',
13790        SQLConnectInfo => $db_file,
13791        SQLLogFile => $log_file,
13792        SQLMinID => 200,
13793
13794        SQLGroupPrimaryKey => 'gid',
13795
13796        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO sessions (name, ip_addr, primary_key) VALUES (\'%g\', \'%L\', %{note:sql.group-primary-key})"',
13797        SQLLog => 'PASS session_start',
13798      },
13799    },
13800  };
13801
13802  my ($port, $config_user, $config_group) = config_write($config_file, $config);
13803
13804  # Open pipes, for use between the parent and child processes.  Specifically,
13805  # the child will indicate when it's done with its test by writing a message
13806  # to the parent.
13807  my ($rfh, $wfh);
13808  unless (pipe($rfh, $wfh)) {
13809    die("Can't open pipe: $!");
13810  }
13811
13812  my $ex;
13813
13814  # Fork child
13815  $self->handle_sigchld();
13816  defined(my $pid = fork()) or die("Can't fork: $!");
13817  if ($pid) {
13818    eval {
13819      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13820      $client->login($user, $passwd);
13821      $client->quit();
13822    };
13823
13824    if ($@) {
13825      $ex = $@;
13826    }
13827
13828    $wfh->print("done\n");
13829    $wfh->flush();
13830
13831  } else {
13832    eval { server_wait($config_file, $rfh) };
13833    if ($@) {
13834      warn($@);
13835      exit 1;
13836    }
13837
13838    exit 0;
13839  }
13840
13841  # Stop server
13842  server_stop($pid_file);
13843
13844  $self->assert_child_ok($pid);
13845
13846  if ($ex) {
13847    test_append_logfile($log_file, $ex);
13848    unlink($log_file);
13849
13850    die($ex);
13851  }
13852
13853  eval {
13854    my ($name, $ip_addr, $primary_key) = get_session_with_primary_key($db_file,
13855      "name = \'$group\'");
13856
13857    my $expected;
13858
13859    $expected = $group;
13860    $self->assert($expected eq $name,
13861      test_msg("Expected name '$expected', got '$name'"));
13862
13863    $expected = '127.0.0.1';
13864    $self->assert($expected eq $ip_addr,
13865      test_msg("Expected IP address '$expected', got '$ip_addr'"));
13866
13867    $expected = $uid;
13868    $self->assert($expected == $primary_key,
13869      test_msg("Expected primary key $expected, got $primary_key"));
13870  };
13871  if ($@) {
13872    $ex = $@;
13873  }
13874
13875  if ($ex) {
13876    test_append_logfile($log_file, $ex);
13877    unlink($log_file);
13878
13879    die($ex);
13880  }
13881
13882  unlink($log_file);
13883}
13884
13885sub sql_groupprimarykey_custom_bug3864 {
13886  my $self = shift;
13887  my $tmpdir = $self->{tmpdir};
13888  my $setup = test_setup($tmpdir, 'sqlite');
13889
13890  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
13891
13892  # Build up sqlite3 command to create users, groups tables and populate them
13893  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
13894
13895  if (open(my $fh, "> $db_script")) {
13896    print $fh <<EOS;
13897CREATE TABLE users (
13898  userid TEXT,
13899  passwd TEXT,
13900  uid INTEGER,
13901  gid INTEGER,
13902  homedir TEXT,
13903  shell TEXT
13904);
13905INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash');
13906
13907CREATE TABLE groups (
13908  groupname TEXT,
13909  gid INTEGER,
13910  members TEXT,
13911  primary_key INTEGER
13912);
13913INSERT INTO groups (groupname, gid, members, primary_key) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}', $setup->{gid});
13914
13915CREATE TABLE sessions (
13916  name TEXT,
13917  ip_addr TEXT,
13918  primary_key INTEGER
13919);
13920EOS
13921    unless (close($fh)) {
13922      die("Can't write $db_script: $!");
13923    }
13924
13925  } else {
13926    die("Can't open $db_script: $!");
13927  }
13928
13929  my $cmd = "sqlite3 $db_file < $db_script";
13930  build_db($cmd, $db_script);
13931
13932  # Make sure that, if we're running as root, the database file has
13933  # the permissions/privs set for use by proftpd
13934  if ($< == 0) {
13935    unless (chmod(0666, $db_file)) {
13936      die("Can't set perms on $db_file to 0666: $!");
13937    }
13938  }
13939
13940  my $config = {
13941    PidFile => $setup->{pid_file},
13942    ScoreboardFile => $setup->{scoreboard_file},
13943    SystemLog => $setup->{log_file},
13944    TraceLog => $setup->{log_file},
13945    Trace => 'jot:20 sql:20',
13946
13947    IfModules => {
13948      'mod_delay.c' => {
13949        DelayEngine => 'off',
13950      },
13951
13952      'mod_sql.c' => [
13953        'SQLAuthTypes plaintext',
13954        'SQLBackend sqlite3',
13955        "SQLConnectInfo $db_file",
13956        "SQLLogFile $setup->{log_file}",
13957        'SQLMinID 200',
13958
13959        'SQLNamedQuery get-group-primary-key SELECT "primary_key from groups WHERE groupname = \'%{0}\'"',
13960        'SQLGroupPrimaryKey custom:/get-group-primary-key',
13961
13962        'SQLNamedQuery session_start FREEFORM "INSERT INTO sessions (name, ip_addr, primary_key) VALUES (\'%g\', \'%L\', %{note:sql.group-primary-key})"',
13963        'SQLLog PASS session_start',
13964      ],
13965    },
13966  };
13967
13968  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
13969    $config);
13970
13971  # Open pipes, for use between the parent and child processes.  Specifically,
13972  # the child will indicate when it's done with its test by writing a message
13973  # to the parent.
13974  my ($rfh, $wfh);
13975  unless (pipe($rfh, $wfh)) {
13976    die("Can't open pipe: $!");
13977  }
13978
13979  my $ex;
13980
13981  # Fork child
13982  $self->handle_sigchld();
13983  defined(my $pid = fork()) or die("Can't fork: $!");
13984  if ($pid) {
13985    eval {
13986      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
13987      $client->login($setup->{user}, $setup->{passwd});
13988      $client->quit();
13989    };
13990    if ($@) {
13991      $ex = $@;
13992    }
13993
13994    $wfh->print("done\n");
13995    $wfh->flush();
13996
13997  } else {
13998    eval { server_wait($setup->{config_file}, $rfh) };
13999    if ($@) {
14000      warn($@);
14001      exit 1;
14002    }
14003
14004    exit 0;
14005  }
14006
14007  # Stop server
14008  server_stop($setup->{pid_file});
14009  $self->assert_child_ok($pid);
14010
14011  if ($ex) {
14012    test_cleanup($setup->{log_file}, $ex);
14013  }
14014
14015  eval {
14016    my ($name, $ip_addr, $primary_key) = get_session_with_primary_key($db_file,
14017      "name = \'$setup->{group}\'");
14018
14019    my $expected = $setup->{group};
14020    $self->assert($expected eq $name, "Expected name '$expected', got '$name'");
14021
14022    $expected = '127.0.0.1';
14023    $self->assert($expected eq $ip_addr,
14024      "Expected IP address '$expected', got '$ip_addr'");
14025
14026    $expected = $setup->{gid};
14027    $self->assert($expected == $primary_key,
14028      "Expected primary key $expected, got $primary_key");
14029  };
14030  if ($@) {
14031    $ex = $@;
14032  }
14033
14034  test_cleanup($setup->{log_file}, $ex);
14035}
14036
14037sub sql_sqllog_var_basename_bug3987 {
14038  my $self = shift;
14039  my $tmpdir = $self->{tmpdir};
14040
14041  my $config_file = "$tmpdir/sqlite.conf";
14042  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
14043  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
14044
14045  my $log_file = test_get_logfile();
14046
14047  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
14048  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
14049
14050  my $user = 'proftpd';
14051  my $passwd = 'test';
14052  my $group = 'ftpd';
14053  my $home_dir = File::Spec->rel2abs($tmpdir);
14054  my $uid = 500;
14055  my $gid = 500;
14056
14057  # Make sure that, if we're running as root, that the home directory has
14058  # permissions/privs set for the account we create
14059  if ($< == 0) {
14060    unless (chmod(0755, $home_dir)) {
14061      die("Can't set perms on $home_dir to 0755: $!");
14062    }
14063
14064    unless (chown($uid, $gid, $home_dir)) {
14065      die("Can't set owner of $home_dir to $uid/$gid: $!");
14066    }
14067  }
14068
14069  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
14070    '/bin/bash');
14071  auth_group_write($auth_group_file, $group, $gid, $user);
14072
14073  my $test_file = File::Spec->rel2abs($config_file);
14074
14075  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14076
14077  # Build up sqlite3 command to create users, groups tables and populate them
14078  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14079
14080  if (open(my $fh, "> $db_script")) {
14081    print $fh <<EOS;
14082CREATE TABLE ftpsessions (
14083  user TEXT,
14084  ip_addr TEXT,
14085  dir TEXT
14086);
14087EOS
14088
14089    unless (close($fh)) {
14090      die("Can't write $db_script: $!");
14091    }
14092
14093  } else {
14094    die("Can't open $db_script: $!");
14095  }
14096
14097  my $cmd = "sqlite3 $db_file < $db_script";
14098  build_db($cmd, $db_script);
14099
14100  # Make sure that, if we're running as root, the database file has
14101  # the permissions/privs set for use by proftpd
14102  if ($< == 0) {
14103    unless (chmod(0666, $db_file)) {
14104      die("Can't set perms on $db_file to 0666: $!");
14105    }
14106  }
14107
14108  my $config = {
14109    PidFile => $pid_file,
14110    ScoreboardFile => $scoreboard_file,
14111    SystemLog => $log_file,
14112
14113    AuthUserFile => $auth_user_file,
14114    AuthGroupFile => $auth_group_file,
14115
14116    IfModules => {
14117      'mod_delay.c' => {
14118        DelayEngine => 'off',
14119      },
14120
14121      'mod_sql.c' => {
14122        SQLEngine => 'log',
14123        SQLBackend => 'sqlite3',
14124        SQLConnectInfo => $db_file,
14125        SQLLogFile => $log_file,
14126        SQLNamedQuery => 'filename FREEFORM "INSERT INTO ftpsessions (user, ip_addr, dir) VALUES (\'%u\', \'%L\', \'%{basename}\')"',
14127        SQLLog => 'RETR filename',
14128      },
14129    },
14130  };
14131
14132  my ($port, $config_user, $config_group) = config_write($config_file, $config);
14133
14134  # Open pipes, for use between the parent and child processes.  Specifically,
14135  # the child will indicate when it's done with its test by writing a message
14136  # to the parent.
14137  my ($rfh, $wfh);
14138  unless (pipe($rfh, $wfh)) {
14139    die("Can't open pipe: $!");
14140  }
14141
14142  my $ex;
14143
14144  # Fork child
14145  $self->handle_sigchld();
14146  defined(my $pid = fork()) or die("Can't fork: $!");
14147  if ($pid) {
14148    eval {
14149      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14150      $client->login($user, $passwd);
14151
14152      my $conn = $client->retr_raw($test_file);
14153      unless ($conn) {
14154        die("Failed to RETR: " . $client->response_code() . " " .
14155          $client->response_msg());
14156      }
14157
14158      my $buf;
14159      $conn->read($buf, 8192, 30);
14160      eval { $conn->close() };
14161
14162      my $resp_code = $client->response_code();
14163      my $resp_msg = $client->response_msg();
14164      $self->assert_transfer_ok($resp_code, $resp_msg);
14165
14166      $client->quit();
14167    };
14168
14169    if ($@) {
14170      $ex = $@;
14171    }
14172
14173    $wfh->print("done\n");
14174    $wfh->flush();
14175
14176  } else {
14177    eval { server_wait($config_file, $rfh) };
14178    if ($@) {
14179      warn($@);
14180      exit 1;
14181    }
14182
14183    exit 0;
14184  }
14185
14186  # Stop server
14187  server_stop($pid_file);
14188
14189  $self->assert_child_ok($pid);
14190
14191  if ($ex) {
14192    test_append_logfile($log_file, $ex);
14193    unlink($log_file);
14194
14195    die($ex);
14196  }
14197
14198  # Note: We are simply re-using the existing get_locations() function here
14199  # for convenience/expedience.
14200  my ($login, $ip_addr, $name) = get_locations($db_file, "user = \'$user\'");
14201
14202  my $expected;
14203
14204  $expected = $user;
14205  $self->assert($expected eq $login,
14206    test_msg("Expected '$expected', got '$login'"));
14207
14208  $expected = '127.0.0.1';
14209  $self->assert($expected eq $ip_addr,
14210    test_msg("Expected '$expected', got '$ip_addr'"));
14211
14212  $expected = 'sqlite.conf';
14213  $self->assert($expected eq $name,
14214    test_msg("Expected '$expected', got '$name'"));
14215
14216  unlink($log_file);
14217}
14218
14219sub sql_user_info_defaulthomedir_bug4083 {
14220  my $self = shift;
14221  my $tmpdir = $self->{tmpdir};
14222
14223  my $config_file = "$tmpdir/sqlite.conf";
14224  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
14225  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
14226
14227  my $log_file = test_get_logfile();
14228
14229  my $user = 'proftpd';
14230  my $passwd = 'test';
14231  my $group = 'ftpd';
14232  my $home_dir = File::Spec->rel2abs("$tmpdir/$user");
14233  mkpath($home_dir);
14234  my $uid = 500;
14235  my $gid = 500;
14236
14237  my $default_home = File::Spec->rel2abs($tmpdir) . '/%u';
14238
14239  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14240
14241  # Build up sqlite3 command to create users, groups tables and populate them
14242  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14243
14244  if (open(my $fh, "> $db_script")) {
14245    print $fh <<EOS;
14246CREATE TABLE users (
14247  userid TEXT,
14248  passwd TEXT,
14249  uid INTEGER,
14250  gid INTEGER
14251);
14252INSERT INTO users (userid, passwd, uid, gid) VALUES ('$user', '$passwd', $uid, $gid);
14253
14254EOS
14255
14256    unless (close($fh)) {
14257      die("Can't write $db_script: $!");
14258    }
14259
14260  } else {
14261    die("Can't open $db_script: $!");
14262  }
14263
14264  my $cmd = "sqlite3 $db_file < $db_script";
14265  build_db($cmd, $db_script);
14266
14267  # Make sure that, if we're running as root, the database file has
14268  # the permissions/privs set for use by proftpd
14269  if ($< == 0) {
14270    unless (chmod(0666, $db_file)) {
14271      die("Can't set perms on $db_file to 0666: $!");
14272    }
14273  }
14274
14275  my $config = {
14276    PidFile => $pid_file,
14277    ScoreboardFile => $scoreboard_file,
14278    SystemLog => $log_file,
14279
14280    IfModules => {
14281      'mod_delay.c' => {
14282        DelayEngine => 'off',
14283      },
14284
14285      'mod_sql.c' => {
14286        SQLAuthenticate => 'users',
14287        SQLAuthTypes => 'plaintext',
14288        SQLBackend => 'sqlite3',
14289        SQLConnectInfo => $db_file,
14290        SQLLogFile => $log_file,
14291        SQLUserInfo => 'users userid passwd uid gid Null Null',
14292        SQLDefaultHomedir => $default_home,
14293      },
14294    },
14295  };
14296
14297  my ($port, $config_user, $config_group) = config_write($config_file, $config);
14298
14299  # Open pipes, for use between the parent and child processes.  Specifically,
14300  # the child will indicate when it's done with its test by writing a message
14301  # to the parent.
14302  my ($rfh, $wfh);
14303  unless (pipe($rfh, $wfh)) {
14304    die("Can't open pipe: $!");
14305  }
14306
14307  my $ex;
14308
14309  # Fork child
14310  $self->handle_sigchld();
14311  defined(my $pid = fork()) or die("Can't fork: $!");
14312  if ($pid) {
14313    eval {
14314      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14315      $client->login($user, $passwd);
14316
14317      my $resp_msgs = $client->response_msgs();
14318      my $nmsgs = scalar(@$resp_msgs);
14319
14320      my $expected;
14321
14322      $expected = 1;
14323      $self->assert($expected == $nmsgs,
14324        test_msg("Expected $expected, got $nmsgs"));
14325
14326      $expected = "User proftpd logged in";
14327      $self->assert($expected eq $resp_msgs->[0],
14328        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
14329    };
14330
14331    if ($@) {
14332      $ex = $@;
14333    }
14334
14335    $wfh->print("done\n");
14336    $wfh->flush();
14337
14338  } else {
14339    eval { server_wait($config_file, $rfh) };
14340    if ($@) {
14341      warn($@);
14342      exit 1;
14343    }
14344
14345    exit 0;
14346  }
14347
14348  # Stop server
14349  server_stop($pid_file);
14350
14351  $self->assert_child_ok($pid);
14352
14353  if ($ex) {
14354    test_append_logfile($log_file, $ex);
14355    unlink($log_file);
14356
14357    die($ex);
14358  }
14359
14360  unlink($log_file);
14361}
14362
14363sub sql_user_info_null_uid_gid_columns {
14364  my $self = shift;
14365  my $tmpdir = $self->{tmpdir};
14366
14367  my $config_file = "$tmpdir/sqlite.conf";
14368  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
14369  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
14370
14371  my $log_file = test_get_logfile();
14372
14373  my $user = 'proftpd';
14374  my $passwd = 'test';
14375  my $group = 'ftpd';
14376  my $home_dir = File::Spec->rel2abs($tmpdir);
14377  my $uid = 500;
14378  my $gid = 500;
14379
14380  my $default_home = '/tmp';
14381  my $default_uid = 14;
14382  my $default_gid = 28;
14383
14384  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14385
14386  # Build up sqlite3 command to create users, groups tables and populate them
14387  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14388
14389  if (open(my $fh, "> $db_script")) {
14390    print $fh <<EOS;
14391CREATE TABLE users (
14392  userid TEXT,
14393  passwd TEXT
14394);
14395INSERT INTO users (userid, passwd) VALUES ('$user', '$passwd');
14396
14397CREATE TABLE groups (
14398  groupname TEXT,
14399  gid INTEGER,
14400  members TEXT
14401);
14402INSERT INTO groups (groupname, gid, members) VALUES ('$group', $default_gid, '$user');
14403
14404EOS
14405
14406    unless (close($fh)) {
14407      die("Can't write $db_script: $!");
14408    }
14409
14410  } else {
14411    die("Can't open $db_script: $!");
14412  }
14413
14414  my $cmd = "sqlite3 $db_file < $db_script";
14415  build_db($cmd, $db_script);
14416
14417  # Make sure that, if we're running as root, the database file has
14418  # the permissions/privs set for use by proftpd
14419  if ($< == 0) {
14420    unless (chmod(0666, $db_file)) {
14421      die("Can't set perms on $db_file to 0666: $!");
14422    }
14423  }
14424
14425  my $config = {
14426    PidFile => $pid_file,
14427    ScoreboardFile => $scoreboard_file,
14428    SystemLog => $log_file,
14429
14430    IfModules => {
14431      'mod_delay.c' => {
14432        DelayEngine => 'off',
14433      },
14434
14435      'mod_sql.c' => {
14436        SQLAuthenticate => 'users groups',
14437        SQLAuthTypes => 'plaintext',
14438        SQLBackend => 'sqlite3',
14439        SQLConnectInfo => $db_file,
14440        SQLLogFile => $log_file,
14441        SQLUserInfo => 'users userid passwd Null Null Null Null',
14442        SQLDefaultUID => $default_uid,
14443        SQLDefaultGID => $default_gid,
14444        SQLDefaultHomedir => $default_home,
14445      },
14446    },
14447  };
14448
14449  my ($port, $config_user, $config_group) = config_write($config_file, $config);
14450
14451  # Open pipes, for use between the parent and child processes.  Specifically,
14452  # the child will indicate when it's done with its test by writing a message
14453  # to the parent.
14454  my ($rfh, $wfh);
14455  unless (pipe($rfh, $wfh)) {
14456    die("Can't open pipe: $!");
14457  }
14458
14459  my $ex;
14460
14461  # Fork child
14462  $self->handle_sigchld();
14463  defined(my $pid = fork()) or die("Can't fork: $!");
14464  if ($pid) {
14465    eval {
14466      sleep(2);
14467
14468      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14469      $client->login($user, $passwd);
14470
14471      my $resp_msgs = $client->response_msgs();
14472      my $nmsgs = scalar(@$resp_msgs);
14473
14474      my $expected;
14475
14476      $expected = 1;
14477      $self->assert($expected == $nmsgs,
14478        test_msg("Expected $expected, got $nmsgs"));
14479
14480      $expected = "User proftpd logged in";
14481      $self->assert($expected eq $resp_msgs->[0],
14482        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
14483    };
14484
14485    if ($@) {
14486      $ex = $@;
14487    }
14488
14489    $wfh->print("done\n");
14490    $wfh->flush();
14491
14492  } else {
14493    eval { server_wait($config_file, $rfh) };
14494    if ($@) {
14495      warn($@);
14496      exit 1;
14497    }
14498
14499    exit 0;
14500  }
14501
14502  # Stop server
14503  server_stop($pid_file);
14504
14505  $self->assert_child_ok($pid);
14506
14507  if ($ex) {
14508    test_append_logfile($log_file, $ex);
14509    unlink($log_file);
14510
14511    die($ex);
14512  }
14513
14514  unlink($log_file);
14515}
14516
14517sub sql_64_bit_uid_bug4164 {
14518  my $self = shift;
14519  my $tmpdir = $self->{tmpdir};
14520
14521  my $config_file = "$tmpdir/sqlite.conf";
14522  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
14523  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
14524
14525  my $log_file = test_get_logfile();
14526
14527  my $user = 'proftpd';
14528  my $passwd = 'test';
14529  my $group = 'ftpd';
14530  my $home_dir = File::Spec->rel2abs($tmpdir);
14531
14532  # See: https://github.com/proftpd/proftpd/issues/74
14533  my $uid = 25440237859;
14534  my $gid = 500;
14535
14536  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14537
14538  # Build up sqlite3 command to create users, groups tables and populate them
14539  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14540
14541  if (open(my $fh, "> $db_script")) {
14542    print $fh <<EOS;
14543CREATE TABLE users (
14544  userid TEXT,
14545  passwd TEXT,
14546  uid INTEGER,
14547  gid INTEGER,
14548  homedir TEXT,
14549  shell TEXT
14550);
14551INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$user', '$passwd', $uid, $gid, '$home_dir', '/bin/bash');
14552
14553CREATE TABLE groups (
14554  groupname TEXT,
14555  gid INTEGER,
14556  members TEXT
14557);
14558INSERT INTO groups (groupname, gid, members) VALUES ('$group', $gid, '$user');
14559EOS
14560
14561    unless (close($fh)) {
14562      die("Can't write $db_script: $!");
14563    }
14564
14565  } else {
14566    die("Can't open $db_script: $!");
14567  }
14568
14569  my $cmd = "sqlite3 $db_file < $db_script";
14570  build_db($cmd, $db_script);
14571
14572  # Make sure that, if we're running as root, the database file has
14573  # the permissions/privs set for use by proftpd
14574  if ($< == 0) {
14575    unless (chmod(0666, $db_file)) {
14576      die("Can't set perms on $db_file to 0666: $!");
14577    }
14578  }
14579
14580  my $config = {
14581    PidFile => $pid_file,
14582    ScoreboardFile => $scoreboard_file,
14583    SystemLog => $log_file,
14584
14585    IfModules => {
14586      'mod_delay.c' => {
14587        DelayEngine => 'off',
14588      },
14589
14590      'mod_sql.c' => {
14591        SQLAuthTypes => 'plaintext',
14592        SQLBackend => 'sqlite3',
14593        SQLConnectInfo => $db_file,
14594        SQLLogFile => $log_file,
14595        SQLMinID => 100,
14596      },
14597    },
14598  };
14599
14600  my ($port, $config_user, $config_group) = config_write($config_file, $config);
14601
14602  # Open pipes, for use between the parent and child processes.  Specifically,
14603  # the child will indicate when it's done with its test by writing a message
14604  # to the parent.
14605  my ($rfh, $wfh);
14606  unless (pipe($rfh, $wfh)) {
14607    die("Can't open pipe: $!");
14608  }
14609
14610  my $ex;
14611
14612  # Fork child
14613  $self->handle_sigchld();
14614  defined(my $pid = fork()) or die("Can't fork: $!");
14615  if ($pid) {
14616    eval {
14617      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14618      $client->login($user, $passwd);
14619
14620      my $resp_msgs = $client->response_msgs();
14621      my $nmsgs = scalar(@$resp_msgs);
14622
14623      my $expected;
14624
14625      $expected = 1;
14626      $self->assert($expected == $nmsgs,
14627        test_msg("Expected $expected, got $nmsgs"));
14628
14629      $expected = "User proftpd logged in";
14630      $self->assert($expected eq $resp_msgs->[0],
14631        test_msg("Expected '$expected', got '$resp_msgs->[0]'"));
14632
14633      $client->quit();
14634    };
14635
14636    if ($@) {
14637      $ex = $@;
14638    }
14639
14640    $wfh->print("done\n");
14641    $wfh->flush();
14642
14643  } else {
14644    eval { server_wait($config_file, $rfh) };
14645    if ($@) {
14646      warn($@);
14647      exit 1;
14648    }
14649
14650    exit 0;
14651  }
14652
14653  # Stop server
14654  server_stop($pid_file);
14655
14656  $self->assert_child_ok($pid);
14657
14658  if ($ex) {
14659    test_append_logfile($log_file, $ex);
14660    unlink($log_file);
14661
14662    die($ex);
14663  }
14664
14665  unlink($log_file);
14666}
14667
14668sub get_port {
14669  my $db_file = shift;
14670  my $where = shift;
14671
14672  my $sql = "SELECT user, ip_addr, port FROM ftpsessions";
14673  if ($where) {
14674    $sql .= " WHERE $where";
14675  }
14676
14677  my $cmd = "sqlite3 $db_file \"$sql\"";
14678
14679  if ($ENV{TEST_VERBOSE}) {
14680    print STDERR "Executing sqlite3: $cmd\n";
14681  }
14682
14683  my $res = join('', `$cmd`);
14684  chomp($res);
14685
14686  # The default sqlite3 delimiter is '|'
14687  return split(/\|/, $res);
14688}
14689
14690sub sql_sqllog_var_remote_port {
14691  my $self = shift;
14692  my $tmpdir = $self->{tmpdir};
14693
14694  my $config_file = "$tmpdir/sqlite.conf";
14695  my $pid_file = File::Spec->rel2abs("$tmpdir/sqlite.pid");
14696  my $scoreboard_file = File::Spec->rel2abs("$tmpdir/sqlite.scoreboard");
14697
14698  my $log_file = test_get_logfile();
14699
14700  my $auth_user_file = File::Spec->rel2abs("$tmpdir/sqlite.passwd");
14701  my $auth_group_file = File::Spec->rel2abs("$tmpdir/sqlite.group");
14702
14703  my $user = 'proftpd';
14704  my $passwd = 'test';
14705  my $group = 'ftpd';
14706  my $home_dir = File::Spec->rel2abs($tmpdir);
14707  my $uid = 500;
14708  my $gid = 500;
14709
14710  # Make sure that, if we're running as root, that the home directory has
14711  # permissions/privs set for the account we create
14712  if ($< == 0) {
14713    unless (chmod(0755, $home_dir)) {
14714      die("Can't set perms on $home_dir to 0755: $!");
14715    }
14716
14717    unless (chown($uid, $gid, $home_dir)) {
14718      die("Can't set owner of $home_dir to $uid/$gid: $!");
14719    }
14720  }
14721
14722  auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
14723    '/bin/bash');
14724  auth_group_write($auth_group_file, $group, $gid, $user);
14725
14726  my $test_file = File::Spec->rel2abs($config_file);
14727
14728  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14729
14730  # Build up sqlite3 command to create users, groups tables and populate them
14731  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14732
14733  if (open(my $fh, "> $db_script")) {
14734    print $fh <<EOS;
14735CREATE TABLE ftpsessions (
14736  user TEXT,
14737  ip_addr TEXT,
14738  port NUMBER
14739);
14740EOS
14741
14742    unless (close($fh)) {
14743      die("Can't write $db_script: $!");
14744    }
14745
14746  } else {
14747    die("Can't open $db_script: $!");
14748  }
14749
14750  my $cmd = "sqlite3 $db_file < $db_script";
14751  build_db($cmd, $db_script);
14752
14753  # Make sure that, if we're running as root, the database file has
14754  # the permissions/privs set for use by proftpd
14755  if ($< == 0) {
14756    unless (chmod(0666, $db_file)) {
14757      die("Can't set perms on $db_file to 0666: $!");
14758    }
14759  }
14760
14761  my $config = {
14762    PidFile => $pid_file,
14763    ScoreboardFile => $scoreboard_file,
14764    SystemLog => $log_file,
14765
14766    AuthUserFile => $auth_user_file,
14767    AuthGroupFile => $auth_group_file,
14768
14769    IfModules => {
14770      'mod_delay.c' => {
14771        DelayEngine => 'off',
14772      },
14773
14774      'mod_sql.c' => {
14775        SQLEngine => 'log',
14776        SQLBackend => 'sqlite3',
14777        SQLConnectInfo => $db_file,
14778        SQLLogFile => $log_file,
14779        SQLNamedQuery => 'filename FREEFORM "INSERT INTO ftpsessions (user, ip_addr, port) VALUES (\'%u\', \'%L\', %{remote-port})"',
14780        SQLLog => 'RETR filename',
14781      },
14782    },
14783  };
14784
14785  my ($port, $config_user, $config_group) = config_write($config_file, $config);
14786
14787  # Open pipes, for use between the parent and child processes.  Specifically,
14788  # the child will indicate when it's done with its test by writing a message
14789  # to the parent.
14790  my ($rfh, $wfh);
14791  unless (pipe($rfh, $wfh)) {
14792    die("Can't open pipe: $!");
14793  }
14794
14795  my $ex;
14796
14797  # Fork child
14798  $self->handle_sigchld();
14799  defined(my $pid = fork()) or die("Can't fork: $!");
14800  if ($pid) {
14801    eval {
14802      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14803      $client->login($user, $passwd);
14804
14805      my $conn = $client->retr_raw($test_file);
14806      unless ($conn) {
14807        die("Failed to RETR: " . $client->response_code() . " " .
14808          $client->response_msg());
14809      }
14810
14811      my $buf;
14812      $conn->read($buf, 8192, 30);
14813      eval { $conn->close() };
14814
14815      my $resp_code = $client->response_code();
14816      my $resp_msg = $client->response_msg();
14817      $self->assert_transfer_ok($resp_code, $resp_msg);
14818
14819      $client->quit();
14820    };
14821
14822    if ($@) {
14823      $ex = $@;
14824    }
14825
14826    $wfh->print("done\n");
14827    $wfh->flush();
14828
14829  } else {
14830    eval { server_wait($config_file, $rfh) };
14831    if ($@) {
14832      warn($@);
14833      exit 1;
14834    }
14835
14836    exit 0;
14837  }
14838
14839  # Stop server
14840  server_stop($pid_file);
14841  $self->assert_child_ok($pid);
14842
14843  if ($ex) {
14844    test_append_logfile($log_file, $ex);
14845    unlink($log_file);
14846
14847    die($ex);
14848  }
14849
14850  my ($login, $ip_addr, $port) = get_port($db_file, "user = \'$user\'");
14851
14852  my $expected = $user;
14853  $self->assert($expected eq $login,
14854    test_msg("Expected '$expected', got '$login'"));
14855
14856  $expected = '127.0.0.1';
14857  $self->assert($expected eq $ip_addr,
14858    test_msg("Expected '$expected', got '$ip_addr'"));
14859
14860  $expected = '^\d+$';
14861  $self->assert(qr/$expected/, $port,
14862    test_msg("Expected '$expected', got '$port'"));
14863
14864  unlink($log_file);
14865}
14866
14867sub get_ftps {
14868  my $db_file = shift;
14869  my $where = shift;
14870
14871  my $sql = "SELECT user, ip_addr, ftps FROM ftpsessions";
14872  if ($where) {
14873    $sql .= " WHERE $where";
14874  }
14875
14876  my $cmd = "sqlite3 $db_file \"$sql\"";
14877
14878  if ($ENV{TEST_VERBOSE}) {
14879    print STDERR "Executing sqlite3: $cmd\n";
14880  }
14881
14882  my $res = join('', `$cmd`);
14883  chomp($res);
14884
14885  # The default sqlite3 delimiter is '|'
14886  return split(/\|/, $res);
14887}
14888
14889sub sql_sqllog_var_note_ftps_without_tls_issue1232 {
14890  my $self = shift;
14891  my $tmpdir = $self->{tmpdir};
14892  my $setup = test_setup($tmpdir, 'sqlite');
14893
14894  my $test_file = File::Spec->rel2abs($setup->{config_file});
14895  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
14896
14897  # Build up sqlite3 command to create users, groups tables and populate them
14898  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
14899
14900  if (open(my $fh, "> $db_script")) {
14901    print $fh <<EOS;
14902CREATE TABLE ftpsessions (
14903  user TEXT,
14904  ip_addr TEXT,
14905  ftps TEXT
14906);
14907EOS
14908
14909    unless (close($fh)) {
14910      die("Can't write $db_script: $!");
14911    }
14912
14913  } else {
14914    die("Can't open $db_script: $!");
14915  }
14916
14917  my $cmd = "sqlite3 $db_file < $db_script";
14918  build_db($cmd, $db_script);
14919
14920  # Make sure that, if we're running as root, the database file has
14921  # the permissions/privs set for use by proftpd
14922  if ($< == 0) {
14923    unless (chmod(0666, $db_file)) {
14924      die("Can't set perms on $db_file to 0666: $!");
14925    }
14926  }
14927
14928  my $config = {
14929    PidFile => $setup->{pid_file},
14930    ScoreboardFile => $setup->{scoreboard_file},
14931    SystemLog => $setup->{log_file},
14932    TraceLog => $setup->{log_file},
14933
14934    AuthUserFile => $setup->{auth_user_file},
14935    AuthGroupFile => $setup->{auth_group_file},
14936
14937    IfModules => {
14938      'mod_delay.c' => {
14939        DelayEngine => 'off',
14940      },
14941
14942      'mod_sql.c' => {
14943        SQLEngine => 'log',
14944        SQLBackend => 'sqlite3',
14945        SQLConnectInfo => $db_file,
14946        SQLLogFile => $setup->{log_file},
14947        SQLNamedQuery => 'on_login FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ftps) VALUES (\'%u\', \'%L\', \'%{note:FTPS}\')"',
14948        SQLLog => 'PASS on_login',
14949      },
14950    },
14951  };
14952
14953  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
14954    $config);
14955
14956  # Open pipes, for use between the parent and child processes.  Specifically,
14957  # the child will indicate when it's done with its test by writing a message
14958  # to the parent.
14959  my ($rfh, $wfh);
14960  unless (pipe($rfh, $wfh)) {
14961    die("Can't open pipe: $!");
14962  }
14963
14964  my $ex;
14965
14966  # Fork child
14967  $self->handle_sigchld();
14968  defined(my $pid = fork()) or die("Can't fork: $!");
14969  if ($pid) {
14970    eval {
14971      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
14972      $client->login($setup->{user}, $setup->{passwd});
14973
14974      my $conn = $client->retr_raw($test_file);
14975      unless ($conn) {
14976        die("Failed to RETR: " . $client->response_code() . " " .
14977          $client->response_msg());
14978      }
14979
14980      my $buf;
14981      $conn->read($buf, 8192, 30);
14982      eval { $conn->close() };
14983
14984      my $resp_code = $client->response_code();
14985      my $resp_msg = $client->response_msg();
14986      $self->assert_transfer_ok($resp_code, $resp_msg);
14987
14988      $client->quit();
14989    };
14990    if ($@) {
14991      $ex = $@;
14992    }
14993
14994    $wfh->print("done\n");
14995    $wfh->flush();
14996
14997  } else {
14998    eval { server_wait($setup->{config_file}, $rfh) };
14999    if ($@) {
15000      warn($@);
15001      exit 1;
15002    }
15003
15004    exit 0;
15005  }
15006
15007  # Stop server
15008  server_stop($setup->{pid_file});
15009  $self->assert_child_ok($pid);
15010
15011  test_cleanup($setup->{log_file}, $ex) if $ex;
15012
15013  eval {
15014    my ($login, $ip_addr, $ftps);
15015    ($login, $ip_addr, $ftps) = get_ftps($db_file, "user = \'$setup->{user}\'");
15016
15017    my $expected = $setup->{user};
15018    $self->assert($expected eq $login,
15019      test_msg("Expected '$expected', got '$login'"));
15020
15021    $expected = '127.0.0.1';
15022    $self->assert($expected eq $ip_addr,
15023      test_msg("Expected '$expected', got '$ip_addr'"));
15024
15025    $expected = '';
15026    $self->assert(qr/$expected/, $ftps,
15027      test_msg("Expected '$expected', got '$ftps'"));
15028  };
15029  if ($@) {
15030    $ex = $@;
15031  }
15032
15033  test_cleanup($setup->{log_file}, $ex);
15034}
15035
15036sub sql_sqllog_var_note_ftps_with_tls_issue1232 {
15037  my $self = shift;
15038  my $tmpdir = $self->{tmpdir};
15039  my $setup = test_setup($tmpdir, 'sqlite');
15040
15041  my $test_file = File::Spec->rel2abs($setup->{config_file});
15042  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
15043
15044  # Build up sqlite3 command to create users, groups tables and populate them
15045  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
15046
15047  if (open(my $fh, "> $db_script")) {
15048    print $fh <<EOS;
15049CREATE TABLE ftpsessions (
15050  user TEXT,
15051  ip_addr TEXT,
15052  ftps TEXT
15053);
15054EOS
15055
15056    unless (close($fh)) {
15057      die("Can't write $db_script: $!");
15058    }
15059
15060  } else {
15061    die("Can't open $db_script: $!");
15062  }
15063
15064  my $cmd = "sqlite3 $db_file < $db_script";
15065  build_db($cmd, $db_script);
15066
15067  # Make sure that, if we're running as root, the database file has
15068  # the permissions/privs set for use by proftpd
15069  if ($< == 0) {
15070    unless (chmod(0666, $db_file)) {
15071      die("Can't set perms on $db_file to 0666: $!");
15072    }
15073  }
15074
15075  my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
15076  my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
15077
15078  my $config = {
15079    PidFile => $setup->{pid_file},
15080    ScoreboardFile => $setup->{scoreboard_file},
15081    SystemLog => $setup->{log_file},
15082    TraceLog => $setup->{log_file},
15083    Trace => 'jot:30 sql:20 tls:20',
15084
15085    AuthUserFile => $setup->{auth_user_file},
15086    AuthGroupFile => $setup->{auth_group_file},
15087
15088    IfModules => {
15089      'mod_delay.c' => {
15090        DelayEngine => 'off',
15091      },
15092
15093      'mod_sql.c' => {
15094        SQLEngine => 'log',
15095        SQLBackend => 'sqlite3',
15096        SQLConnectInfo => $db_file,
15097        SQLLogFile => $setup->{log_file},
15098        SQLNamedQuery => 'on_login FREEFORM "INSERT INTO ftpsessions (user, ip_addr, ftps) VALUES (\'%u\', \'%L\', \'%{note:FTPS}\')"',
15099        SQLLog => 'PASS on_login',
15100      },
15101
15102      'mod_tls.c' => {
15103        TLSEngine => 'on',
15104        TLSLog => $setup->{log_file},
15105        TLSRequired => 'on',
15106        TLSRSACertificateFile => $cert_file,
15107        TLSCACertificateFile => $ca_file,
15108      },
15109    },
15110  };
15111
15112  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
15113    $config);
15114
15115  # Open pipes, for use between the parent and child processes.  Specifically,
15116  # the child will indicate when it's done with its test by writing a message
15117  # to the parent.
15118  my ($rfh, $wfh);
15119  unless (pipe($rfh, $wfh)) {
15120    die("Can't open pipe: $!");
15121  }
15122
15123  require Net::FTPSSL;
15124
15125  my $ex;
15126
15127  # Fork child
15128  $self->handle_sigchld();
15129  defined(my $pid = fork()) or die("Can't fork: $!");
15130  if ($pid) {
15131    eval {
15132      # Give the server a chance to start up
15133      sleep(2);
15134
15135      my $client = Net::FTPSSL->new('127.0.0.1',
15136        Encryption => 'E',
15137        Port => $port,
15138      );
15139
15140      unless ($client) {
15141        die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr());
15142      }
15143
15144      unless ($client->login($setup->{user}, $setup->{passwd})) {
15145        die("Login failed unexpectedly: " + $client->message());
15146      }
15147
15148      $client->quit();
15149    };
15150    if ($@) {
15151      $ex = $@;
15152    }
15153
15154    $wfh->print("done\n");
15155    $wfh->flush();
15156
15157  } else {
15158    eval { server_wait($setup->{config_file}, $rfh) };
15159    if ($@) {
15160      warn($@);
15161      exit 1;
15162    }
15163
15164    exit 0;
15165  }
15166
15167  # Stop server
15168  server_stop($setup->{pid_file});
15169  $self->assert_child_ok($pid);
15170
15171  test_cleanup($setup->{log_file}, $ex) if $ex;
15172
15173  eval {
15174    my ($login, $ip_addr, $ftps);
15175    ($login, $ip_addr, $ftps) = get_ftps($db_file, "user = \'$setup->{user}\'");
15176
15177    my $expected = $setup->{user};
15178    $self->assert($expected eq $login,
15179      test_msg("Expected '$expected', got '$login'"));
15180
15181    $expected = '127.0.0.1';
15182    $self->assert($expected eq $ip_addr,
15183      test_msg("Expected '$expected', got '$ip_addr'"));
15184
15185    $expected = '1';
15186    $self->assert(qr/$expected/, $ftps,
15187      test_msg("Expected '$expected', got '$ftps'"));
15188  };
15189  if ($@) {
15190    $ex = $@;
15191  }
15192
15193  test_cleanup($setup->{log_file}, $ex);
15194}
15195
15196sub sql_userowner_issue346 {
15197  my $self = shift;
15198  my $tmpdir = $self->{tmpdir};
15199  my $setup = test_setup($tmpdir, 'sqlite');
15200
15201  my $uid = 0;
15202  my $gid = 0;
15203
15204  my $test_file = File::Spec->rel2abs("$tmpdir/test.dat");
15205  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
15206
15207  # Build up sqlite3 command to create users, groups tables and populate them
15208  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
15209
15210  if (open(my $fh, "> $db_script")) {
15211    print $fh <<EOS;
15212CREATE TABLE users (
15213  userid TEXT,
15214  passwd TEXT,
15215  uid INTEGER,
15216  gid INTEGER,
15217  homedir TEXT,
15218  shell TEXT,
15219  lastdir TEXT
15220);
15221INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $uid, $gid, '$setup->{home_dir}', '/bin/bash');
15222
15223CREATE TABLE groups (
15224  groupname TEXT,
15225  gid INTEGER,
15226  members TEXT
15227);
15228INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $gid, '$setup->{user}');
15229EOS
15230
15231    unless (close($fh)) {
15232      die("Can't write $db_script: $!");
15233    }
15234
15235  } else {
15236    die("Can't open $db_script: $!");
15237  }
15238
15239  my $cmd = "sqlite3 $db_file < $db_script";
15240  build_db($cmd, $db_script);
15241
15242  # Make sure that, if we're running as root, the database file has
15243  # the permissions/privs set for use by proftpd
15244  if ($< == 0) {
15245    unless (chmod(0666, $db_file)) {
15246      die("Can't set perms on $db_file to 0666: $!");
15247    }
15248  }
15249
15250  my $config = {
15251    PidFile => $setup->{pid_file},
15252    ScoreboardFile => $setup->{scoreboard_file},
15253    SystemLog => $setup->{log_file},
15254
15255    RootLogin => 'on',
15256    RootRevoke => 'off',
15257
15258    Directory => {
15259      '/' => {
15260        UserOwner => 'www',
15261        GroupOwner => 'www',
15262      },
15263    },
15264
15265    IfModules => {
15266      'mod_delay.c' => {
15267        DelayEngine => 'off',
15268      },
15269
15270      'mod_sql.c' => {
15271        SQLAuthTypes => 'plaintext',
15272        SQLBackend => 'sqlite3',
15273        SQLConnectInfo => $db_file,
15274        SQLLogFile => $setup->{log_file},
15275        SQLMinUserUID => 0,
15276        SQLMinUserGID => 0,
15277        SQLMinID => 0,
15278      },
15279    },
15280  };
15281
15282  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
15283    $config);
15284
15285  # Open pipes, for use between the parent and child processes.  Specifically,
15286  # the child will indicate when it's done with its test by writing a message
15287  # to the parent.
15288  my ($rfh, $wfh);
15289  unless (pipe($rfh, $wfh)) {
15290    die("Can't open pipe: $!");
15291  }
15292
15293  my $ex;
15294
15295  # Fork child
15296  $self->handle_sigchld();
15297  defined(my $pid = fork()) or die("Can't fork: $!");
15298  if ($pid) {
15299    eval {
15300      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0);
15301      $client->login($setup->{user}, $setup->{passwd});
15302
15303      my $conn = $client->stor_raw('test.dat');
15304      unless ($conn) {
15305        die("STOR test.dat failed: " . $client->response_code() . " " .
15306          $client->response_msg());
15307      }
15308
15309      my $text = "Hello, World!\n";
15310      $conn->write($text, length($text), 10);
15311      eval { $conn->close() };
15312
15313      my $resp_code = $client->response_code();
15314      my $resp_msg = $client->response_msg();
15315      $self->assert_transfer_ok($resp_code, $resp_msg);
15316      $client->quit();
15317
15318      $self->assert(-f $test_file,
15319        "File $test_file does not exist as expected");
15320
15321      my $file_uid = (stat($test_file))[4];
15322      my $file_gid = (stat($test_file))[5];
15323
15324      $self->assert($file_uid != 0, "Expected UID non-0, got $file_uid");
15325      $self->assert($file_gid != 0, "Expected GID non-0, got $file_gid");
15326    };
15327
15328    if ($@) {
15329      $ex = $@;
15330    }
15331
15332    $wfh->print("done\n");
15333    $wfh->flush();
15334
15335  } else {
15336    eval { server_wait($setup->{config_file}, $rfh) };
15337    if ($@) {
15338      warn($@);
15339      exit 1;
15340    }
15341
15342    exit 0;
15343  }
15344
15345  # Stop server
15346  server_stop($setup->{pid_file});
15347  $self->assert_child_ok($pid);
15348
15349  test_cleanup($setup->{log_file}, $ex);
15350}
15351
15352sub sql_sqlite_log_db_enoent_issue654 {
15353  my $self = shift;
15354  my $tmpdir = $self->{tmpdir};
15355  my $setup = test_setup($tmpdir, 'sqlite');
15356
15357  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
15358
15359  my $config = {
15360    PidFile => $setup->{pid_file},
15361    ScoreboardFile => $setup->{scoreboard_file},
15362    SystemLog => $setup->{log_file},
15363    TraceLog => $setup->{log_file},
15364    Trace => 'sql:20',
15365
15366    AuthUserFile => $setup->{auth_user_file},
15367    AuthGroupFile => $setup->{auth_group_file},
15368
15369    IfModules => {
15370      'mod_delay.c' => {
15371        DelayEngine => 'off',
15372      },
15373
15374      'mod_sql.c' => {
15375        SQLEngine => 'log',
15376        SQLBackend => 'sqlite3',
15377        SQLConnectInfo => $db_file,
15378        SQLLogFile => $setup->{log_file},
15379        SQLNamedQuery => 'session_start FREEFORM "INSERT INTO ftpsessions (user, ip_addr, timestamp) VALUES (\'%u\', \'%L\', \'%{time:%Y-%m-%d %H:%M:%S}\')"',
15380        SQLLog => 'PASS session_start',
15381      },
15382    },
15383  };
15384
15385  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
15386    $config);
15387
15388  # Open pipes, for use between the parent and child processes.  Specifically,
15389  # the child will indicate when it's done with its test by writing a message
15390  # to the parent.
15391  my ($rfh, $wfh);
15392  unless (pipe($rfh, $wfh)) {
15393    die("Can't open pipe: $!");
15394  }
15395
15396  my $ex;
15397
15398  # Fork child
15399  $self->handle_sigchld();
15400  defined(my $pid = fork()) or die("Can't fork: $!");
15401  if ($pid) {
15402    eval {
15403      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
15404      eval { $client->login($setup->{user}, $setup->{passwd}) };
15405      unless ($@) {
15406        $client->quit();
15407        die("Login succeeded unexpectedly");
15408      }
15409    };
15410    if ($@) {
15411      $ex = $@;
15412    }
15413
15414    $wfh->print("done\n");
15415    $wfh->flush();
15416
15417  } else {
15418    eval { server_wait($setup->{config_file}, $rfh) };
15419    if ($@) {
15420      warn($@);
15421      exit 1;
15422    }
15423
15424    exit 0;
15425  }
15426
15427  # Stop server
15428  server_stop($setup->{pid_file});
15429  $self->assert_child_ok($pid);
15430
15431  test_cleanup($setup->{log_file}, $ex);
15432}
15433
15434sub sql_namebased_vhost_using_host_issue882 {
15435  my $self = shift;
15436  my $tmpdir = $self->{tmpdir};
15437  my $setup = test_setup($tmpdir, 'sqlite');
15438
15439  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
15440
15441  # Build up sqlite3 command to create users, groups tables and populate them
15442  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
15443
15444  if (open(my $fh, "> $db_script")) {
15445    print $fh <<EOS;
15446CREATE TABLE users (
15447  userid TEXT,
15448  passwd TEXT,
15449  uid INTEGER,
15450  gid INTEGER,
15451  homedir TEXT,
15452  shell TEXT,
15453  lastdir TEXT
15454);
15455INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash');
15456CREATE TABLE groups (
15457  groupname TEXT,
15458  gid INTEGER,
15459  members TEXT
15460);
15461INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
15462EOS
15463
15464    unless (close($fh)) {
15465      die("Can't write $db_script: $!");
15466    }
15467
15468  } else {
15469    die("Can't open $db_script: $!");
15470  }
15471
15472  my $cmd = "sqlite3 $db_file < $db_script";
15473  build_db($cmd, $db_script);
15474
15475  # Make sure that, if we're running as root, the database file has
15476  # the permissions/privs set for use by proftpd
15477  if ($< == 0) {
15478    unless (chmod(0666, $db_file)) {
15479      die("Can't set perms on $db_file to 0666: $!");
15480    }
15481  }
15482
15483  my $config = {
15484    PidFile => $setup->{pid_file},
15485    ScoreboardFile => $setup->{scoreboard_file},
15486    SystemLog => $setup->{log_file},
15487    TraceLog => $setup->{log_file},
15488    Trace => 'sql:20',
15489
15490    AuthUserFile => $setup->{auth_user_file},
15491    AuthGroupFile => $setup->{auth_group_file},
15492
15493    Global => {
15494      AuthOrder => 'mod_sql.c',
15495      SQLAuthTypes => 'plaintext',
15496      SQLEngine => 'on',
15497      SQLBackend => 'sqlite3',
15498      SQLConnectInfo => $db_file,
15499      SQLLogFile => $setup->{log_file},
15500    },
15501
15502    IfModules => {
15503      'mod_delay.c' => {
15504        DelayEngine => 'off',
15505      },
15506    },
15507  };
15508
15509  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
15510    $config);
15511
15512  my $server_alias = 'ftp.example.com';
15513
15514  if (open(my $fh, ">> $setup->{config_file}")) {
15515    print $fh <<EOC;
15516<VirtualHost 127.0.0.1>
15517  Port $port
15518  ServerAlias $server_alias
15519  WtmpLog off
15520  TransferLog none
15521</VirtualHost>
15522EOC
15523  } else {
15524    die("Can't open $setup->{config_file}: $!");
15525  }
15526
15527  # Open pipes, for use between the parent and child processes.  Specifically,
15528  # the child will indicate when it's done with its test by writing a message
15529  # to the parent.
15530  my ($rfh, $wfh);
15531  unless (pipe($rfh, $wfh)) {
15532    die("Can't open pipe: $!");
15533  }
15534
15535  my $ex;
15536
15537  # Fork child
15538  $self->handle_sigchld();
15539  defined(my $pid = fork()) or die("Can't fork: $!");
15540  if ($pid) {
15541    eval {
15542      my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
15543      $client->host($server_alias);
15544      $client->login($setup->{user}, $setup->{passwd});
15545      $client->quit();
15546    };
15547    if ($@) {
15548      $ex = $@;
15549    }
15550
15551    $wfh->print("done\n");
15552    $wfh->flush();
15553
15554  } else {
15555    eval { server_wait($setup->{config_file}, $rfh) };
15556    if ($@) {
15557      warn($@);
15558      exit 1;
15559    }
15560
15561    exit 0;
15562  }
15563
15564  # Stop server
15565  server_stop($setup->{pid_file});
15566  $self->assert_child_ok($pid);
15567
15568  test_cleanup($setup->{log_file}, $ex);
15569}
15570
15571sub sql_namebased_vhost_using_sni_issue882 {
15572  my $self = shift;
15573  my $tmpdir = $self->{tmpdir};
15574  my $setup = test_setup($tmpdir, 'sqlite');
15575
15576  my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
15577
15578  # Build up sqlite3 command to create users, groups tables and populate them
15579  my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
15580
15581  if (open(my $fh, "> $db_script")) {
15582    print $fh <<EOS;
15583CREATE TABLE users (
15584  userid TEXT,
15585  passwd TEXT,
15586  uid INTEGER,
15587  gid INTEGER,
15588  homedir TEXT,
15589  shell TEXT,
15590  lastdir TEXT
15591);
15592INSERT INTO users (userid, passwd, uid, gid, homedir, shell) VALUES ('$setup->{user}', '$setup->{passwd}', $setup->{uid}, $setup->{gid}, '$setup->{home_dir}', '/bin/bash');
15593CREATE TABLE groups (
15594  groupname TEXT,
15595  gid INTEGER,
15596  members TEXT
15597);
15598INSERT INTO groups (groupname, gid, members) VALUES ('$setup->{group}', $setup->{gid}, '$setup->{user}');
15599EOS
15600
15601    unless (close($fh)) {
15602      die("Can't write $db_script: $!");
15603    }
15604
15605  } else {
15606    die("Can't open $db_script: $!");
15607  }
15608
15609  my $cmd = "sqlite3 $db_file < $db_script";
15610  build_db($cmd, $db_script);
15611
15612  # Make sure that, if we're running as root, the database file has
15613  # the permissions/privs set for use by proftpd
15614  if ($< == 0) {
15615    unless (chmod(0666, $db_file)) {
15616      die("Can't set perms on $db_file to 0666: $!");
15617    }
15618  }
15619
15620  my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
15621  my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
15622
15623  my $config = {
15624    PidFile => $setup->{pid_file},
15625    ScoreboardFile => $setup->{scoreboard_file},
15626    SystemLog => $setup->{log_file},
15627    TraceLog => $setup->{log_file},
15628    Trace => 'command:20 event:20 sql:20 tls:20',
15629
15630    AuthUserFile => $setup->{auth_user_file},
15631    AuthGroupFile => $setup->{auth_group_file},
15632
15633    Global => {
15634      AuthOrder => 'mod_sql.c',
15635      SQLAuthTypes => 'plaintext',
15636      SQLEngine => 'on',
15637      SQLBackend => 'sqlite3',
15638      SQLConnectInfo => $db_file,
15639      SQLLogFile => $setup->{log_file},
15640    },
15641
15642    IfModules => {
15643      'mod_delay.c' => {
15644        DelayEngine => 'off',
15645      },
15646
15647      'mod_tls.c' => {
15648        TLSEngine => 'on',
15649        TLSLog => $setup->{log_file},
15650        TLSRequired => 'on',
15651        TLSRSACertificateFile => $cert_file,
15652        TLSCACertificateFile => $ca_file,
15653      },
15654    },
15655  };
15656
15657  my ($port, $config_user, $config_group) = config_write($setup->{config_file},
15658    $config);
15659
15660  my $server_alias = 'ftp.example.com';
15661
15662  if (open(my $fh, ">> $setup->{config_file}")) {
15663    print $fh <<EOC;
15664<VirtualHost 127.0.0.1>
15665  Port $port
15666  ServerAlias $server_alias
15667  WtmpLog off
15668  TransferLog none
15669</VirtualHost>
15670EOC
15671  } else {
15672    die("Can't open $setup->{config_file}: $!");
15673  }
15674
15675  # Open pipes, for use between the parent and child processes.  Specifically,
15676  # the child will indicate when it's done with its test by writing a message
15677  # to the parent.
15678  my ($rfh, $wfh);
15679  unless (pipe($rfh, $wfh)) {
15680    die("Can't open pipe: $!");
15681  }
15682
15683  require Net::FTPSSL;
15684
15685  my $ex;
15686
15687  # Fork child
15688  $self->handle_sigchld();
15689  defined(my $pid = fork()) or die("Can't fork: $!");
15690  if ($pid) {
15691    eval {
15692      # Give the server a chance to start up
15693      sleep(2);
15694
15695      my $client = Net::FTPSSL->new('127.0.0.1',
15696        Encryption => 'E',
15697        Port => $port,
15698        SSL_hostname => $server_alias,
15699      );
15700
15701      unless ($client) {
15702        die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr());
15703      }
15704
15705      unless ($client->login($setup->{user}, $setup->{passwd})) {
15706        die("Login failed unexpectedly: " + $client->message());
15707      }
15708
15709      $client->quit();
15710    };
15711    if ($@) {
15712      $ex = $@;
15713    }
15714
15715    $wfh->print("done\n");
15716    $wfh->flush();
15717
15718  } else {
15719    eval { server_wait($setup->{config_file}, $rfh) };
15720    if ($@) {
15721      warn($@);
15722      exit 1;
15723    }
15724
15725    exit 0;
15726  }
15727
15728  # Stop server
15729  server_stop($setup->{pid_file});
15730  $self->assert_child_ok($pid);
15731
15732  test_cleanup($setup->{log_file}, $ex);
15733}
15734
157351;
15736