1<?php 2/** 3 * file.class.php 4 * 5 * Copyright � 2006 Stephane Gully <stephane.gully@gmail.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the 19 * Free Software Foundation, 51 Franklin St, Fifth Floor, 20 * Boston, MA 02110-1301 USA 21 */ 22 23require_once dirname(__FILE__)."/../pfccontainerinterface.class.php"; 24 25/** 26 * pfcContainer_File is a concret container which stock data into files 27 * 28 * @author Stephane Gully <stephane.gully@gmail.com> 29 */ 30class pfcContainer_File extends pfcContainerInterface 31{ 32 var $_users = array("nickid" => array(), 33 "timestamp" => array()); 34 var $_meta = array(); 35 36 function pfcContainer_File() 37 { 38 pfcContainerInterface::pfcContainerInterface(); 39 } 40 41 function loadPaths(&$c) 42 { 43 if (!isset($c->container_cfg_chat_dir) || $c->container_cfg_chat_dir == '') 44 $c->container_cfg_chat_dir = $c->data_private_path."/chat"; 45 if (!isset($c->container_cfg_server_dir) || $c->container_cfg_server_dir == '') 46 $c->container_cfg_server_dir = $c->container_cfg_chat_dir."/s_".$c->serverid; 47 } 48 49 function getDefaultConfig() 50 { 51 $cfg = pfcContainerInterface::getDefaultConfig(); 52 $cfg["chat_dir"] = ''; // will be generated from the other parameters into the init step 53 $cfg["server_dir"] = ''; // will be generated from the other parameters into the init step 54 return $cfg; 55 } 56 57 function init(&$c) 58 { 59 $errors = pfcContainerInterface::init($c); 60 61 // generate the container parameters from other config parameters 62 $this->loadPaths($c); 63 64 $errors = array_merge($errors, @test_writable_dir($c->container_cfg_chat_dir, "container_cfg_chat_dir")); 65 $errors = array_merge($errors, @test_writable_dir($c->container_cfg_server_dir, "container_cfg_chat_dir/serverid")); 66 67 // test the filemtime php function because it doesn't work on special filesystem 68 // example : NFS, VZFS 69 $filename = $c->data_private_path.'/filemtime.test'; 70 $timetowait = 2; 71 if (is_writable(dirname($filename))) 72 { 73 file_put_contents($filename,'some-data1-'.time()); 74 clearstatcache(); 75 $time1 = filemtime($filename); 76 sleep($timetowait); 77 file_put_contents($filename,'some-data2-'.time()); 78 clearstatcache(); 79 $time2 = filemtime($filename); 80 unlink($filename); 81 if ($time2-$time1 != $timetowait) 82 $errors[] = "filemtime php fuction is not usable on your filesystem. Please do not use the 'file' container (try the 'mysql' container) or swith to another filesystem type."; 83 } 84 85 // test the LOCK_EX feature because it doesn't work on special filsystem like NFS 86 $filename = $c->data_private_path.'/filemtime.test'; 87 if (is_writable(dirname($filename))) 88 { 89 $data1 = time(); 90 file_put_contents($filename, $data1, LOCK_EX); 91 $data2 = file_get_contents($filename); 92 if ($data1 != $data2) 93 $errors[] = "LOCK_EX feature is not usable on your filesystem. Please do not use the 'file' container (try the 'mysql' container) or swith to another filesystem type."; 94 } 95 96 return $errors; 97 } 98 99 function setMeta($group, $subgroup, $leaf, $leafvalue = NULL) 100 { 101 $c =& pfcGlobalConfig::Instance(); 102 103 // create directories 104 $dir_base = $c->container_cfg_server_dir; 105 $dir = $dir_base.'/'.$group.'/'.$subgroup; 106 if (!is_dir($dir)) mkdir_r($dir); 107 108 // create or replace metadata file 109 $leaffilename = $dir."/".$leaf; 110 $leafexists = file_exists($leaffilename); 111 if ($leafvalue == NULL) 112 { 113 @unlink($leaffilename); 114 touch($leaffilename); 115 } 116 else 117 { 118 file_put_contents($leaffilename, $leafvalue, LOCK_EX); 119 } 120 121 // store the value in the memory cache 122 //@todo 123 // $this->_meta[$enc_type][$enc_subtype][$enc_key] = $value; 124 125 if ($leafexists) 126 return 1; // value overwritten 127 else 128 return 0; // value created 129 } 130 131 function getMeta($group, $subgroup = null, $leaf = null, $withleafvalue = false) 132 { 133 $c =& pfcGlobalConfig::Instance(); 134 135 // read data from metadata file 136 $ret = array(); 137 $ret["timestamp"] = array(); 138 $ret["value"] = array(); 139 $dir_base = $c->container_cfg_server_dir; 140 141 $dir = $dir_base.'/'.$group; 142 if ($subgroup == NULL) 143 { 144 if (is_dir($dir)) 145 { 146 $dh = opendir($dir); 147 while (false !== ($file = readdir($dh))) 148 { 149 if ($file == "." || $file == "..") continue; // skip . and .. generic files 150 $ret["timestamp"][] = filemtime($dir.'/'.$file); 151 $ret["value"][] = $file; 152 } 153 closedir($dh); 154 } 155 return $ret; 156 } 157 158 $dir .= '/'.$subgroup; 159 160 if ($leaf == NULL) 161 { 162 if (is_dir($dir)) 163 { 164 $dh = opendir($dir); 165 while (false !== ($file = readdir($dh))) 166 { 167 if ($file == "." || $file == "..") continue; // skip . and .. generic files 168 $ret["timestamp"][] = filemtime($dir.'/'.$file); 169 $ret["value"][] = $file; 170 } 171 closedir($dh); 172 } 173 174 return $ret; 175 } 176 177 $leaffilename = $dir."/".$leaf; 178 179 if (!file_exists($leaffilename)) return $ret; 180 if ($withleafvalue) 181 $ret["value"][] = file_get_contents_flock($leaffilename); 182 else 183 $ret["value"][] = NULL; 184 $ret["timestamp"][] = filemtime($leaffilename); 185 186 return $ret; 187 } 188 189 function incMeta($group, $subgroup, $leaf) 190 { 191 $c =& pfcGlobalConfig::Instance(); 192 193 // create directories 194 $dir_base = $c->container_cfg_server_dir; 195 $dir = $dir_base.'/'.$group.'/'.$subgroup; 196 if (!is_dir($dir)) mkdir_r($dir); 197 198 // create or replace metadata file 199 $leaffilename = $dir."/".$leaf; 200 201 // create return array 202 $ret = array(); 203 $ret["timestamp"] = array(); 204 $ret["value"] = array(); 205 206 // read and increment data from metadata file 207 clearstatcache(); 208 if (file_exists($leaffilename)) 209 { 210 $fh = fopen($leaffilename, 'r+'); 211 for($i = 0; $i < 10; $i++) // Try 10 times until an exclusive lock can be obtained 212 { 213 if (flock($fh, LOCK_EX)) 214 { 215 $leafvalue = chop(fread($fh, filesize($leaffilename))); 216 $leafvalue++; 217 rewind($fh); 218 fwrite($fh, $leafvalue); 219 fflush($fh); 220 ftruncate($fh, ftell($fh)); 221 flock($fh, LOCK_UN); 222 break; 223 } 224 // If flock is working properly, this will never be reached 225 $delay = rand(0, pow(2, ($i+1)) - 1) * 5000; // Exponential backoff 226 usleep($delay); 227 } 228 fclose($fh); 229 } 230 else 231 { 232 $leafvalue="1"; 233 file_put_contents($leaffilename, $leafvalue, LOCK_EX); 234 } 235 236 $ret["value"][] = $leafvalue; 237 $ret["timestamp"][] = filemtime($leaffilename); 238 239 return $ret; 240 } 241 242 function rmMeta($group, $subgroup = null, $leaf = null) 243 { 244 $c =& pfcGlobalConfig::Instance(); 245 246 $dir = $c->container_cfg_server_dir; 247 248 if ($group == NULL) 249 { 250 rm_r($dir); 251 return true; 252 } 253 254 $dir .= '/'.$group; 255 256 if ($subgroup == NULL) 257 { 258 rm_r($dir); 259 return true; 260 } 261 262 $dir .= '/'.$subgroup; 263 264 if ($leaf == NULL) 265 { 266 rm_r($dir); 267 return true; 268 } 269 270 $leaffilename = $dir.'/'.$leaf; 271 if (!file_exists($leaffilename)) return false; 272 unlink($leaffilename); 273 274 // check that the directory is empty or not 275 // remove it if it doesn't contains anything 276 $dh = opendir($dir); 277 readdir($dh); readdir($dh); // skip . and .. directories 278 $isnotempty = readdir($dh); 279 closedir($dh); 280 if ($isnotempty === false) rmdir($dir); 281 282 return true; 283 } 284 285 /** 286 * Used to encode UTF8 strings to ASCII filenames 287 */ 288 function encode($str) 289 { 290 return urlencode($str); 291 } 292 293 /** 294 * Used to decode ASCII filenames to UTF8 strings 295 */ 296 function decode($str) 297 { 298 return urldecode($str); 299 } 300} 301?> 302