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