abook_local_file.php 9.2 KB

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