abook_local_file.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. <?php
  2. /**
  3. ** abook_local_file.php
  4. **
  5. ** Backend for addressbook as a pipe separated file
  6. **
  7. ** An array with the following elements must be passed to
  8. ** the class constructor (elements marked ? are optional):
  9. **
  10. ** filename => path to addressbook file
  11. ** ? create => if true: file is created if it does not exist.
  12. ** ? umask => umask set before opening file.
  13. **
  14. ** NOTE. This class should not be used directly. Use the
  15. ** "AddressBook" class instead.
  16. **/
  17. class abook_local_file extends addressbook_backend {
  18. var $btype = "local";
  19. var $bname = "local_file";
  20. var $filename = "";
  21. var $filehandle = 0;
  22. var $create = false;
  23. var $umask;
  24. // ========================== Private =======================
  25. // Constructor
  26. function abook_local_file($param) {
  27. $this->sname = _("Personal address book");
  28. $this->umask = Umask();
  29. if(is_array($param)) {
  30. if(empty($param["filename"]))
  31. return $this->set_error("Invalid parameters");
  32. if(!is_string($param["filename"]))
  33. return $this->set_error($param["filename"] . ": ".
  34. _("Not a file name"));
  35. $this->filename = $param["filename"];
  36. if($param["create"])
  37. $this->create = true;
  38. if(isset($param["umask"]))
  39. $this->umask = $param["umask"];
  40. if(!empty($param["name"]))
  41. $this->sname = $param["name"];
  42. $this->open(true);
  43. } else {
  44. $this->set_error("Invalid argument to constructor");
  45. }
  46. }
  47. // Open the addressbook file and store the file pointer.
  48. // Use $file as the file to open, or the class' own
  49. // filename property. If $param is empty and file is
  50. // open, do nothing.
  51. function open($new = false) {
  52. $this->error = "";
  53. $file = $this->filename;
  54. $create = $this->create;
  55. // Return true is file is open and $new is unset
  56. if($this->filehandle && !$new)
  57. return true;
  58. // Check that new file exitsts
  59. if((!(file_exists($file) && is_readable($file))) && !$create)
  60. return $this->set_error("$file: " .
  61. _("No such file or directory"));
  62. // Close old file, if any
  63. if($this->filehandle) $this->close();
  64. // Open file. First try to open for reading and writing,
  65. // but fall back to read only.
  66. umask($this->umask);
  67. $fh = @fopen($file, "a+");
  68. if($fh) {
  69. $this->filehandle = &$fh;
  70. $this->filename = $file;
  71. $this->writeable = true;
  72. } else {
  73. $fh = @fopen($file, "r");
  74. if($fh) {
  75. $this->filehandle = &$fh;
  76. $this->filename = $file;
  77. $this->writeable = false;
  78. } else {
  79. return $this->set_error("$file: "._("Open failed"));
  80. }
  81. }
  82. return true;
  83. }
  84. // Close the file and forget the filehandle
  85. function close() {
  86. @fclose($this->filehandle);
  87. $this->filehandle = 0;
  88. $this->filename = "";
  89. $this->writable = false;
  90. }
  91. // Lock the datafile - try 20 times in 5 seconds
  92. function lock() {
  93. for($i = 0 ; $i < 20 ; $i++) {
  94. if(flock($this->filehandle, 2 + 4))
  95. return true;
  96. else
  97. usleep(250000);
  98. }
  99. return false;
  100. }
  101. // Lock the datafile
  102. function unlock() {
  103. return flock($this->filehandle, 3);
  104. }
  105. // Overwrite the file with data from $rows
  106. // NOTE! Previous locks are broken by this function
  107. function overwrite($rows) {
  108. $newfh = @fopen($this->filename, "w");
  109. if(!$newfh)
  110. return $this->set_error("$file: "._("Open failed"));
  111. for($i = 0 ; $i < sizeof($rows) ; $i++) {
  112. if(is_array($rows[$i]))
  113. fwrite($newfh, join("|", $rows[$i])."\n");
  114. }
  115. fclose($newfh);
  116. $this->unlock();
  117. $this->open(true);
  118. return true;
  119. }
  120. // ========================== Public ========================
  121. // Search the file
  122. function search($expr) {
  123. // To be replaced by advanded search expression parsing
  124. if(is_array($expr)) return;
  125. // Make regexp from glob'ed expression
  126. $expr = ereg_replace("\?", ".", $expr);
  127. $expr = ereg_replace("\*", ".*", $expr);
  128. $res = array();
  129. if(!$this->open())
  130. return false;
  131. @rewind($this->filehandle);
  132. while ($row = @fgetcsv($this->filehandle, 2048, "|")) {
  133. $line = join(" ", $row);
  134. if(eregi($expr, $line)) {
  135. array_push($res, array("nickname" => $row[0],
  136. "name" => $row[1] . " " . $row[2],
  137. "firstname" => $row[1],
  138. "lastname" => $row[2],
  139. "email" => $row[3],
  140. "label" => $row[4],
  141. "backend" => $this->bnum,
  142. "source" => &$this->sname));
  143. }
  144. }
  145. return $res;
  146. }
  147. // Lookup alias
  148. function lookup($alias) {
  149. if(empty($alias))
  150. return array();
  151. $alias = strtolower($alias);
  152. $this->open();
  153. @rewind($this->filehandle);
  154. while ($row = @fgetcsv($this->filehandle, 2048, "|")) {
  155. if(strtolower($row[0]) == $alias) {
  156. return array("nickname" => $row[0],
  157. "name" => $row[1] . " " . $row[2],
  158. "firstname" => $row[1],
  159. "lastname" => $row[2],
  160. "email" => $row[3],
  161. "label" => $row[4],
  162. "backend" => $this->bnum,
  163. "source" => &$this->sname);
  164. }
  165. }
  166. return array();
  167. }
  168. // List all addresses
  169. function list_addr() {
  170. $res = array();
  171. $this->open();
  172. @rewind($this->filehandle);
  173. while ($row = @fgetcsv($this->filehandle, 2048, "|")) {
  174. array_push($res, array("nickname" => $row[0],
  175. "name" => $row[1] . " " . $row[2],
  176. "firstname" => $row[1],
  177. "lastname" => $row[2],
  178. "email" => $row[3],
  179. "label" => $row[4],
  180. "backend" => $this->bnum,
  181. "source" => &$this->sname));
  182. }
  183. return $res;
  184. }
  185. // Add address
  186. function add($userdata) {
  187. if(!$this->writeable)
  188. return $this->set_error(_("Addressbook is read-only"));
  189. // See if user exist already
  190. $ret = $this->lookup($userdata["nickname"]);
  191. if(!empty($ret))
  192. return $this->set_error(sprintf(_("User '%s' already exist"),
  193. $ret["nickname"]));
  194. // Here is the data to write
  195. $data = sprintf("%s|%s|%s|%s|%s", $userdata["nickname"],
  196. $userdata["firstname"], $userdata["lastname"],
  197. $userdata["email"], $userdata["label"]);
  198. // Strip linefeeds
  199. $data = ereg_replace("[\r\n]", " ", $data);
  200. // Add linefeed at end
  201. $data = $data."\n";
  202. // Reopen file, just to be sure
  203. $this->open(true);
  204. if(!$this->writeable)
  205. return $this->set_error(_("Addressbook is read-only"));
  206. // Lock the file
  207. if(!$this->lock())
  208. return $this->set_error(_("Could not lock datafile"));
  209. // Write
  210. $r = fwrite($this->filehandle, $data);
  211. // Unlock file
  212. $this->unlock();
  213. // Test write result and exit if OK
  214. if($r > 0) return true;
  215. // Fail
  216. $this->set_error(_("Write to addressbook failed"));
  217. return false;
  218. }
  219. // Delete address
  220. function remove($alias) {
  221. if(!$this->writeable)
  222. return $this->set_error(_("Addressbook is read-only"));
  223. // Lock the file to make sure we're the only process working
  224. // on it.
  225. if(!$this->lock())
  226. return $this->set_error(_("Could not lock datafile"));
  227. // Read file into memory, ignoring nicknames to delete
  228. $this->open();
  229. @rewind($this->filehandle);
  230. $i = 0;
  231. $rows = array();
  232. while($row = @fgetcsv($this->filehandle, 2048, "|")) {
  233. if(!in_array($row[0], $alias))
  234. $rows[$i++] = $row;
  235. }
  236. // Write data back
  237. if(!$this->overwrite(&$rows)) {
  238. $this->unlock();
  239. return false;
  240. }
  241. $this->unlock();
  242. return true;
  243. }
  244. // Modify address
  245. function modify($alias, $userdata) {
  246. if(!$this->writeable)
  247. return $this->set_error(_("Addressbook is read-only"));
  248. // See if user exist
  249. $ret = $this->lookup($alias);
  250. if(empty($ret))
  251. return $this->set_error(sprintf(_("User '%s' does not exist"),
  252. $alias));
  253. // Lock the file to make sure we're the only process working
  254. // on it.
  255. if(!$this->lock())
  256. return $this->set_error(_("Could not lock datafile"));
  257. // Read file into memory, modifying the data for the
  258. // user identifyed by $alias
  259. $this->open();
  260. @rewind($this->filehandle);
  261. $i = 0;
  262. $rows = array();
  263. while($row = @fgetcsv($this->filehandle, 2048, "|")) {
  264. if(strtolower($row[0]) != strtolower($alias)) {
  265. $rows[$i++] = $row;
  266. } else {
  267. $rows[$i++] = array(0 => $userdata["nickname"],
  268. 1 => $userdata["firstname"],
  269. 2 => $userdata["lastname"],
  270. 3 => $userdata["email"],
  271. 4 => $userdata["label"]);
  272. }
  273. }
  274. // Write data back
  275. if(!$this->overwrite(&$rows)) {
  276. $this->unlock();
  277. return false;
  278. }
  279. $this->unlock();
  280. return true;
  281. }
  282. } // End of class abook_local_file
  283. ?>