#!/usr/bin/perl # ###################################################################### # LedgerSMB Small Medium Business Accounting Software Installer # http://www.ledgersmb.org/ # # Copyright (C) 2006 # This work contains copyrighted information from a number of sources all used # with permission. # # This file contains source code included with or based on SQL-Ledger which # is Copyright Dieter Simader and DWS Systems Inc. 2000-2005 and licensed # under the GNU General Public License version 2 or, at your option, any later # version. For a full list including contact information of contributors, # maintainers, and copyright holders, see the CONTRIBUTORS file. # # Original Copyright Notice from SQL-Ledger 2.6.17 (before the fork): # Copyright (c) 2002, Dieter Simader # # Web: http://www.sql-ledger.org # ####################################################################### # Next bunch of lines are to check to see if they have the cpan module installed. my $cpan=0; eval { use CPAN; }; if (!$@){ $cpan = 1; } $| = 1; #not sure how safe this is. If the browser sends a blank HTTP_USER_AGENT #will this script destroy part of the install? #This script should probably be made inaccessible via HTTP until this feature is working if (($ENV{HTTP_USER_AGENT})||($ENV{HTTP_HOST})) { print "Content-type: text/html\n\nThis does not work yet! use $0 from the command line"; exit; } # Make sure they have the required perl modules installed. # bin/mozilla/admin.pl needs Digest::MD5 for session handling # HTML:LinkExtor is used by the setup program. my @req_modules=(qw(DBI DBD::Pg Digest::MD5 HTML::LinkExtor)); foreach my $module(@req_modules){ print "Checking for: $module ...\t"; my @results=&check_module($module); print "$results[1]\n"; next if($results[0]); # Passed, no need to continue.. if ($cpan == 1){ # Can try to install the module.. print "\n\nWould you like to try and install this package ($module) through CPAN? (Y/N) [Y]:"; my $response=; if(($response=~/y/i) or ($response eq "\n")){ my $inst_obj = CPAN::Shell->install($module); @results=&check_module($module); if(!$results[0]){ print "\n\nCould not install $module using CPAN.\n"; die "Please try to install this module manually\n"; } } else { die "Please install the $module perl module and retry the setup.\n"; } } else { # Can't try to install the module.. die "Please install the $module perl module and retry the setup.\n"; } } use HTML::LinkExtor; my $lynx = `lynx -version`; # if LWP is not installed use lynx my $gzip = `gzip -V 2>&1`; # gz decompression utility my $tar = `tar --version 2>&1`; # tar archiver my $latex = `latex -version`; my $versionurl ='http://prdownloads.sourceforge.net/ledger-smb'; my %source = ( 1 => { url => "http://voxel.dl.sourceforge.net/sourceforge/ledger-smb", site => "New York, U.S.A", locale => 'us' }, 2 => { url => "http://easynews.dl.sourceforge.net/sourceforge/ledger-smb", site => "Arizona, U.S.A", locale => 'us' }, 3 => { url => "http://ufpr.dl.sourceforge.net/sourceforge/ledger-smb", site =>"Brazil", locale => 'br' }, 4 => { url => "http://surfnet.dl.sourceforge.net/sourceforge/ledger-smb", site => "The Netherlands", locale => 'nl' }, 5 => { url => "http://http://kent.dl.sourceforge.net/sourceforge/ledger-smb", site => "U.K", locale => 'uk' }, 6 => { url => "http://ovh.dl.sourceforge.net/sourceforge/ledger-smb", site => "France", locale => 'fr' }, 7 => { url => "http://mesh.dl.sourceforge.net/sourceforge/ledger-smb", site => "Germany", locale => 'de' }, 8 => { url => "http://citkit.dl.sourceforge.net/sourceforge/ledger-smb", site => "Russia", locale => 'ru' }, 9 => { url => "http://optusnet.dl.sourceforge.net/sourceforge/ledger-smb", site => "Sydney, Australia", locale => 'au' }, 10 => { url => "http://nchc.dl.sourceforge.net/sourceforge/ledger-smb", site => "Taiwan", locale => 'tw' }, 11 => { url => "http://jaist.dl.sourceforge.net/sourceforge/ledger-smb", site => "Japan", locale => 'jp' }, 12 => { url => "http://heanet.dl.sourceforge.net/sourceforge/ledger-smb", site => "Ireland", locale => 'ie' } ); my $userspath = "users"; # default for new installation eval { require "ledger-smb.conf"; }; my $filename = shift; chomp $filename; my $newinstall = 1; # is LWP installed eval { require LWP::Simple; }; $lwp = !($@); unless ($lwp || $lynx || $filename) { die "You must have either lynx or LWP installed or specify a filename. perl $0 \n"; } if ($filename) { # extract version die "Not a Ledger-SMB archive\n" if ($filename !~ /^ledger-smb/); $version = $filename; $version =~ s/ledger-smb-(\d+\.\d+\.\d+).*$/$1/; } if (-f "VERSION") { # get installed version from VERSION file open(FH, "VERSION"); @a = ; close(FH); $version = $a[0]; chomp $version; $newinstall = !$version; if (! -f "ledger-smb.conf") { $newinstall = 1; } } # Try to determine web user and group.. $webowner = "nobody"; $webgroup = "nogroup"; # Check for apache2.conf if ($httpd = `find /etc /usr/local/etc -type f -name 'apache2.conf'`) { chomp $httpd; $webowner = `grep "^User " $httpd`; $webgroup = `grep "^Group " $httpd`; chomp $webowner; chomp $webgroup; (undef, $webowner) = split / /, $webowner; (undef, $webgroup) = split / /, $webgroup; } elsif ($httpd = `find /etc /usr/local/etc -type f -name 'httpd.conf'`) { # Else check for httpd.conf chomp $httpd; $webowner = `grep "^User " $httpd`; $webgroup = `grep "^Group " $httpd`; chomp $webowner; chomp $webgroup; (undef, $webowner) = split / /, $webowner; (undef, $webgroup) = split / /, $webgroup; } if ($confd = `find /etc /usr/local/etc -type d -name 'apache*/conf.d'`) { chomp $confd; } # If we are doing a new install.. check the postgresql installation.. if ($newinstall == 1){ # Check the postgresql version before we even check for a connection if local system("tput clear"); # Clear the screen.. our ($pghost, $pgport, $pguser, $pgpassword); print "\n\nIs PostgreSQL installed [L]ocally,\n or will you be connecting to a [R]emote server? (L/R) [L]:"; my $localremote=; if(($localremote=~/L/i) or ($localremote eq "\n")){ $pghost = 'localhost'; # If local, check the local postgresql version.. my $pgversion = `psql --version`; ($pgversionnum) = $pgversion =~ m/(\d\.\d\.\d)/; unless ($pgversionnum gt '8.0.0'){ # Die, cannot continue.. print "LedgerSMB requires postgres version 8.0 or higher. You have version $pgversionnum installed\n"; die; } } if (!&check_pgconnect){ print "\n\n\nInstallation was not successful\n Exiting....\n"; exit; } } system("tput clear"); if ($filename) { $install = "\ninstall $version from (f)ile\n"; } # check for latest version &get_latest_version; chomp $latest_version; if (!$newinstall) { $install .= "\n(r)einstall $version\n"; } if ($version && $latest_version) { if ($version ne $latest_version) { $install .= "\n(u)pgrade to $latest_version\n"; } } $install .= "\n(i)nstall $latest_version (from Internet)\n" if $latest_version; $install .= "\n(d)ownload $latest_version (no installation)" unless $filename; print qq| LedgerSMB Accounting and ERP Installation $install Enter: |; $a = ; chomp $a; exit unless $a; $a = lc $a; if ($a !~ /d/) { print qq|\nEnter httpd owner [$webowner] : |; $web = ; chomp $web; $webowner = $web if $web; print qq|\nEnter httpd group [$webgroup] : |; $web = ; chomp $web; $webgroup = $web if $web; } if ($a ne 'f') { system("tput clear"); # choose site foreach $item (sort { $a <=> $b } keys %source) { $i++; print qq|$i. $source{$item}{site}\n|; } $site = "1"; print qq|\nChoose Location [$site] : |; $b = ; chomp $b; $site = $b if $b; } if ($a eq 'd') { &download; } if ($a =~ /(i|u)/) { &install_smb; } if ($a eq 'r') { $latest_version = $version; &install_smb; } if ($a eq 'f') { &install_smb; } exit; # end main sub check_module{ my($module)=@_; eval "use $module"; if(!$@){ return 1, "Ok"; }else{ return 0, "FAILED",$@; } } sub download { &get_source_code; } sub get_latest_version { print "Checking for latest version number .... "; if ($filename) { print "skipping, filename supplied\n"; return; } my $urlresult = ''; if ($lwp) { if ($urlresult = LWP::Simple::get("$versionurl")){ $latest_version = parse_links($urlresult); } else { print "not found"; } } else { if (!$lynx) { print "\nYou must have either lynx or LWP installed"; exit 1; } $ok = `lynx -dump -head $versionurl`; if ($ok = ($ok =~ s/HTTP.*?200 //)) { $urlresult = `lynx -dump $versionurl`; $latest_version = parse_links($urlresult); } else { print "not found"; } die unless $ok; } if ($latest_version) { print "ok\n"; 1; } } my @versions = (); sub parse_links{ # Take the html retrieved by lwp or lynx and look for the version numbers. my $text = shift; my $version = ''; my $p = HTML::LinkExtor->new(\&cb); $p->parse($text) or die; foreach (@versions){ my ($chkversion) = $_ =~ /^\/ledger-smb\/ledger-smb-(\d{1,3}\.\d{1,3}\.\d{1,3}\w*)\.tar\.gz$/; $version = $chkversion if ($chkversion gt $version); } return $version; } sub cb { # Callback function for LinkExtor my($tag, %attr) = @_; return if $tag ne 'a'; return unless $attr{href} =~ /^\/ledger-smb\/ledger-smb-\d{1,3}\.\d{1,3}\.\d{1,3}\w*\.tar\.gz$/; push(@versions, values %attr); } sub get_source_code { $err = 0; @order = (); push @order, $site; for (sort { $a <=> $b } keys %source) { push @order, $_; } if ($latest_version) { # download it chomp $latest_version; $latest_version = "ledger-smb-${latest_version}.tar.gz"; print "\nStatus\n"; print "Downloading $latest_version .... "; foreach $key (@order) { print "\n$source{$key}{site} .... "; if ($lwp) { $err = LWP::Simple::getstore("$source{$key}{url}/$latest_version", "$latest_version"); $err -= 200; } else { $ok = `lynx -dump -head $source{$key}{url}/$latest_version`; $err = !($ok =~ s/HTTP.*?200 //); if (!$err) { $err = system("lynx -dump $source{$key}{url}/$latest_version > $latest_version"); } } if ($err) { print "failed!"; } else { last; } } } else { $err = -1; } if ($err) { die "Cannot get $latest_version"; } else { print "ok!\n"; } $latest_version; } sub install_smb { if ($filename) { $latest_version = $filename; } else { $latest_version = &get_source_code; } &decompress; if ($newinstall) { open(FH, "ledger-smb.conf.default"); @f = ; close(FH); unless ($latex) { grep { s/^\$latex.*/\$latex = 0;/ } @f; } open(FH, ">ledger-smb.conf"); print FH @f; close(FH); $alias = $absolutealias = $ENV{'PWD'}; $alias =~ s/.*\///g; $httpddir = `dirname $httpd`; if ($confd) { $httpddir = $confd; } chomp $httpddir; $filename = "ledger-smb-httpd.conf"; # do we have write permission? if (!open(FH, ">>$httpddir/$filename")) { open(FH, ">$filename"); $norw = 1; } $directives = qq| Alias /$alias $absolutealias/ AllowOverride All AddHandler cgi-script .pl Options ExecCGI Includes FollowSymlinks Order Allow,Deny Allow from All Order Deny,Allow Deny from All |; print FH $directives; close(FH); print qq| This is a new installation. |; if ($norw) { print qq| Webserver directives were written to $filename Copy $filename to $httpddir |; if (!$confd) { print qq| and add # Ledger-SMB Include $httpddir/$filename to $httpd |; } print qq| and restart your webserver!\n|; if (!$permset) { print qq| WARNING: permissions for templates, users, css and spool directory could not be set. Login as root and set permissions # chown -hR :$webgroup users templates css spool # chmod 771 users templates css spool |; } } else { print qq| Webserver directives were written to $httpddir/$filename |; if (!$confd) { if (!(`grep "^# LedgerSMB" $httpd`)) { open(FH, ">>$httpd"); print FH qq| # LedgerSMB Include $httpddir/$filename |; close(FH); } } if (!$>) { # send SIGHUP to httpd if ($f = `find /var -type f -name 'httpd.pid'`) { $pid = `cat $f`; chomp $pid; if ($pid) { system("kill -s HUP $pid"); } } } } } # if this is not root, check if user is part of $webgroup if ($>) { if ($permset = ($) =~ getgrnam $webgroup)) { `chown -hR :$webgroup users templates css spool`; chmod 0771, 'users', 'templates', 'css', 'spool'; `chown :$webgroup ledger-smb.conf`; } } else { # root `chown -hR 0:0 *`; `chown -hR $webowner:$webgroup users templates css spool`; chmod 0771, 'users', 'templates', 'css', 'spool'; `chown $webowner:$webgroup ledger-smb.conf`; } chmod 0644, 'ledger-smb.conf'; unlink "ledger-smb.conf.default"; &cleanup; while ($a !~ /(Y|N)/) { print qq|\nDisplay README (Y/n) : |; $a = ; chomp $a; $a = ($a) ? uc $a : 'Y'; if ($a eq 'Y') { @args = ("more", "doc/README"); system(@args); } } } sub decompress { die "Error: gzip not installed\n" unless ($gzip); die "Error: tar not installed\n" unless ($tar); &create_lockfile; # ungzip and extract source code print "Decompressing $latest_version ... "; if (system("gzip -df $latest_version")) { print "Error: Could not decompress $latest_version\n"; &remove_lockfile; exit; } else { print "done\n"; } # strip gz from latest_version $latest_version =~ s/\.gz//; # now untar it print "Unpacking $latest_version ... "; if (system("tar -xf $latest_version")) { print "Error: Could not unpack $latest_version\n"; &remove_lockfile; exit; } else { # now we have a copy in ledger-smb if (system("tar -cf $latest_version -C ledger-smb .")) { print "Error: Could not create archive for $latest_version\n"; &remove_lockfile; exit; } else { if (system("tar -xf $latest_version")) { print "Error: Could not unpack $latest_version\n"; &remove_lockfile; exit; } else { print "done\n"; print "cleaning up ... "; `rm -rf ledger-smb`; print "done\n"; } } } } sub create_lockfile { if (-d "$userspath") { open(FH, ">$userspath/nologin"); close(FH); } } sub cleanup { unlink "$latest_version"; unlink "$userspath/members.default" if (-f "$userspath/members.default"); &remove_lockfile; } sub remove_lockfile { unlink "$userspath/nologin" if (-f "$userspath/nologin") }; sub check_pgconnect{ print "We will now attempt to validate that we are able to \nconnect to your postgres database.\n"; my $cnx = 0; while (!$cnx){ print "\nPlease enter the host name of the postgresql database? ". "(ie localhost)\n [$pghost]:"; my $response=; $response =~ s/\s*//g; chomp($response); # Should probably try to validate the hostname here.. but for now, we'll leave it. $response = $pghost if ($response eq ''); while (!$pgport){ print "\nPlease enter the port postgres is listening on.\n[5432]:"; $pgport=; chomp($pgport); $pgport = 5432 if ($pgport eq ''); if (($pgport =~ /\D/)||($pgport > 65535)){ print "\nThe port must be a number between 0 and 65535, ". "postgres default is 5432\n"; undef $pgport; } } while (!$pguser){ print "\nPlease enter a valid postgres user name ". "to validate the connection.:"; $pguser=; chomp($pguser); if ($pguser eq ''){ print "\nYou must enter a username\n"; } } while (!$pgpass){ print "\nPlease enter a valid postgres password ". "to validate the connection.:"; $pgpass=; chomp($pgpass); if ($pgpass eq ''){ print "\nYou must enter a password\n"; } } # Try to connect; eval { my $dbh = DBI->connect("dbi:Pg:dbname=template1;host=$response;". "port=$pgport;", $pguser, $pgpass) or die $DBI::errstr; my $version = $dbh->get_info( 18 ); if ($version =~ /^07/){ die "You have postgres version $version installed, ". "we require a minimum of 8.0\n"; } }; if ($@){ system("tput clear"); print "Connection to the database was unsucessful\n". "The error we received was this:\n$@\n". "Would you like to try to enter the authentication ". "information again? (Y/N)[N]:"; $answer=; chomp($answer); if($answer=~/n/i){ $cnx = 1; } } else { $cnx = 1; } } # Try to guide the user to an answer to the connection problems. system("tput clear"); # Clear the screen.. print "Have you already set up a database user for LedgerSMB? (Y/N) [N]:"; $answer = ; chomp($answer); if(($answer=~/n/i) or ($answer eq "")){ print q| If you have not set up a database user yet, you can use the following command: # su postgres $ createuser -d ledger-smb Shall the new user be allowed to create databases? (y/n) y Shall the new user be allowed to create more new users? (y/n) n if you use passwords to access postgres use this command $ createuser -d -P ledger-smb |; return 0; } # Maybe they did not change pg_hba.conf? print qq|Did you modify pg_hba.conf to allow access? See pg_hba.conf example entries here: http://www.postgresql.org/docs/8.0/static/client-authentication.html#EXAMPLE-PG-HBA.CONF A good starting point would be to add something similar to below to pg_hba.conf: host all all 192.168.1.5/32 MD5 Which would allow a connection from any user, to any database, from that one IP (You will need to change this for your setup) Using a MD5 password. Also, remember to reload postgres when you make the change. Did you allow access to the postgres database from the web server? (Y/N) [Y]:|; $answer = ; if(($response=~/n/i) or ($response eq "\n")){ return 0; } # Add other checks here.. return 0; } print "\nConnection Successful, Press enter to continue\n"; $answer=; return 1;