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