<?php
/////////////////////////////////////////
// バックアップに関する拡張で用いる関数群
//                 Since 2009/10/08(Thur)
//                 by T.Shirai
/////////////////////////////////////////

// 　制限は，
// 　（１）ファイル名／フォルダ名（正規表現可）
// 　（２）拡張子（正規表現は不可とする．信頼性と安全性に対してメリットが少ないため）
// 　（３）ファイルサイズ
// によって行う．（１），（２）はファイルサイズによる制限も同時指定可能（AND条件）．
// 　これらの制限はコースIDを指定して特定のコースにのみ適用することも可能．

// コースID＝1はフロントページ
// コースID＝0はＮＧ
// コースID＝-1で全コース（フロントページ含む）を対象とする

// ファイルサイズ＝0byteはＮＧ
// ファイルサイズ＝-1で，ファイルサイズは無関係とする（ファイル名／フォルダ名，拡張子指定の場合）
// ファイルサイズのみによる制限の場合，1byte未満の指定はＮＧ

// 【拡張子による制限】
// （５）拡張子による制限
function fs_add_backup_exception_extension_sub($extension, $filesize = -1, $courseid = -1, $moddatafolder = false)
{
    global $fsCFG;
    if (empty($extension))  return false;
    if (($filesize != -1) and ($filesize < 1)) return false;
    if ($courseid >= 1) {
        if (! fs_get_record("course", "id", $courseid)) return false;
    } else if ($courseid != -1) return false;
    $exception->extension = strtoupper($extension);
    if ($filesize >= 1) $exception->filesize = (int)($filesize * 1024 * 1024);
    if ($courseid >= 1) $exception->courseid = $courseid;
    $exception->moddatafolder = ($moddatafolder ? true : false);
    $exception->enable    = false;
    $exception->control = $fsCFG->backup_exception_control;
    $fsCFG->backup_exception[] = $exception;
    return true;
}

// 【ファイルサイズによる制限】
// （６）ファイルサイズのみによる制限
function fs_add_backup_exception_filesize_sub($filesize, $courseid, $moddatafolder = false)
{
    global $fsCFG;
    if (! is_int($filesize) or ($filesize < 1)) return false;
    if ($courseid >= 1) {
        if (! fs_get_record("course", "id", $courseid)) return false;
    } else if ($courseid != -1) return false;

    $exception->filesize       = (int)($filesize * 1024 * 1024);
    if ($courseid >= 1) $exception->courseid = $courseid;
    $exception->moddatafolder = ($moddatafolder ? true : false);
    $exception->enable = false;
    $exception->control = $fsCFG->backup_exception_control;
    $fsCFG->backup_exception[] = $exception;
    return true;
}

$fsCFG->modlist = array('all', 'assignment', 'chat', 'choice', 'data', 'forum', 'glossary', 'hotpot', 'journal', 'label', 'lams', 'lesson', 'quiz', 'resource', 'scorm', 'survey', 'wiki', 'workshop');

// ----------------------------------------------------------------------
// バックアップ制限のコントロール用宣言 (fsconfig_optoin.phpで指定する）
// ----------------------------------------------------------------------
// 以下の宣言はバックアップ制限条件と混在して記述する．宣言する位置が以降の制限条件の適用タイミングに影響する
// （１）以降のバックアップ制限条件は自動バックアップ時のみ有効

$fsCFG->backup_exception_control = 3;  // デフォルト値

function fs_add_backup_exception_automation_only()
{
    global $fsCFG;
    $fsCFG->backup_exception_control = 1;
}
// （２）以降のバックアップ制限条件は手動バックアップ時のみ有効
function fs_add_backup_exception_manual_only()
{
    global $fsCFG;
    $fsCFG->backup_exception_control = 2;
}
// （３）以降のバックアップ制限条件は自動／手動バックアップ時共に有効
function fs_add_backup_exception_auto_and_manual()
{
    global $fsCFG;
    $fsCFG->backup_exception_control = 3;
}

// ---------- ライブラリ ------------

// -----------------------------------------------------------
// ファイル名／フォルダ名による制限の本体（設定には使用しない）
// -----------------------------------------------------------
function fs_add_backup_exception_filename_and_ereg($filename, $filesize, $courseid, $moddatafolder, $dirflag, $eregflag)
{
    global $fsCFG;
    if (empty($filename)) return false;
    if (($filesize != -1) and ($filesize < 1)) return false;
    if ($courseid >= 1) {
        if (! fs_get_record("course", "id", $courseid)) return false;
    } else if ($courseid != -1) return false;
    $exception->filename = $filename;  // 正規表現対応なので大文字に変換しない
    if ($filesize >= 1) $exception->filesize = (int)($filesize * 1024 * 1024);
    if ($courseid >= 1) $exception->courseid = $courseid;
    $exception->moddatafolder = ($moddatafolder ? true : false);
    $exception->enable  = false;
    $exception->ereg    = $eregflag;
    $exception->isdir   = $dirflag;
    $exception->control = $fsCFG->backup_exception_control;
    $fsCFG->backup_exception[] = $exception;

    return true;
}

