+
+# This subroutine corrects relative paths to ensure they
+# will work within the SM space. If the path falls within
+# the SM directory tree, the SM_PATH variable will be
+# prepended to the path, if not, then the path will be
+# converted to an absolute path, e.g.
+# '../images/logo.gif' --> SM_PATH . 'images/logo.gif'
+# '../../someplace/data' --> '/absolute/path/someplace/data'
+# 'images/logo.gif' --> SM_PATH . 'config/images/logo.gif'
+# '/absolute/path/logo.gif' --> '/absolute/path/logo.gif'
+# 'http://whatever/' --> 'http://whatever'
+# $some_var/path --> "$some_var/path"
+sub change_to_SM_path() {
+ my ($old_path) = @_;
+ my $new_path = '';
+ my @rel_path;
+ my @abs_path;
+ my $subdir;
+
+ # If the path is absolute, don't bother.
+ return "\'" . $old_path . "\'" if ( $old_path eq '');
+ return "\'" . $old_path . "\'" if ( $old_path =~ /^(\/|http)/ );
+ return "\'" . $old_path . "\'" if ( $old_path =~ /^\w:\// );
+ return $old_path if ( $old_path =~ /^\'(\/|http)/ );
+ return $old_path if ( $old_path =~ /^\'\w:\// );
+ return $old_path if ( $old_path =~ /^SM_PATH/);
+
+ if ( $old_path =~ /^\$/ ) {
+ # check if it's a single var, or a $var/path combination
+ # if it's $var/path, enclose in ""
+ if ( $old_path =~ /\// ) {
+ return '"'.$old_path.'"';
+ }
+ return $old_path;
+ }
+
+ # Remove remaining '
+ $old_path =~ s/\'//g;
+
+ # For relative paths, split on '../'
+ @rel_path = split(/\.\.\//, $old_path);
+
+ if ( $#rel_path > 1 ) {
+ # more than two levels away. Make it absolute.
+ @abs_path = split(/\//, $dir);
+
+ # Lop off the relative pieces of the absolute path..
+ for ( $i = 0; $i <= $#rel_path; $i++ ) {
+ pop @abs_path;
+ shift @rel_path;
+ }
+ push @abs_path, @rel_path;
+ $new_path = "\'" . join('/', @abs_path) . "\'";
+ } elsif ( $#rel_path > 0 ) {
+ # it's within the SM tree, prepend SM_PATH
+ $new_path = $old_path;
+ $new_path =~ s/^\.\.\//SM_PATH . \'/;
+ $new_path .= "\'";
+ } else {
+ # Last, it's a relative path without any leading '.'
+ # Prepend SM_PATH and config, since the paths are
+ # relative to the config directory
+ $new_path = "SM_PATH . \'config/" . $old_path . "\'";
+ }
+ return $new_path;
+}
+
+
+# Change SM_PATH to admin-friendly version, e.g.:
+# SM_PATH . 'images/logo.gif' --> '../images/logo.gif'
+# SM_PATH . 'config/some.php' --> 'some.php'
+# '/absolute/path/logo.gif' --> '/absolute/path/logo.gif'
+# 'http://whatever/' --> 'http://whatever'
+sub change_to_rel_path() {
+ my ($old_path) = @_;
+ my $new_path = $old_path;
+
+ if ( $old_path =~ /^SM_PATH/ ) {
+ # FIXME: the following replacement loses the opening quote mark!
+ $new_path =~ s/^SM_PATH . \'/\.\.\//;
+ $new_path =~ s/\.\.\/config\///;
+ }
+
+ return $new_path;
+}
+
+# Attempts to auto-detect if a specific auth mechanism is supported.
+# Called by 'command112a' and 'command112b'
+# ARGS: service-name (IMAP or SMTP), host:port, mech-name (ie. CRAM-MD5)
+sub detect_auth_support {
+ # Try loading IO::Socket
+ unless (eval("use IO::Socket; 1")) {
+ print "Perl IO::Socket module is not available.";
+ return undef;
+ }
+ # Misc setup
+ my $service = shift;
+ my $host = shift;
+ my $mech = shift;
+ # Sanity checks
+ if ((!defined($service)) or (!defined($host)) or (!defined($mech))) {
+ # Error - wrong # of args
+ print "BAD ARGS!\n";
+ return undef;
+ }
+
+ if ($service eq 'SMTP') {
+ $cmd = "AUTH $mech\r\n";
+ $logout = "QUIT\r\n";
+ } elsif ($service eq 'IMAP') {
+ $cmd = "A01 AUTHENTICATE $mech\n";
+ $logout = "C01 LOGOUT\n";
+ } else {
+ # unknown service - whoops.
+ return undef;
+ }
+
+ # Get this show on the road
+ my $sock=IO::Socket::INET->new($host);
+ if (!defined($sock)) {
+ # Connect failed
+ return undef;
+ }
+ my $discard = <$sock>; # Server greeting/banner - who cares..
+
+ if ($service eq 'SMTP') {
+ # Say hello first..
+ print $sock "HELO $domain\r\n";
+ $discard = <$sock>; # Yeah yeah, you're happy to see me..
+ }
+ print $sock $cmd;
+
+ my $response = <$sock>;
+ chomp($response);
+ if (!defined($response)) {
+ return undef;
+ }
+
+ # So at this point, we have a response, and it is (hopefully) valid.
+ if ($service eq 'SMTP') {
+ if (($response =~ /^535/) or ($response =~/^502/)) {
+ # Not supported
+ print $sock $logout;
+ close $sock;
+ return 'NO';
+ } elsif ($response =~ /^503/) {
+ #Something went wrong
+ return undef;
+ }
+ } elsif ($service eq 'IMAP') {
+ if ($response =~ /^A01/) {
+ # Not supported
+ print $sock $logout;
+ close $sock;
+ return 'NO';
+ }
+ } else {
+ # Unknown service - this shouldn't be able to happen.
+ close $sock;
+ return undef;
+ }
+
+ # If it gets here, the mech is supported
+ print $sock "*\n"; # Attempt to cancel authentication
+ print $sock $logout; # Try to log out, but we don't really care if this fails
+ close $sock;
+ return 'YES';
+}
+
+# trims whitespace
+# Example code from O'Reilly Perl Cookbook
+sub trim {
+ my @out = @_;
+ for (@out) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ return wantarray ? @out : $out[0];
+}
+
+sub clear_screen() {
+ if ( $^O =~ /^mswin/i) {
+ system "cls";
+ } else {
+ system "clear";
+ }
+}
+
+# checks IMAP mailbox name. Refuses to accept 8bit folders
+# returns 0 (folder name is not correct) or 1 (folder name is correct)
+sub check_imap_folder($) {
+ my $folder_name = shift(@_);
+
+ if ($folder_name =~ /[\x80-\xFFFF]/) {
+ print "Folder name contains 8bit characters. Configuration utility requires\n";
+ print "UTF7-IMAP encoded folder names.\n";
+ print "Press enter to continue...";
+ my $tmp = <STDIN>;
+ return 0;
+ } elsif ($folder_name =~ /[&\*\%]/) {
+ # check for ampersand and list-wildcards
+ print "Folder name contains special UTF7-IMAP characters.\n";
+ print "Are you sure that folder name is correct? (yN): ";
+ my $tmp = <STDIN>;
+ $tmp = lc(trim($tmp));
+ if ($tmp =~ /^y$/) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 1;
+ }
+}
+
+# quotes string written in single quotes
+sub quote_single($) {
+ my $string = shift(@_);
+ $string =~ s/\'/\\'/g;
+ return $string;
+}
+
+# determine a plugin's version number
+#
+# parses the setup.php file, looking for the
+# version string in the <plugin>_info() or the
+# <plugin>_version functions.
+#
+sub get_plugin_version() {
+
+ my $plugin_name = shift(@_);
+
+ $setup_file = '../plugins/' . $plugin_name . '/setup.php';
+ if ( -e "$setup_file" ) {
+ # Make sure that file is readable
+ if (! -r "$setup_file") {
+ print "\n";
+ print "WARNING:\n";
+ print "The file \"$setup_file\" was found, but you don't\n";
+ print "have rights to read it. The plugin \"";
+ print $plugin_name . "\" may not work correctly until you fix this.\n";
+ print "\nPress enter to continue";
+ $ctu = <STDIN>;
+ print "\n";
+ next;
+ }
+
+ $version = ' ';
+# FIXME: grep the file instead of reading it into memory?
+ $whole_file = '';
+ open( FILE, "$setup_file" );
+ while ( $line = <FILE> ) {
+ $whole_file .= $line;
+ }
+ close(FILE);
+
+ # ideally, there is a version in the <plugin>_info function...
+ #
+ if ($whole_file =~ /('version'\s*=>\s*['"](.*?)['"])/) {
+ $version .= $2;
+
+ # this assumes there is only one function that returns
+ # a static string in the setup file
+ #
+ } elsif ($whole_file =~ /(return\s*['"](.*?)['"])/) {
+ $version .= $2;
+ }
+
+ return $version;
+
+ } else {
+ print "\n";
+ print "WARNING:\n";
+ print "The file \"$setup_file\" was not found.\n";
+ print "The plugin \"" . $plugin_name;
+ print "\" may not work correctly until you fix this.\n";
+ print "\nPress enter to continue";
+ $ctu = <STDIN>;
+ print "\n";
+ next;
+ }
+
+}
+
+# parses the setup.php files for all activated plugins and
+# builds static plugin hooks array so we don't have to load
+# ALL plugins are runtime and build the hook array on every
+# page request
+#
+# hook array is saved in config/plugin_hooks.php
+#
+# Note the $verbose variable at the top of this routine
+# can be set to zero to quiet it down.
+#
+# NOTE/FIXME: we aren't necessarily interested in writing
+# a full-blown PHP parsing engine, so plenty
+# of assumptions are included herein about the
+# coding of the plugin setup files, and things
+# like commented out curly braces or other
+# such oddities can break this in a bad way.
+#
+sub build_plugin_hook_array() {
+
+ $verbose = 1;
+
+ if ($verbose) {
+ print "\n\n";
+ }
+
+ if ( open( HOOKFILE, ">plugin_hooks.php" ) ) {
+ print HOOKFILE "<?php\n";
+ print HOOKFILE "\n";
+
+ print HOOKFILE "/**\n";
+ print HOOKFILE " * SquirrelMail Plugin Hook Registration File\n";
+ print HOOKFILE " * Auto-generated using the configure script, conf.pl\n";
+ print HOOKFILE " */\n";
+ print HOOKFILE "\n";
+ print HOOKFILE "global \$squirrelmail_plugin_hooks;\n";
+ print HOOKFILE "\n";
+
+PLUGIN: for ( $ct = 0 ; $ct <= $#plugins ; $ct++ ) {
+
+ if ($verbose) {
+ print "Activating plugin \"" . $plugins[$ct] . "\"...\n";
+ }
+
+ $setup_file = '../plugins/' . $plugins[$ct] . '/setup.php';
+ if ( -e "$setup_file" ) {
+ # Make sure that file is readable
+ if (! -r "$setup_file") {
+ print "\n";
+ print "WARNING:\n";
+ print "The file \"$setup_file\" was found, but you don't\n";
+ print "have rights to read it. The plugin \"";
+ print $plugins[$ct] . "\" will not be activated until you fix this.\n";
+ print "\nPress enter to continue";
+ $ctu = <STDIN>;
+ print "\n";
+ next;
+ }
+ open( FILE, "$setup_file" );
+ $inside_init_fxn = 0;
+ $brace_count = 0;
+ while ( $line = <FILE> ) {
+
+ # throw away lines until we get to target function
+ #
+ if (!$inside_init_fxn
+ && $line !~ /^\s*function\s*squirrelmail_plugin_init_/i) {
+ next;
+ }
+ $inside_init_fxn = 1;
+
+
+ # count open braces
+ #
+ if ($line =~ /{/) {
+ $brace_count++;
+ }
+
+
+ # count close braces
+ #
+ if ($line =~ /}/) {
+ $brace_count--;
+
+ # leaving <plugin>_init() function...
+ if ($brace_count == 0) {
+ close(FILE);
+ next PLUGIN;
+ }
+
+ }
+
+
+ # throw away lines that are not exactly one "brace set" deep
+ #
+ if ($brace_count > 1) {
+ next;
+ }
+
+
+ # also not interested in lines that are not
+ # hook registration points
+ #
+ if ($line !~ /^\s*\$squirrelmail_plugin_hooks/i) {
+ next;
+ }
+
+
+ # if $line does not have an ending semicolon,
+ # we need to recursively read in subsequent
+ # lines until we find one
+ while ( $line !~ /;\s*$/ ) {
+ $line =~ s/[\n\r]\s*$//;
+ $line .= <FILE>;
+ }
+
+
+ $line =~ s/^\s+//;
+ $line =~ s/^\$//;
+ $var = $line;
+
+ $var =~ s/=/EQUALS/;
+ if ( $var =~ /^([a-z])/i ) {
+ @options = split ( /\s*EQUALS\s*/, $var );
+ $options[1] =~ s/[\n\r]//g;
+ $options[1] =~ s/[\'\"];\s*$//;
+ $options[1] =~ s/;$//;
+ $options[1] =~ s/^[\'\"]//;
+ # de-escape escaped strings
+ $options[1] =~ s/\\'/'/g;
+ $options[1] =~ s/\\\\/\\/g;
+
+ if ( $options[0] =~ /^squirrelmail_plugin_hooks\s*\[\s*['"]([a-z0-9 \/._*-]+)['"]\s*\]\s*\[\s*['"]([0-9a-z._-]+)['"]\s*\]/i ) {
+ $hook_name = $1;
+ $hooked_plugin_name = $2;
+ # Note: if we wanted to stop plugins from registering
+ # a *different* plugin on a hook, we could catch
+ # it here, however this has actually proven to be
+ # a useful *feature*
+ #if ($hooked_plugin_name ne $plugins[$ct]) {
+ # print "...plugin is tring to hook in under different name...\n";
+ #}
+
+#FIXME: do we want to count the number of hook registrations for each plugin and warn if a plugin doesn't have any?
+ # hook registration has been found!
+ if ($verbose) {
+ if ($hooked_plugin_name ne $plugins[$ct]) {
+ print " registering on hook \"" . $hook_name . "\" (as \"$hooked_plugin_name\" plugin)\n";
+ } else {
+ print " registering on hook \"" . $hook_name . "\"\n";
+ }
+ }
+ $line =~ s/ {2,}/ /g;
+ $line =~ s/=/\n =/;
+ print HOOKFILE "\$$line";
+
+ }
+
+ }
+
+ }
+ close(FILE);
+
+ } else {
+ print "\n";
+ print "WARNING:\n";
+ print "The file \"$setup_file\" was not found.\n";
+ print "The plugin \"" . $plugins[$ct];
+ print "\" will not be activated until you fix this.\n";
+ print "\nPress enter to continue";
+ $ctu = <STDIN>;
+ print "\n";
+ next;
+ }
+
+ }
+
+ print HOOKFILE "\n\n";
+ close(HOOKFILE);
+# if ($verbose) {
+ print "\nDone activating plugins; registration data saved in plugin_hooks.php\n\n";
+# }
+
+ } else {
+
+ print "\n";
+ print "WARNING:\n";
+ print "The file \"plugin_hooks.php\" was not able to be written to.\n";
+ print "No plugins will be activated until you fix this.\n";
+ print "\nPress enter to continue";
+ $ctu = <STDIN>;
+ print "\n";
+
+ }
+
+}
+