6
* Copyright (c) 1999-2003 The SquirrelMail Project Team
7
* Licensed under the GNU GPL. For full terms see the file COPYING.
9
* Backend for addressbook as a pipe separated file
11
* An array with the following elements must be passed to
12
* the class constructor (elements marked ? are optional):
14
* filename => path to addressbook file
15
* ? create => if true: file is created if it does not exist.
16
* ? umask => umask set before opening file.
18
* NOTE. This class should not be used directly. Use the
19
* "AddressBook" class instead.
21
* $Id: abook_local_file.php,v 1.24 2003/10/29 15:25:04 tassium Exp $
22
* @package squirrelmail
26
* Store the addressbook in a local file
27
* @package squirrelmail
29
class abook_local_file extends addressbook_backend {
31
var $bname = 'local_file';
38
/* ========================== Private ======================= */
41
function abook_local_file($param) {
42
$this->sname = _("Personal address book");
43
$this->umask = Umask();
45
if(is_array($param)) {
46
if(empty($param['filename'])) {
47
return $this->set_error('Invalid parameters');
49
if(!is_string($param['filename'])) {
50
return $this->set_error($param['filename'] . ': '.
51
_("Not a file name"));
54
$this->filename = $param['filename'];
56
if($param['create']) {
59
if(isset($param['umask'])) {
60
$this->umask = $param['umask'];
62
if(!empty($param['name'])) {
63
$this->sname = $param['name'];
68
$this->set_error('Invalid argument to constructor');
72
/* Open the addressbook file and store the file pointer.
73
* Use $file as the file to open, or the class' own
74
* filename property. If $param is empty and file is
75
* open, do nothing. */
76
function open($new = false) {
78
$file = $this->filename;
79
$create = $this->create;
81
/* Return true is file is open and $new is unset */
82
if($this->filehandle && !$new) {
86
/* Check that new file exitsts */
87
if((!(file_exists($file) && is_readable($file))) && !$create) {
88
return $this->set_error("$file: " . _("No such file or directory"));
91
/* Close old file, if any */
92
if($this->filehandle) { $this->close(); }
94
/* Open file. First try to open for reading and writing,
95
* but fall back to read only. */
97
$fh = @fopen($file, 'a+');
99
$this->filehandle = &$fh;
100
$this->filename = $file;
101
$this->writeable = true;
103
$fh = @fopen($file, 'r');
105
$this->filehandle = &$fh;
106
$this->filename = $file;
107
$this->writeable = false;
109
return $this->set_error("$file: " . _("Open failed"));
115
/* Close the file and forget the filehandle */
117
@fclose($this->filehandle);
118
$this->filehandle = 0;
119
$this->filename = '';
120
$this->writable = false;
123
/* Lock the datafile - try 20 times in 5 seconds */
125
for($i = 0 ; $i < 20 ; $i++) {
126
if(flock($this->filehandle, 2 + 4))
134
/* Lock the datafile */
136
return flock($this->filehandle, 3);
139
/* Overwrite the file with data from $rows
140
* NOTE! Previous locks are broken by this function */
141
function overwrite(&$rows) {
143
$newfh = @fopen($this->filename.'.tmp', 'w');
146
return $this->set_error($this->filename. '.tmp:' . _("Open failed"));
149
for($i = 0, $cnt=sizeof($rows) ; $i < $cnt ; $i++) {
150
if(is_array($rows[$i])) {
151
for($j = 0, $cnt_part=count($rows[$i]) ; $j < $cnt_part ; $j++) {
152
$rows[$i][$j] = $this->quotevalue($rows[$i][$j]);
154
$tmpwrite = @fwrite($newfh, join('|', $rows[$i]) . "\n");
155
if ($tmpwrite == -1) {
156
return $this->set_error($this->filename . '.tmp:' . _("Write failed"));
162
if (!@copy($this->filename . '.tmp' , $this->filename)) {
163
return $this->set_error($this->filename . ':' . _("Unable to update"));
165
@unlink($this->filename . '.tmp');
171
/* ========================== Public ======================== */
173
/* Search the file */
174
function search($expr) {
176
/* To be replaced by advanded search expression parsing */
177
if(is_array($expr)) { return; }
179
/* Make regexp from glob'ed expression
180
* May want to quote other special characters like (, ), -, [, ], etc. */
181
$expr = str_replace('?', '.', $expr);
182
$expr = str_replace('*', '.*', $expr);
188
@rewind($this->filehandle);
190
while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
191
$line = join(' ', $row);
192
if(eregi($expr, $line)) {
193
array_push($res, array('nickname' => $row[0],
194
'name' => $row[1] . ' ' . $row[2],
195
'firstname' => $row[1],
196
'lastname' => $row[2],
199
'backend' => $this->bnum,
200
'source' => &$this->sname));
208
function lookup($alias) {
213
$alias = strtolower($alias);
216
@rewind($this->filehandle);
218
while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
219
if(strtolower($row[0]) == $alias) {
220
return array('nickname' => $row[0],
221
'name' => $row[1] . ' ' . $row[2],
222
'firstname' => $row[1],
223
'lastname' => $row[2],
226
'backend' => $this->bnum,
227
'source' => &$this->sname);
234
/* List all addresses */
235
function list_addr() {
238
@rewind($this->filehandle);
240
while ($row = @fgetcsv($this->filehandle, 2048, '|')) {
241
array_push($res, array('nickname' => $row[0],
242
'name' => $row[1] . ' ' . $row[2],
243
'firstname' => $row[1],
244
'lastname' => $row[2],
247
'backend' => $this->bnum,
248
'source' => &$this->sname));
254
function add($userdata) {
255
if(!$this->writeable) {
256
return $this->set_error(_("Addressbook is read-only"));
258
/* See if user exists already */
259
$ret = $this->lookup($userdata['nickname']);
261
return $this->set_error(sprintf(_("User '%s' already exist"),
265
/* Here is the data to write */
266
$data = $this->quotevalue($userdata['nickname']) . '|' .
267
$this->quotevalue($userdata['firstname']) . '|' .
268
$this->quotevalue($userdata['lastname']) . '|' .
269
$this->quotevalue($userdata['email']) . '|' .
270
$this->quotevalue($userdata['label']);
272
/* Strip linefeeds */
273
$data = ereg_replace("[\r\n]", ' ', $data);
274
/* Add linefeed at end */
275
$data = $data . "\n";
277
/* Reopen file, just to be sure */
279
if(!$this->writeable) {
280
return $this->set_error(_("Addressbook is read-only"));
285
return $this->set_error(_("Could not lock datafile"));
289
$r = fwrite($this->filehandle, $data);
294
/* Test write result and exit if OK */
295
if($r > 0) return true;
298
$this->set_error(_("Write to addressbook failed"));
303
function remove($alias) {
304
if(!$this->writeable) {
305
return $this->set_error(_("Addressbook is read-only"));
308
/* Lock the file to make sure we're the only process working
311
return $this->set_error(_("Could not lock datafile"));
314
/* Read file into memory, ignoring nicknames to delete */
315
@rewind($this->filehandle);
318
while($row = @fgetcsv($this->filehandle, 2048, '|')) {
319
if(!in_array($row[0], $alias)) {
324
/* Write data back */
325
if(!$this->overwrite($rows)) {
335
function modify($alias, $userdata) {
336
if(!$this->writeable) {
337
return $this->set_error(_("Addressbook is read-only"));
340
/* See if user exists */
341
$ret = $this->lookup($alias);
343
return $this->set_error(sprintf(_("User '%s' does not exist"),
347
/* Lock the file to make sure we're the only process working
350
return $this->set_error(_("Could not lock datafile"));
353
/* Read file into memory, modifying the data for the
354
* user identified by $alias */
356
@rewind($this->filehandle);
359
while($row = @fgetcsv($this->filehandle, 2048, '|')) {
360
if(strtolower($row[0]) != strtolower($alias)) {
363
$rows[$i++] = array(0 => $userdata['nickname'],
364
1 => $userdata['firstname'],
365
2 => $userdata['lastname'],
366
3 => $userdata['email'],
367
4 => $userdata['label']);
371
/* Write data back */
372
if(!$this->overwrite($rows)) {
381
/* Function for quoting values before saving */
382
function quotevalue($value) {
383
/* Quote the field if it contains | or ". Double quotes need to
384
* be replaced with "" */
385
if(ereg("[|\"]", $value)) {
386
$value = '"' . str_replace('"', '""', $value) . '"';
391
} /* End of class abook_local_file */