// 【ソース中での使用方法】
// 　まず，現在，適用しようとしているコースIDを取得しておく必要がある．
// 　次に，fs_exception_condition_settingup($courseid)にコースIDを引数として渡し，
// 各バックアップ制限条件がこのコースIDに対して有効か無効かを状態設定する．
// 強制的に全てのバックアップ制限条件のコースID指定を無視したい場合
// （たとえばデバッグやテスト時）には，コースIDとして -1 を指定する．
// 　次に，fs_set_backup_exception()を実行して，バックアップ制限条件を有効化する．
// 有効化された本機能を再び無効化するには，fs_reset_backup_exception()を実行する．
// これは，意図せずしてバックアップ制限機能が機能してしまうことを防ぐための仕組みである．
// （心配しすぎかも知れないが）
// 　与えられたフルパス名のファイルやフォルダがバックアップ制限条件を満たすか調べるには，
// fs_is_this_exceptional($fpath, $test = false)を用いる．$testをtrueとした場合は，
// $fpathで与えられたファイルが実際に存在していなくても，バックアップ制限条件を満たすか
// チェックしてくれる．ファイルサイズやファイル／フォルダの有無のチェックは行わない．
// もし，バックアップ制限条件に合致しないならばfalseがリターンされ，合致する場合は
// 該当するバックアップ制限条件の番号をリターンする（１以上）．
// 　バックアップ制限付きで単一のファイルをコピーするには，fs_copy_exception($from, $to)を
// 用いる．

// ----------------------------------------------------------------
// バックアップの拡張の有効／無効のリアルタイムな切り替えと状態判断
// ----------------------------------------------------------------
// backup_copy_file()関数でファイルをコピーする際にバックアップ制限機能を無効とする
function fs_reset_backup_exception()
{
    global $fsCFG;
    $fsCFG->enable_backup_exception = false;
}
// backup_copy_file()関数でファイルをコピーする際にバックアップ制限機能を有効とする
function fs_set_backup_exception()
{
    global $fsCFG;
    $fsCFG->enable_backup_exception = true;
}
// バックアップ制限機能が有効か，無効か（機能自体の有効／無効も含めて）チェックする
function fs_is_enable_backup_exception()
{
    global $fsCFG;
    // 機能有効化スイッチが無効
    if (! fs_function_enable('FS_ENABLE_BACKUP_EXCEPTION')) return false;
    // バックアップ制限条件が未設定
    if (empty($fsCFG->backup_exception)) return false;
    return $fsCFG->enable_backup_exception;
}

// ----------------------------------------
// バックアップ制限機能の利用のための下準備
// ----------------------------------------

// 末端のbackup_copy_file()ではコースIDが分からない．（パスから分からなくはないが）
// 上位の関数で各条件が有効か無効かを判断して $exception->enable をリアルタイムに切り替える
// $courseid=-1の時は強制的に全てのバックアップ制限条件を有効にする（デバッグ用）
function fs_exception_condition_settingup($courseid)
{
    global $fsCFG;
    // バックアップ制限条件が指定されていない
    if (empty($fsCFG->backup_exception))        return false;  // true?
    // 指定されたコースIDが不正
    $courseid = (int)$courseid;
    if (empty($courseid) or ($courseid < 1 and $courseid != -1)) return false;
    // 指定されたコースIDに対して指定されたバックアップ制限条件が有効か無効化かを個別に設定
    foreach ($fsCFG->backup_exception as $key => $exception) {
        $exception->enable = false;
        if (empty($exception->courseid) or ($courseid == -1)) $exception->enable = true;
          else if ($exception->courseid == $courseid) $exception->enable = true;
        $fsCFG->backup_exception[$key] = $exception;
    }
    return true;
}

// ------------------------------------------------
// 実際のコピーを行うcopy()関数をオーバーライドする
// ------------------------------------------------

function is_this_filename_exceptional_sub($path, $exception)
{
    if ($exception->ereg) {
        // 正規表現の場合
        mb_regex_encoding("UTF-8");
        if (mb_eregi($exception->filename, $path, $reg)) {
            if (strlen($reg[0]) == strlen($path)) return true;
            return false;
        }
        return false;
    } else {
        // 単純な指定の場合
        if (strcasecmp($exception->filename, $path) != 0) return false;
        return true;
    }
}



