<?php
/* daCode http://www.dacode.org/
 * src/phplib/cache.php3
 * $Id: cache.php3,v 1.46 2001/07/22 22:04:17 barbier Exp $
 *
 * This Class will handle all cache system
 *
 * Depends: Config
*/

Class Cache {
	var $state,$mark_outdated,$topdir,$cachedir,$htmldir;

	Function Cache() {
//echo '<br>Cache';
		global $config;

		$this->db = LoadClass('Db');
		$this->session = LoadClass('Session');
		$this->utils = LoadClass('Utils');

		//   When an HTML file is locked up, wait x seconds
		//   for the new version being regenerated
		$this->lock_delay = 3;

		// State used in write_html()
		//   0: HTML page is written to file
		//   1: outdated, because some boxes were not up-to-date
		//   2: a .del file has been found
		$this->state = 0;

		$this->mark_outdated = $config->mark_outdated;
		$this->topdir = $config->topdir;
		$this->cachedir = $config->cachedir;
		if ($config->htmldir) {
			$this->htmldir = $config->topdir.$config->htmldir;
		} else {
			$this->htmldir = '';
		}
	}

	//   Return contents of cached box when it exists
	Function check_box($cache_time="60",$name,$options="") {
		$cache_file = str_replace('//','/',$this->cachedir.$name."/#");
		if (gettype($options) == "array") {
			reset($options);
			while (list ($key, $val) = each ($options)) {
				$cache_file .= $val.".";
			}
		} else {
			$cache_file .= $options.".";
		}

		if ($this->box_file_exists($cache_file)) {
			$filedate = filectime($cache_file);
			if ($filedate && time() < $filedate + $cache_time) {
				return @join ('', file($cache_file));
			}
		}
	}

	Function write_box($name,$options,$input) {
		global $config;
		if (empty($input) || empty($this->cachedir) || isset($config->nosave)) {
			return;
		}
		$cache_dir = $this->cachedir.$name;
		if (!$this->utils->createdir($cache_dir, 0755)) {
			return $input;
		}
		$cache_file = $cache_dir."/#";
		if (gettype($options) == "array") {
			reset($options);
			while (list ($key, $val) = each ($options)) {
				$cache_file .= $val.".";
			}
		} else {
			$cache_file .= $options.".";
		}
		$this->utils->write_file($cache_file, $input);
		@unlink($cache_file.'.lock');
	}

	//   Check whether file exists, with a lock mechanism to reduce
	//   SQL queries.
	Function box_file_exists($file) {
		if (!file_exists($file)) {
			return 0;
		}

		$ret = 0;
		if (file_exists($file.'.del') && file_exists($file.'.lock')) {
			$filedate = filectime($file.'.lock');
			if ($filedate && time() < $filedate + $this->lock_delay) {
				//   This box is being regenerated ;
				//   do not wait, but tell HTML files it
				//   will be outdated
				$this->state = max(1, $this->state);
				$ret = 1;
			} else {
				//   Lock file is outdated, regenerate it
				$ret = 0;
			}
		} elseif (file_exists($file.'.del')) {
			$ret = 0;
		} else {
			$ret = 1;
		}

		if (!$ret) {
			// we now ignore STOP from user
			ignore_user_abort(true);

			//   File will be regenerated, so remove .del flag
			//   and add a lock
			$this->tag_file($file.'.lock', 1);
			@unlink($file.'.del');
		}
		return $ret;
	}

	//   Return contents of HTML file if it exists
	Function check_html($name) {
		if (substr($name,-1) == '/') {
			return;
		}
		$cache_file = str_replace('//','/',$this->htmldir.$name);
		if ($this->html_file_exists($cache_file)) {
			if (substr($cache_file,-5) == '.html') {
				readfile($cache_file);
			} else {
				$topdir = $this->topdir;
				include $cache_file;
			}
			@unlink($cache_file.'.lock');
			exit;
		}
	}

	Function write_html($file,$text) {
		global $config;

		if (empty($text) || empty($this->htmldir) || isset($config->nosave)) {
			return;
		}
		if (substr($file,-5) != '.html') {
			//  Remove some boxes
			if (ereg('/', $file)) {
				$alttopdir = '.';
				$tmp = split('/', $file);
				$i = 1;
				while ($i < count($tmp)) {
					$alttopdir .= '/..';
					$i++;
				}
			} else {
				$alttopdir = '.';
			}
			$text = '<?php if (empty($topdir)) {'.
				'$topdir="'.$alttopdir.'";'.
				'include $topdir."/dacode.php3"; }'.
				'$user = LoadClass("User");'.
				'$message = LoadClass("Message");'.
				'$admin = LoadClass("Admin");'.
				'$board = LoadClass("Board");'.
				'?>' . $text;

			// Should be optimized...
			$texta = split("\n", $text);
			$text="";
			while($t=each($texta)) {
				$t[1] = ereg_replace('<!-- SIDEBOX login -->(.*)<!-- /SIDEBOX login -->',
					'<?php echo $user->loginbox(); ?>', $t[1]);
				$t[1] = ereg_replace('<!-- ADMIN -->(.*)<!-- /ADMIN -->',
					'<?php echo $admin->adminbox(); ?>', $t[1]);
				$t[1] = ereg_replace('<!-- MESSAGESHOW -->(.*)<!-- /MESSAGESHOW -->',
									'<?php echo $message->show_new_title(); ?>', $t[1]);
				$t[1] = ereg_replace('<!-- ADMINSCORE:([0-9]+):([0-9]+) -->.*<!-- /ADMINSCORE -->',
					'<?php echo $admin->comments_showscore(\\1,\\2); ?>',$t[1]);
				$t[1] = ereg_replace('<!-- ADMINEDIT:([0-9]+) -->.*<!-- /ADMINEDIT -->',
					'<?php echo $admin->news_showedit(\\1); ?>',$t[1]);
				$t[1] = ereg_replace('<!-- WEBCAMTITLE:([0-9]+) -->.*<!-- /WEBCAMTITLE -->',
					'<?php echo $admin->webcam_showtitle(\\1); ?>',$t[1]);
				$t[1] = eregi_replace('<!-- TIPTITLE:([0-9]+) -->.*<!-- /TIPTITLE -->',
					'<?php echo $admin->tips_showtitle(\\1); ?>',$t[1]);
				$t[1] = ereg_replace('<!-- SIDEBOX board -->.*<!-- /SIDEBOX board -->',
					'<?php echo $board->print_message(); ?>',$t[1]);

				$text .= $t[1]."\n";
			}

			$text = ereg_replace('<!-- SIDEBOX login -->(.*)<!-- /SIDEBOX login -->',
						'<?php echo $user->loginbox(); ?>', $text);
			$text = ereg_replace('<!-- MESSAGESHOW -->(.*)<!-- /MESSAGESHOW -->',
						'<?php echo $message->show_new_title(); ?>', $text);
			$text = ereg_replace('<!-- ADMIN -->(.*)<!-- /ADMIN -->',
						'<?php echo $admin->adminbox(); ?>', $text);
			
			$this->utils->write_file($file,$text);
		} elseif (!$this->session->checked) {
			$this->utils->write_file($file,$text);
		}
		@unlink($file.'.lock');
	}

	//   Check whether file exists, with a lock mechanism to reduce
	//   SQL queries
	Function html_file_exists($file) {
		$ret = 0;
		$delfile = (file_exists($file.'.del') || $this->state >= 2);
		if ($delfile && file_exists($file.'.lock')) {
			$filedate = filectime($file.'.lock');
			while (file_exists($file.'.lock') && $filedate &&
					time() < $filedate + $this->lock_delay) {
				sleep(1);
			}
			if (file_exists($file.'.lock')) {
				//   An outdated lock file exists
				$ret = 0;
			}
			$this->state = 2;
		} elseif ($delfile) {
			$this->state = 2;
			$ret = 0;
		} elseif (file_exists($file.'.lock') && file_exists($file)) {
			$ret = 1;
		} elseif (file_exists($file.'.lock')) {
			$filedate = filectime($file.'.lock');
			while (file_exists($file.'.lock') && $filedate &&
					time() < $filedate + $this->lock_delay) {
				sleep(1);
			}
			$ret = (file_exists($file) ? 1 : 0);
		} else {
			$ret = (file_exists($file) ? 1 : 0);
		}

		if (!$ret && (!$this->session->checked || substr($file,-5) != '.html')) {
			//   File is regenerated
			// we now ignore STOP from user
			ignore_user_abort(true);

			//   File will be regenerated, so remove .del flag
			//   and add a lock
			$this->tag_file($file.'.lock', 1);
			@unlink($file.'.del');
		}
		return $ret;
	}

	//   Delete cached files
	Function delete_files($dir,$location,$recurs,$rmdir,$rmtag,$pattern,$dirpattern) {
		$dir = ereg_replace('/$', '', $dir);
		if ($location) {
			if (!$this->htmldir) return;
			$fulldir = $this->htmldir . $dir;
		} else {
			if (!$this->cachedir) return;
			$fulldir = $this->cachedir . $dir;
		}
		if (!file_exists($fulldir) ||
			!is_dir($fulldir)) {
			return;
		}
		$handle=opendir($fulldir);
		while ($file = readdir($handle)) {
			if ($file == '.' || $file == '..') continue;
			if (!$rmtag && (substr($file, -4) == '.del' ||
					substr($file, -5) == '.lock')) {
				continue;
			}

			if (is_dir($fulldir.'/'.$file)) {
				if ($recurs && ereg($dirpattern, $file)) {
					$this->delete_files($dir.'/'.$file,$location,1,$rmdir,$rmtag,$pattern,$dirpattern);
					@rmdir($fulldir.'/'.$file);
				}
			} elseif (ereg($pattern, $file)) {
				/* Sometimes the file already disappeared...*/
				if (file_exists($fulldir.'/'.$file)) {
					unlink($fulldir.'/'.$file);
				}
			}
		}
		closedir($handle);
		if ($rmdir) {
			@rmdir($fulldir);
		}
	}

	//  Mnemonic shorthand
	Function delete_htmlfiles($dir,$recurs=0,$rmdir=0,$rmtag=0,$pattern=".*",$dirpattern="") {
		if (!$dirpattern) {
			$dirpattern = $pattern;
		}
		if ($this->mark_outdated) {
			$this->tag_delete_files($dir,1,$recurs,$pattern,$dirpattern);
		} else {
			$this->delete_files($dir,1,$recurs,$rmdir,$rmtag,$pattern,$dirpattern);
		}
	}

	//  Mnemonic shorthand
	Function delete_boxfiles($dir,$recurs=0,$rmdir=0,$rmtag=0,$pattern=".*") {
		if ($this->mark_outdated && !$rmtag) {
			$this->tag_delete_files($dir,0,$recurs,$pattern,".");
		} else {
			$this->delete_files($dir,0,$recurs,$rmdir,$rmtag,$pattern,".");
		}
	}

	//  Tag files for deletion
	Function tag_delete_files($dir,$location,$recurs,$pattern,$dirpattern) {
		$dir = ereg_replace('/$', '', $dir);
		if ($location) {
			if (!$this->htmldir) return;
			$fulldir = $this->htmldir . $dir;
		} else {
			if (!$this->cachedir) return;
			$fulldir = $this->cachedir . $dir;
		}
		if (!is_dir($fulldir))
			return;
		$handle=opendir($fulldir);
		while ($file = readdir($handle)) {
			if ($file == '.' || $file == '..') continue;
			if (substr($file, -4) == '.del' ||
					substr($file, -5) == '.lock') {
				continue;
			}

			if (is_dir($fulldir.'/'.$file)) {
				if ($recurs && ereg($dirpattern, $file)) {
					$this->tag_delete_files($dir.'/'.$file,$location,1,$pattern,$dirpattern);
				}
			} elseif (ereg($pattern, $file)) {
				$this->tag_file($fulldir.'/'.$file.'.del');
			}
		}
		closedir($handle);
	}

	Function delete_obsolete_news ($news_id) {
		global $config;
		if (empty($this->htmldir))
			return;

		$sqlc_q = "SELECT timestamp,section,topic FROM ".
			$config->tables['news'].",".
			$config->tables['sections'].",".
			$config->tables['topics'].
			" WHERE ".$config->tables['news'].".id='".addslashes($news_id).
			"' AND ".$config->tables['news'].".section_id=".$config->tables['sections'].".id".
			" AND ".$config->tables['news'].".topic_id=".$config->tables['topics'].".id";
		$ret = $this->db->query($sqlc_q);
		if (!$ret) {
			echo "<!-- SQL failed: ".$this->db->error() . " -->\n";
			echo "<!-- SQL Command: $sqlc_q -->\n";
			return -1;
		}
		$row = $this->db->fetch_array();
		ereg("^([0-9]{4})([0-9]{2})([0-9]{2})", $row[0], $regs);
		$this->delete_htmlfiles($regs[1].'/'.$regs[2].'/'.$regs[3], 0, 0, 0, "^".$news_id.'[,.]');
		$this->delete_htmlfiles('section/'.$row[1], 0, 0, 0, '^'.$news_id.'[,.]');
		$this->delete_htmlfiles('topic/'.$row[2], 0, 0, 0, '^'.$news_id.'[,.]');
		$this->delete_htmlfiles('.', 0, 0, 0, '^index[,.]');
		$this->delete_boxfiles('news_show', 0, 0, 0, '^#'.$news_id.'\.');
		$this->delete_boxfiles('news_index', 0, 0, 0, '^#'.$row[1].'\.');
		$this->delete_boxfiles('news_index', 0, 0, 0, '^#\.');
	}

	//   Creates a tag file
	Function tag_file ($file,$force="0") {
		if ($this->mark_outdated || $force) {
			$fp = @fopen($file, 'w');
			if ($fp) {
				fclose($fp);
			}
		}
	}

}

?>
