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