// ファイル名の条件判定部
// パス指定とは，$CFG->dataroot/コースIDを基点とする相対パスによるファイル名／フォルダ名の指定
// 単体指定とは，単にファイル名／フォルダ名のみによる指定
// $testがtrueの場合はファイルの存在を確認しない．したがってファイルサイズも確認しない
function is_this_filename_exceptional($fpath, $exception, $test = false)
{
    global $CFG;

    // フルパスから余計なパス（$CFG->dataroot/コースID）を取り除く（頭に/は残らない）
    if (!$test) {
        $dir  = substr($fpath, 0, strlen($CFG->dataroot) + 1);
        $path = substr($fpath, strlen($CFG->dataroot) + 1);
        mb_eregi('^[0-9]+/', $path, $reg);
        $dir .= $reg[0];
        $path = substr($path, strlen($reg[0]));
    } else {
        if (substr($fpath, 0, 1) == '/') $path = substr($fpath, 1); else $path = $fpath;
    }
    // パスを分解
    $patharray = explode('/', $path);

    // パス指定か単体指定か
    $isrelpath = (substr($exception->filename, 0, 1) == '/') ? true : false;

    // バックアップ制限条件がフォルダ名の指定かファイル名の指定か
    if ($exception->isdir) {
        // フォルダ名の指定（サブフォルダに含まれるかどうかもチェックする必要がある）
        // 頭から調べて一致するか
        $path = '';
        $flag = false;
        foreach ($patharray as $part) {
            if (strlen($part) === 0) continue;
            $path .= '/'.$part;
            if (is_this_filename_exceptional_sub(($isrelpath ? $path : $part), $exception)) {
                $flag = true;
                break;
            }
        }
        if (!$flag) return false;
        if (!$test and !fs_is_dir($dir.$path)) return false;
    } else {
        // ファイル名の指定（合致するかどうかだけ調べれば良い）
        if ($isrelpath) {
            // 相対パスが条件に一致するか
            if (!is_this_filename_exceptional_sub('/'.$path, $exception)) return false;
        } else {
            // ベースネームが一致するか
            $path_parts = fs_pathinfo($path);
            if (!is_this_filename_exceptional_sub($path_parts['basename'], $exception)) return false;
        }
        if (!$test and !fs_is_file($fpath)) return false;
    }

    // ファイルサイズのチェック
    if ($exception->isdir) return true;  // ディレクトリ名のチェックの場合はサイズに無関係
    if (!$test and !empty($exception->filesize)) {
        if ($filesize <= $exception->filesize) false;
    }
    return true;
}

// 拡張子の条件判定部
function is_this_extension_exceptional($fpath, $exception, $test = false)
{
    $path_parts = fs_pathinfo($fpath);
    if (!$test and fs_is_dir($fpath)) return false;
    if (strtoupper($path_parts['extension']) != $exception->extension) return false;
    if (!$test and !empty($exception->filesize)) {
        if (fs_filesize($fpath) <= $exception->filesize) return false;
    }
    return true;
}

// ファイルサイズの条件判定部
function is_this_filesize_exceptional($fpath, $exception, $test = false)
{
    if ($test) return false;
    if (fs_is_dir($fpath)) return false;
    if ($filesize <= $exception->filesize) false;
    return true;
}

// このファイル（あるいはフォルダ）はバックアップ制限条件に合致するか？
// バックアップしない場合はtrueを返す
// $testがtrueの場合はファイル名／フォルダ名と拡張子のチェック（実際にファイルの存在は確認しない）のみ
// を行う．ファイルサイズのチェックも行わない．FS_ENABLE_BACKUP_EXCEPTIONなどの制限もパスする．
function fs_is_this_exceptional($fpath, $test = false)
{
    global $fsCFG, $CFG;

    // 機能が有効かつバックアップ制限コピーが機能しているか？
    if (!$test and !fs_is_enable_backup_exception()) return false;
    // 前処理
    $thisismoddatafolder = false;
    if (!$test) {
        if (fs_is_file($fpath)) $filesize = fs_filesize($fpath);
          else if (!fs_is_dir($fpath)) return true;  // 異常？
        // backupdataは除外，moddataは例外あり
        $path = substr($fpath, strlen($CFG->dataroot) + 1);
        mb_eregi('^[0-9]+/', $path, $reg);
        $path = substr($path, strlen($reg[0]));
        $patharray = explode('/', $path);
        if ($patharray[0] == 'backupdata') return false;
        if ($patharray[0] == 'moddata') $thisismoddatafolder = true;
    }
    $no = 0;
    foreach ($fsCFG->backup_exception as $exception) {
        if ($thisismoddatafolder and !$exception->moddatafolder) continue;
        $no++;
        // どれか一つでも条件に合致したらファイルはコピーしない
        if ($exception->enable) {
            // メインのチェックルーチン
            if (!empty($exception->filename)) {
                // ファイル名／フォルダ名による制限
                if (is_this_filename_exceptional($fpath, $exception, $test)) return $no;
                  else continue;
            } else if (!empty($exception->extension)) {
                // 拡張子による制限
                if (is_this_extension_exceptional($fpath, $exception, $test)) return $no;
                  else continue;
            } else {
                // サイズのみによる制限
                if (is_this_filesize_exceptional($fpath, $exception, $test)) return $no;
                  else continue;
            }
        }
    }
    return false;
}

define('BACKUP_EXCEPTED', 2);

// $fromで指定されたファイルはコピーして良いならばコピーする
// フォルダのコピー可否はメインのルーチンでcontinueするはずだが...
// コースIDのチェックはもっと上位の関数で行われているという前提
function fs_copy_exception($from, $to)
{
    global $fsCFG;

//  if (! fs_file_exists($from)) return false;
    if (!fs_is_enable_backup_exception()) return fs_copy($from, $to);
    if (! fs_is_this_exceptional($from)) return fs_copy($from, $to);
    return BACKUP_EXCEPTED;
}

?>