abook_local_file.php 9.0 KB

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