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