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