<?php
    /****
    _________________________________________________________________________________________________________________________
    Function :
    Delete old orphaned files.
    Walks directories recursively. For each file found to be older than <threshold> it checks there is not a WU in the database. If there is a WU it moves on with no action. If there is not a matching WU it deletes the file.
    Does both download and upload directories.
    ________________________________________________________________________________________________________________________
    Install:
    - Place the script in your /project home directory/html/ops directory
    ________________________________________________________________________________________________________________________
    Inputs:
    - None
    _________________________________________________________________________________________________________________________
    !!! IMPORTANT !!! Pre run changes: 
    - Set the mode variable to "scan" for a test run or "DELETE" for the real thing
    - Set $lookback threshold value you wish. Its midnight today less 'n' days as shown below. Typically 30-60 days.
    - Your project's download and upload paths are extracted from the project's config.xml file. Make sure they are set!
    - Your project's dbms host, db name, db user name and db password are extracted from you project config.xml file. Make sure they are set!
    - Look for the if test that checks for windows and linux apps. This test protects files that are present but are not related to work units.  Example the project apps. Extend this test to include anything more needed by you. I include *htm* here too as I have a html file in there or a link from time to time.
    _________________________________________________________________________________________________________________________
    Outputs are self explanatory:
    - Generally intended to give a success or fail indication on dbms connect followed by a list of files deleted or scanned.
    _________________________________________________________________________________________________________________________
    Run instructions:
    - Run using php. i.e. "php delete_old_download_files.php".
    - Run from your project home directory/html/ops.

    _________________________________________________________________________________________________________________________
    Known:
    - The script triggers a NOTICE from PHP as it cannot find the PROJECT variable that is referenced in the BOINC include file called : translation.inc.
    - The script is designed for projects that have ul/dl directories on the same server on which the script runs. The dbms can be somehwere else.
    - Links are ignored on purpose - only real files are processed.
    _________________________________________________________________________________________________________________________
    Notes:
    - PHP 5 Dev and test. FC6.
    - A regex of "((_[0-9])|(_[0-9][0-9])){2}$" is used to detect output files. If you have different requirments to this you will need to craft your own suitable regex for inclusion in the "file_delete" function.
    _________________________________________________________________
    Author: Ian Tighe aka Tigher. 15 April 2007. In a rush...again!
    Copyright: Project Neuron 2007
    _________________________________________________________________________________________________________________________
    ****/
    require_once '../project/project.inc';
    require_once '../inc/util.inc';
    /*
    Exit codes from this script: Negative codes are errors. 0 is all went OK.
    */
    define("X_READ_CONFIG_FILE", -1);
    define("CONFIG_FILE_ITEMS_MISSING", -2);
    define("INVALID_MODE", -3);
    define("NON_EXISTENT_FILE", -4);
    define("UPLOAD_DIR_NON_EXISTENT", -5);
    define("DOWNLOAD_DIR_NON_EXISTENT", -6);
    define("DOWNLOAD_DIR_CANNOT_READ", -7);
    define("UPLOAD_DIR_CANNOT_READ", -8);
    define("DOWNLOAD_DIR_CANNOT_DELETE",-9);
    define("UPLOAD_DIR_CANNOT_DELETE",-10);
    define("DOWNLOAD_DIR_CANNOT_CREATE",-11);
    define("UPLOAD_DIR_CANNOT_CREATE",-12);

    define("OK", 0);
    /////////////////////////////////////////////////////////
    // YOU MUST CHANGE THE LINES BELOW FOR A REAL DELETE RUN USING A REAL LOOKBACK PERIOD OF YOUR CHOICE.
    //
    $mode = "DELETE";    //DELETE means delete files. scan means look and do NOT delete -  a dry run as it were
    $lookback = 30;      //days
    $port = "3306";      //mysql port
    //
    //
    /////////////////////////////////////////////////////////
    if (!($mode == "scan") && !($mode == "DELETE"))
    {
        print "The mode ($mode) you have set is not known. It must be either \"scan\" or \"DELETE\"\n";
        exit ( INVALID_MODE ); //unknown mode of running
    }
    print "Mode = ".($mode=="scan" ? "Scan" : "Delete")."\n";
    print "Looking back $lookback days and older\n";

    $config = get_config();
    if($config != null)
    {
        $download_dir = parse_config($config, "<download_dir>");
        $upload_dir = parse_config($config, "<upload_dir>");
        $dbms_user = parse_config($config, "<db_user>");
        $dbms_password = parse_config($config, "<db_passwd>");
        $db_name = parse_config($config, "<db_name>");
        $db_host = parse_config($config, "<db_host>");
        $host = parse_config($config, "<host>");
    }
    else
    {
        print "The project's config.xml could not be read!";
        exit ( X_READ_CONFIG_FILE ); //config/xml could not be read
    }
    /*
    Have we got all the config.xml tag data we need? Check all of them for presence at this point
    */
    $errors = 0;
    if ($download_dir == "" )
    {
        print "<download_dir> config.xml data item could not be found";
        $errors++;
    }
    if ($upload_dir == "")
    {
        print "<upload_dir> config.xml data item could not be found";
        $errors++;
    }
    if ($dbms_user == "")
    {
        print "<db_user> config.xml data item could not be found";
        $errors++;
    }
    if ($dbms_password == "")
    {
        print "<db_passwd> config.xml data item could not be found";
        $errors++;
    }
    if ($db_name == "")
    {
        print "<db_name> config.xml data item could not be found";
        $errors++;
    }
    if ($db_host == "")
    {
        print "<db_host> config.xml data item could not be found";
        $errors++;
    }
    if ($host == "")
    {
        print "<host> config.xml data item could not be found";
        $errors++;
    }
    if ($errors > 0 )
    {
        print "Errors have been found in your config.xml parameters/settings. All the items shown above must be included for this script to work correctly.\n";
        exit ( CONFIG_FILE_ITEMS_MISSING ); //needed config.xml data item missing
    }
    /*
        Establish that the ul/dl directories exists, can be read, that a file can be created in it and then deleted
    */
    if(!file_exists($download_dir))
    {
        print "The download directory name provided does not exist\n";
        exit (DOWNLOAD_DIR_NON_EXISTENT);   //the directory does not exist.
    }
    else
    {
        $handle = opendir($download_dir);
        if (!$dir = readdir($handle))
        {
            print "The download directory ($download_dir) exists but cannot be read\n";
            exit (DOWNLOAD_DIR_CANNOT_READ);   //the directory does not exist.
        }
        else
        {
            $res = touch($download_dir."/delete_test_file.xyz");
            if (!$res)
            {
                print "Unable to create a file in $download_dir. Assumption that this script will not be able to delete\n";
                exit (DOWNLOAD_DIR_CANNOT_CREATE);
            }
            else
            {
                $res = unlink($download_dir."/delete_test_file.xyz");
                if(!$res)
                {
                print "Unable to delete a file in $download_dir. Assumption that this script will not be able to run correctly\n";
                exit (DOWNLOAD_DIR_CANNOT_DELETE);
                }
            }
        }
    }
    if(!file_exists($upload_dir))
    {
        print "The upload directory name provided does not exist\n";
        exit (UPLOAD_DIR_NON_EXISTENT);   //the directory does not exist.
    }
    else
    {
        $handle = opendir($upload_dir);
        if ($dir = readdir($handle) == false)
        {
            print "The upload directory ($upload_dir) exists but cannot be read\n";
            exit (UPLOAD_DIR_CANNOT_READ);   //the directory does not exist.
        }
        else
        {
            $res = touch($upload_dir."/delete_test_file.xyz");
            if (!$res)
            {
                print "Unable to create a file in $upload_dir. Assumption that this script will not be able to delete files\n";
                exit (UPLOAD_DIR_CANNOT_CREATE);
            }
            else
            {
                $res = unlink($upload_dir."/delete_test_file.xyz");
                if(!$res)
                {
                    print "Unable to delete a file in $upload_dir. Assumption that this script will not be able to run correctly\n";
                    exit (UPLOAD_DIR_CANNOT_DELETE);
                }
            }
        }
    }
    print "Parameters checked OK. Commencing run....\n";
    /*
        End of checking params
    */
    /*
        Connect to database
    */
    $con = @mysql_connect($db_host.":".$port,$dbms_user, $dbms_password) or die("Could not connect to data server - sorry : \n".mysql_error());
    @mysql_select_db($db_name) or die("Could not open $db_name database\n");


    $threshold = mktime(0, 0, 0, date("m"), date("d")-$lookback,   date("Y"));
    print  "Delete files older than :" . $threshold ." i.e. ";
    $daysAgo = (time()- $threshold)/86400;
    print date("r", $threshold)." (about ". $daysAgo." days ago)\n\n";
    /*
        Do delete files
    */
    //downloads
    $count = file_delete($download_dir, $threshold);
    if ($count == -1)
    {
        print "The download directory provided (from config.xml) does not exist";
    }
    else
    {
        print "Affected Download files = ". $count."\n";
    }
    //uploads
    $count = file_delete($upload_dir, $threshold);
    if ($count == -1)
    {
        print "The upload directory provconided (from config.xml) does not exist";
    }
    else
    {
        print "Affected Upload files = ". $count."\n";
    }
    mysql_close($con);

    exit( OK );
    //done

    function file_delete($dir, $threshold)
    {
    global $con;
    global $mode;
    $handle = opendir($dir);
    $count = 0;

    while ( ($file = readdir($handle)) !==  false)
    {
        if ( is_dir($dir."/".$file) && $file !== '..' && $file !== '.' )
        {
                $count += file_delete($dir."/".$file,  $threshold);
        }
        else
        {
            if (!is_dir($dir."/".$file) && !is_link($dir."/".$file))
            {
                if (($filetime = filemtime($dir."/".$file)) < $threshold)
                {
                    if( !strpos($file, "windows")  && !strpos($file, "linux") && !strpos($file, "htm")) //Extend this
                    {
                        //check if there ia a WU in the dbms. If not delete the file.
                        // if its an output file get the input file name element
                                $check_file = split("((_[0-9])|(_[0-9][0-9])){2}$", $file);
                        if (isset($check_file[1]))
                        {
                            $wu_name = $check_file[0];
                        }
                        else
                        {
                            $wu_name = $file;
                        }

                        $SQL = "select id from workunit where name = '$wu_name' ";
                        $result = mysql_query($SQL, $con);
                        $num_rows = mysql_num_rows($result);
                        if ($num_rows == 0)	//not found in dbms
                        {
                            if ($mode == "scan")
                            {
                                print "Scan mode : ".$dir."/".$file." Dated : ".date("r", filemtime($dir."/".$file))." has met the delete criteria\n";
                            }
                            elseif ($mode == "DELETE")
                            {
                                print "Delete mode : ".$dir."/".$file." Dated : ".date("r", filemtime($dir."/".$file)) ." is being deleted.....";
                                unlink($dir."/".$file);
                                print "Done\n";
                            }
                            $count++;
                        }
                        mysql_free_result($result);
                    }
                    else
                    {
                        //print "Application found - NO delete of ". $file."\n"; //do nothing here
                    }
                }
            }
        }
    }
    closedir($handle);
    return $count;
    }
    ?>