summaryrefslogtreecommitdiff
path: root/setup.pl
blob: 9b583450d1bda09e9a01593f220b189524e4c2fd (plain)
  1. #!/usr/bin/perl
  2. #
  3. ######################################################################
  4. # LedgerSMB Small Medium Business Accounting Software Installer
  5. # http://www.ledgersmb.org/
  6. #
  7. # Copyright (C) 2006
  8. # This work contains copyrighted information from a number of sources all used
  9. # with permission.
  10. #
  11. # This file contains source code included with or based on SQL-Ledger which
  12. # is Copyright Dieter Simader and DWS Systems Inc. 2000-2005 and licensed
  13. # under the GNU General Public License version 2 or, at your option, any later
  14. # version. For a full list including contact information of contributors,
  15. # maintainers, and copyright holders, see the CONTRIBUTORS file.
  16. #
  17. # Original Copyright Notice from SQL-Ledger 2.6.17 (before the fork):
  18. # Copyright (c) 2002, Dieter Simader
  19. #
  20. # Web: http://www.sql-ledger.org
  21. #
  22. #######################################################################
  23. # Next bunch of lines are to check to see if they have the cpan module installed.
  24. my $cpan=0;
  25. eval {
  26. use CPAN;
  27. };
  28. if (!$@){
  29. $cpan = 1;
  30. }
  31. $| = 1;
  32. #not sure how safe this is. If the browser sends a blank HTTP_USER_AGENT
  33. #will this script destroy part of the install?
  34. #This script should probably be made inaccessible via HTTP until this feature is working
  35. if (($ENV{HTTP_USER_AGENT})||($ENV{HTTP_HOST})) {
  36. print "Content-type: text/html\n\nThis does not work yet! use $0 from the command line";
  37. exit;
  38. }
  39. # Make sure they have the required perl modules installed.
  40. # bin/mozilla/admin.pl needs Digest::MD5 for session handling
  41. # HTML:LinkExtor is used by the setup program.
  42. my @req_modules=(qw(DBI DBD::Pg Digest::MD5 HTML::LinkExtor));
  43. foreach my $module(@req_modules){
  44. print "Checking for: $module ...\t";
  45. my @results=&check_module($module);
  46. print "$results[1]\n";
  47. next if($results[0]); # Passed, no need to continue..
  48. if ($cpan == 1){
  49. # Can try to install the module..
  50. print "\n\nWould you like to try and install this package ($module) through CPAN? (Y/N) [Y]:";
  51. my $response=<STDIN>;
  52. if(($response=~/y/i) or ($response eq "\n")){
  53. my $inst_obj = CPAN::Shell->install($module);
  54. @results=&check_module($module);
  55. if(!$results[0]){
  56. print "\n\nCould not install $module using CPAN.\n";
  57. die "Please try to install this module manually\n";
  58. }
  59. } else {
  60. die "Please install the $module perl module and retry the setup.\n";
  61. }
  62. } else {
  63. # Can't try to install the module..
  64. die "Please install the $module perl module and retry the setup.\n";
  65. }
  66. }
  67. use HTML::LinkExtor;
  68. my $lynx = `lynx -version`; # if LWP is not installed use lynx
  69. my $gzip = `gzip -V 2>&1`; # gz decompression utility
  70. my $tar = `tar --version 2>&1`; # tar archiver
  71. my $latex = `latex -version`;
  72. my $versionurl ='http://prdownloads.sourceforge.net/ledger-smb';
  73. my %source = (
  74. 1 => { url => "http://voxel.dl.sourceforge.net/sourceforge/ledger-smb", site => "New York, U.S.A", locale => 'us' },
  75. 2 => { url => "http://easynews.dl.sourceforge.net/sourceforge/ledger-smb", site => "Arizona, U.S.A", locale => 'us' },
  76. 3 => { url => "http://ufpr.dl.sourceforge.net/sourceforge/ledger-smb", site =>"Brazil", locale => 'br' },
  77. 4 => { url => "http://surfnet.dl.sourceforge.net/sourceforge/ledger-smb", site => "The Netherlands", locale => 'nl' },
  78. 5 => { url => "http://http://kent.dl.sourceforge.net/sourceforge/ledger-smb", site => "U.K", locale => 'uk' },
  79. 6 => { url => "http://ovh.dl.sourceforge.net/sourceforge/ledger-smb", site => "France", locale => 'fr' },
  80. 7 => { url => "http://mesh.dl.sourceforge.net/sourceforge/ledger-smb", site => "Germany", locale => 'de' },
  81. 8 => { url => "http://citkit.dl.sourceforge.net/sourceforge/ledger-smb", site => "Russia", locale => 'ru' },
  82. 9 => { url => "http://optusnet.dl.sourceforge.net/sourceforge/ledger-smb", site => "Sydney, Australia", locale => 'au' },
  83. 10 => { url => "http://nchc.dl.sourceforge.net/sourceforge/ledger-smb", site => "Taiwan", locale => 'tw' },
  84. 11 => { url => "http://jaist.dl.sourceforge.net/sourceforge/ledger-smb", site => "Japan", locale => 'jp' },
  85. 12 => { url => "http://heanet.dl.sourceforge.net/sourceforge/ledger-smb", site => "Ireland", locale => 'ie' }
  86. );
  87. my $userspath = "users"; # default for new installation
  88. eval { require "ledger-smb.conf"; };
  89. my $filename = shift;
  90. chomp $filename;
  91. my $newinstall = 1;
  92. # is LWP installed
  93. eval { require LWP::Simple; };
  94. $lwp = !($@);
  95. unless ($lwp || $lynx || $filename) {
  96. die "You must have either lynx or LWP installed or specify a filename.
  97. perl $0 <filename>\n";
  98. }
  99. if ($filename) {
  100. # extract version
  101. die "Not a Ledger-SMB archive\n" if ($filename !~ /^ledger-smb/);
  102. $version = $filename;
  103. $version =~ s/ledger-smb-(\d+\.\d+\.\d+).*$/$1/;
  104. }
  105. if (-f "VERSION") {
  106. # get installed version from VERSION file
  107. open(FH, "VERSION");
  108. @a = <FH>;
  109. close(FH);
  110. $version = $a[0];
  111. chomp $version;
  112. $newinstall = !$version;
  113. if (! -f "ledger-smb.conf") {
  114. $newinstall = 1;
  115. }
  116. }
  117. # Try to determine web user and group..
  118. $webowner = "nobody";
  119. $webgroup = "nogroup";
  120. # Check for apache2.conf
  121. if ($httpd = `find /etc /usr/local/etc -type f -name 'apache2.conf'`) {
  122. chomp $httpd;
  123. $webowner = `grep "^User " $httpd`;
  124. $webgroup = `grep "^Group " $httpd`;
  125. chomp $webowner;
  126. chomp $webgroup;
  127. (undef, $webowner) = split / /, $webowner;
  128. (undef, $webgroup) = split / /, $webgroup;
  129. } elsif ($httpd = `find /etc /usr/local/etc -type f -name 'httpd.conf'`) {
  130. # Else check for httpd.conf
  131. chomp $httpd;
  132. $webowner = `grep "^User " $httpd`;
  133. $webgroup = `grep "^Group " $httpd`;
  134. chomp $webowner;
  135. chomp $webgroup;
  136. (undef, $webowner) = split / /, $webowner;
  137. (undef, $webgroup) = split / /, $webgroup;
  138. }
  139. if ($confd = `find /etc /usr/local/etc -type d -name 'apache*/conf.d'`) {
  140. chomp $confd;
  141. }
  142. # If we are doing a new install.. check the postgresql installation..
  143. if ($newinstall == 1){
  144. # Check the postgresql version before we even check for a connection if local
  145. system("tput clear"); # Clear the screen..
  146. our ($pghost, $pgport, $pguser, $pgpassword);
  147. print "\n\nIs PostgreSQL installed [L]ocally,\n or will you be connecting to a [R]emote server? (L/R) [L]:";
  148. my $localremote=<STDIN>;
  149. if(($localremote=~/L/i) or ($localremote eq "\n")){
  150. $pghost = 'localhost';
  151. # If local, check the local postgresql version..
  152. my $pgversion = `psql --version`;
  153. ($pgversionnum) = $pgversion =~ m/(\d\.\d\.\d)/;
  154. unless ($pgversionnum gt '8.0.0'){
  155. # Die, cannot continue..
  156. print "LedgerSMB requires postgres version 8.0 or higher. You have version $pgversionnum installed\n";
  157. die;
  158. }
  159. }
  160. if (!&check_pgconnect){
  161. print "\n\n\nInstallation was not successful\n Exiting....\n";
  162. exit;
  163. }
  164. }
  165. system("tput clear");
  166. if ($filename) {
  167. $install = "\ninstall $version from (f)ile\n";
  168. }
  169. # check for latest version
  170. &get_latest_version;
  171. chomp $latest_version;
  172. if (!$newinstall) {
  173. $install .= "\n(r)einstall $version\n";
  174. }
  175. if ($version && $latest_version) {
  176. if ($version ne $latest_version) {
  177. $install .= "\n(u)pgrade to $latest_version\n";
  178. }
  179. }
  180. $install .= "\n(i)nstall $latest_version (from Internet)\n" if $latest_version;
  181. $install .= "\n(d)ownload $latest_version (no installation)" unless $filename;
  182. print qq|
  183. LedgerSMB Accounting and ERP Installation
  184. $install
  185. Enter: |;
  186. $a = <STDIN>;
  187. chomp $a;
  188. exit unless $a;
  189. $a = lc $a;
  190. if ($a !~ /d/) {
  191. print qq|\nEnter httpd owner [$webowner] : |;
  192. $web = <STDIN>;
  193. chomp $web;
  194. $webowner = $web if $web;
  195. print qq|\nEnter httpd group [$webgroup] : |;
  196. $web = <STDIN>;
  197. chomp $web;
  198. $webgroup = $web if $web;
  199. }
  200. if ($a ne 'f') {
  201. system("tput clear");
  202. # choose site
  203. foreach $item (sort { $a <=> $b } keys %source) {
  204. $i++;
  205. print qq|$i. $source{$item}{site}\n|;
  206. }
  207. $site = "1";
  208. print qq|\nChoose Location [$site] : |;
  209. $b = <STDIN>;
  210. chomp $b;
  211. $site = $b if $b;
  212. }
  213. if ($a eq 'd') {
  214. &download;
  215. }
  216. if ($a =~ /(i|u)/) {
  217. &install_smb;
  218. }
  219. if ($a eq 'r') {
  220. $latest_version = $version;
  221. &install_smb;
  222. }
  223. if ($a eq 'f') {
  224. &install_smb;
  225. }
  226. exit;
  227. # end main
  228. sub check_module{
  229. my($module)=@_;
  230. eval "use $module";
  231. if(!$@){
  232. return 1, "Ok";
  233. }else{
  234. return 0, "FAILED",$@;
  235. }
  236. }
  237. sub download {
  238. &get_source_code;
  239. }
  240. sub get_latest_version {
  241. print "Checking for latest version number .... ";
  242. if ($filename) {
  243. print "skipping, filename supplied\n";
  244. return;
  245. }
  246. my $urlresult = '';
  247. if ($lwp) {
  248. if ($urlresult = LWP::Simple::get("$versionurl")){
  249. $latest_version = parse_links($urlresult);
  250. } else {
  251. print "not found";
  252. }
  253. } else {
  254. if (!$lynx) {
  255. print "\nYou must have either lynx or LWP installed";
  256. exit 1;
  257. }
  258. $ok = `lynx -dump -head $versionurl`;
  259. if ($ok = ($ok =~ s/HTTP.*?200 //)) {
  260. $urlresult = `lynx -dump $versionurl`;
  261. $latest_version = parse_links($urlresult);
  262. } else {
  263. print "not found";
  264. }
  265. die unless $ok;
  266. }
  267. if ($latest_version) {
  268. print "ok\n";
  269. 1;
  270. }
  271. }
  272. my @versions = ();
  273. sub parse_links{
  274. # Take the html retrieved by lwp or lynx and look for the version numbers.
  275. my $text = shift;
  276. my $version = '';
  277. my $p = HTML::LinkExtor->new(\&cb);
  278. $p->parse($text) or die;
  279. foreach (@versions){
  280. my ($chkversion) = $_ =~ /^\/ledger-smb\/ledger-smb-(\d{1,3}\.\d{1,3}\.\d{1,3}\w*)\.tar\.gz$/;
  281. $version = $chkversion if ($chkversion gt $version);
  282. }
  283. return $version;
  284. }
  285. sub cb {
  286. # Callback function for LinkExtor
  287. my($tag, %attr) = @_;
  288. return if $tag ne 'a';
  289. return unless $attr{href} =~ /^\/ledger-smb\/ledger-smb-\d{1,3}\.\d{1,3}\.\d{1,3}\w*\.tar\.gz$/;
  290. push(@versions, values %attr);
  291. }
  292. sub get_source_code {
  293. $err = 0;
  294. @order = ();
  295. push @order, $site;
  296. for (sort { $a <=> $b } keys %source) {
  297. push @order, $_;
  298. }
  299. if ($latest_version) {
  300. # download it
  301. chomp $latest_version;
  302. $latest_version = "ledger-smb-${latest_version}.tar.gz";
  303. print "\nStatus\n";
  304. print "Downloading $latest_version .... ";
  305. foreach $key (@order) {
  306. print "\n$source{$key}{site} .... ";
  307. if ($lwp) {
  308. $err = LWP::Simple::getstore("$source{$key}{url}/$latest_version", "$latest_version");
  309. $err -= 200;
  310. } else {
  311. $ok = `lynx -dump -head $source{$key}{url}/$latest_version`;
  312. $err = !($ok =~ s/HTTP.*?200 //);
  313. if (!$err) {
  314. $err = system("lynx -dump $source{$key}{url}/$latest_version > $latest_version");
  315. }
  316. }
  317. if ($err) {
  318. print "failed!";
  319. } else {
  320. last;
  321. }
  322. }
  323. } else {
  324. $err = -1;
  325. }
  326. if ($err) {
  327. die "Cannot get $latest_version";
  328. } else {
  329. print "ok!\n";
  330. }
  331. $latest_version;
  332. }
  333. sub install_smb {
  334. if ($filename) {
  335. $latest_version = $filename;
  336. } else {
  337. $latest_version = &get_source_code;
  338. }
  339. &decompress;
  340. if ($newinstall) {
  341. open(FH, "ledger-smb.conf.default");
  342. @f = <FH>;
  343. close(FH);
  344. unless ($latex) {
  345. grep { s/^\$latex.*/\$latex = 0;/ } @f;
  346. }
  347. open(FH, ">ledger-smb.conf");
  348. print FH @f;
  349. close(FH);
  350. $alias = $absolutealias = $ENV{'PWD'};
  351. $alias =~ s/.*\///g;
  352. $httpddir = `dirname $httpd`;
  353. if ($confd) {
  354. $httpddir = $confd;
  355. }
  356. chomp $httpddir;
  357. $filename = "ledger-smb-httpd.conf";
  358. # do we have write permission?
  359. if (!open(FH, ">>$httpddir/$filename")) {
  360. open(FH, ">$filename");
  361. $norw = 1;
  362. }
  363. $directives = qq|
  364. Alias /$alias $absolutealias/
  365. <Directory $absolutealias>
  366. AllowOverride All
  367. AddHandler cgi-script .pl
  368. Options ExecCGI Includes FollowSymlinks
  369. Order Allow,Deny
  370. Allow from All
  371. </Directory>
  372. <Directory $absolutealias/users>
  373. Order Deny,Allow
  374. Deny from All
  375. </Directory>
  376. |;
  377. print FH $directives;
  378. close(FH);
  379. print qq|
  380. This is a new installation.
  381. |;
  382. if ($norw) {
  383. print qq|
  384. Webserver directives were written to $filename
  385. Copy $filename to $httpddir
  386. |;
  387. if (!$confd) {
  388. print qq| and add
  389. # Ledger-SMB
  390. Include $httpddir/$filename
  391. to $httpd
  392. |;
  393. }
  394. print qq| and restart your webserver!\n|;
  395. if (!$permset) {
  396. print qq|
  397. WARNING: permissions for templates, users, css and spool directory
  398. could not be set. Login as root and set permissions
  399. # chown -hR :$webgroup users templates css spool
  400. # chmod 771 users templates css spool
  401. |;
  402. }
  403. } else {
  404. print qq|
  405. Webserver directives were written to
  406. $httpddir/$filename
  407. |;
  408. if (!$confd) {
  409. if (!(`grep "^# LedgerSMB" $httpd`)) {
  410. open(FH, ">>$httpd");
  411. print FH qq|
  412. # LedgerSMB
  413. Include $httpddir/$filename
  414. |;
  415. close(FH);
  416. }
  417. }
  418. if (!$>) {
  419. # send SIGHUP to httpd
  420. if ($f = `find /var -type f -name 'httpd.pid'`) {
  421. $pid = `cat $f`;
  422. chomp $pid;
  423. if ($pid) {
  424. system("kill -s HUP $pid");
  425. }
  426. }
  427. }
  428. }
  429. }
  430. # if this is not root, check if user is part of $webgroup
  431. if ($>) {
  432. if ($permset = ($) =~ getgrnam $webgroup)) {
  433. `chown -hR :$webgroup users templates css spool`;
  434. chmod 0771, 'users', 'templates', 'css', 'spool';
  435. `chown :$webgroup ledger-smb.conf`;
  436. }
  437. } else {
  438. # root
  439. `chown -hR 0:0 *`;
  440. `chown -hR $webowner:$webgroup users templates css spool`;
  441. chmod 0771, 'users', 'templates', 'css', 'spool';
  442. `chown $webowner:$webgroup ledger-smb.conf`;
  443. }
  444. chmod 0644, 'ledger-smb.conf';
  445. unlink "ledger-smb.conf.default";
  446. &cleanup;
  447. while ($a !~ /(Y|N)/) {
  448. print qq|\nDisplay README (Y/n) : |;
  449. $a = <STDIN>;
  450. chomp $a;
  451. $a = ($a) ? uc $a : 'Y';
  452. if ($a eq 'Y') {
  453. @args = ("more", "doc/README");
  454. system(@args);
  455. }
  456. }
  457. }
  458. sub decompress {
  459. die "Error: gzip not installed\n" unless ($gzip);
  460. die "Error: tar not installed\n" unless ($tar);
  461. &create_lockfile;
  462. # ungzip and extract source code
  463. print "Decompressing $latest_version ... ";
  464. if (system("gzip -df $latest_version")) {
  465. print "Error: Could not decompress $latest_version\n";
  466. &remove_lockfile;
  467. exit;
  468. } else {
  469. print "done\n";
  470. }
  471. # strip gz from latest_version
  472. $latest_version =~ s/\.gz//;
  473. # now untar it
  474. print "Unpacking $latest_version ... ";
  475. if (system("tar -xf $latest_version")) {
  476. print "Error: Could not unpack $latest_version\n";
  477. &remove_lockfile;
  478. exit;
  479. } else {
  480. # now we have a copy in ledger-smb
  481. if (system("tar -cf $latest_version -C ledger-smb .")) {
  482. print "Error: Could not create archive for $latest_version\n";
  483. &remove_lockfile;
  484. exit;
  485. } else {
  486. if (system("tar -xf $latest_version")) {
  487. print "Error: Could not unpack $latest_version\n";
  488. &remove_lockfile;
  489. exit;
  490. } else {
  491. print "done\n";
  492. print "cleaning up ... ";
  493. `rm -rf ledger-smb`;
  494. print "done\n";
  495. }
  496. }
  497. }
  498. }
  499. sub create_lockfile {
  500. if (-d "$userspath") {
  501. open(FH, ">$userspath/nologin");
  502. close(FH);
  503. }
  504. }
  505. sub cleanup {
  506. unlink "$latest_version";
  507. unlink "$userspath/members.default" if (-f "$userspath/members.default");
  508. &remove_lockfile;
  509. }
  510. sub remove_lockfile { unlink "$userspath/nologin" if (-f "$userspath/nologin") };
  511. sub check_pgconnect{
  512. print "We will now attempt to validate that we are able to \nconnect to your postgres database.\n";
  513. my $cnx = 0;
  514. while (!$cnx){
  515. print "\nPlease enter the host name of the postgresql database? ".
  516. "(ie localhost)\n [$pghost]:";
  517. my $response=<STDIN>;
  518. $response =~ s/\s*//g;
  519. chomp($response);
  520. # Should probably try to validate the hostname here.. but for now, we'll leave it.
  521. $response = $pghost if ($response eq '');
  522. while (!$pgport){
  523. print "\nPlease enter the port postgres is listening on.\n[5432]:";
  524. $pgport=<STDIN>;
  525. chomp($pgport);
  526. $pgport = 5432 if ($pgport eq '');
  527. if (($pgport =~ /\D/)||($pgport > 65535)){
  528. print "\nThe port must be a number between 0 and 65535, ".
  529. "postgres default is 5432\n";
  530. undef $pgport;
  531. }
  532. }
  533. while (!$pguser){
  534. print "\nPlease enter a valid postgres user name ".
  535. "to validate the connection.:";
  536. $pguser=<STDIN>;
  537. chomp($pguser);
  538. if ($pguser eq ''){
  539. print "\nYou must enter a username\n";
  540. }
  541. }
  542. while (!$pgpass){
  543. print "\nPlease enter a valid postgres password ".
  544. "to validate the connection.:";
  545. $pgpass=<STDIN>;
  546. chomp($pgpass);
  547. if ($pgpass eq ''){
  548. print "\nYou must enter a password\n";
  549. }
  550. }
  551. # Try to connect;
  552. eval {
  553. my $dbh = DBI->connect("dbi:Pg:dbname=template1;host=$response;".
  554. "port=$pgport;", $pguser, $pgpass) or die $DBI::errstr;
  555. my $version = $dbh->get_info( 18 );
  556. if ($version =~ /^07/){
  557. die "You have postgres version $version installed, ".
  558. "we require a minimum of 8.0\n";
  559. }
  560. };
  561. if ($@){
  562. system("tput clear");
  563. print "Connection to the database was unsucessful\n".
  564. "The error we received was this:\n$@\n".
  565. "Would you like to try to enter the authentication ".
  566. "information again? (Y/N)[N]:";
  567. $answer=<STDIN>;
  568. chomp($answer);
  569. if($answer=~/n/i){
  570. $cnx = 1;
  571. }
  572. } else {
  573. $cnx = 1;
  574. }
  575. }
  576. # Try to guide the user to an answer to the connection problems.
  577. system("tput clear"); # Clear the screen..
  578. print "Have you already set up a database user for LedgerSMB? (Y/N) [N]:";
  579. $answer = <STDIN>;
  580. chomp($answer);
  581. if(($answer=~/n/i) or ($answer eq "")){
  582. print q|
  583. If you have not set up a database user yet, you can use the following command:
  584. # su postgres
  585. $ createuser -d ledger-smb
  586. Shall the new user be allowed to create databases? (y/n) y
  587. Shall the new user be allowed to create more new users? (y/n) n
  588. if you use passwords to access postgres use this command
  589. $ createuser -d -P ledger-smb
  590. |;
  591. return 0;
  592. }
  593. # Maybe they did not change pg_hba.conf?
  594. print qq|Did you modify pg_hba.conf to allow access?
  595. See pg_hba.conf example entries here:
  596. http://www.postgresql.org/docs/8.0/static/client-authentication.html#EXAMPLE-PG-HBA.CONF
  597. A good starting point would be to add something similar to below to pg_hba.conf:
  598. host all all 192.168.1.5/32 MD5
  599. Which would allow a connection
  600. from any user,
  601. to any database,
  602. from that one IP (You will need to change this for your setup)
  603. Using a MD5 password.
  604. Also, remember to reload postgres when you make the change.
  605. Did you allow access to the postgres database from the web server?
  606. (Y/N) [Y]:|;
  607. $answer = <STDIN>;
  608. if(($response=~/n/i) or ($response eq "\n")){
  609. return 0;
  610. }
  611. # Add other checks here..
  612. return 0;
  613. }
  614. print "\nConnection Successful, Press enter to continue\n";
  615. $answer=<STDIN>;
  616. return 1;