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) { |
701c9c6b |
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'] . ': '. |
701c9c6b |
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) |
701c9c6b |
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 { |
701c9c6b |
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 |
507832e7 |
128 | function overwrite(&$rows) { |
91be2362 |
129 | $newfh = @fopen($this->filename, 'w'); |
d50a8c48 |
130 | if(!$newfh) |
701c9c6b |
131 | return $this->set_error("$file: " . _("Open failed")); |
d50a8c48 |
132 | |
133 | for($i = 0 ; $i < sizeof($rows) ; $i++) { |
134 | if(is_array($rows[$i])) |
a9bad541 |
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 | |
146e0c45 |
152 | // Make regexp from glob'ed expression |
153 | // May want to quote other special characters like (, ), -, [, ], etc. |
16a973d7 |
154 | $expr = str_replace('?', '.', $expr); |
155 | $expr = str_replace('*', '.*', $expr); |
5100704d |
156 | |
157 | $res = array(); |
158 | if(!$this->open()) |
159 | return false; |
160 | |
161 | @rewind($this->filehandle); |
162 | |
91be2362 |
163 | while ($row = @fgetcsv($this->filehandle, 2048, '|')) { |
164 | $line = join(' ', $row); |
5100704d |
165 | if(eregi($expr, $line)) { |
91be2362 |
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)); |
5100704d |
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 | |
91be2362 |
190 | while ($row = @fgetcsv($this->filehandle, 2048, '|')) { |
5100704d |
191 | if(strtolower($row[0]) == $alias) { |
91be2362 |
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); |
5100704d |
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 | |
91be2362 |
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)); |
5100704d |
221 | } |
222 | return $res; |
223 | } |
224 | |
225 | // Add address |
226 | function add($userdata) { |
227 | if(!$this->writeable) |
701c9c6b |
228 | return $this->set_error(_("Addressbook is read-only")); |
5100704d |
229 | |
230 | // See if user exist already |
91be2362 |
231 | $ret = $this->lookup($userdata['nickname']); |
5100704d |
232 | if(!empty($ret)) |
701c9c6b |
233 | return $this->set_error(sprintf(_("User '%s' already exist"), |
91be2362 |
234 | $ret['nickname'])); |
5100704d |
235 | |
236 | // Here is the data to write |
91be2362 |
237 | $data = $userdata['nickname'] . '|' . $userdata['firstname'] . '|' . |
238 | $userdata['lastname'] . '|' . $userdata['email'] . '|' . |
239 | $userdata['label']; |
5100704d |
240 | // Strip linefeeds |
a9bad541 |
241 | $data = ereg_replace("[\r\n]", ' ', $data); |
5100704d |
242 | // Add linefeed at end |
a9bad541 |
243 | $data = $data . "\n"; |
5100704d |
244 | |
245 | // Reopen file, just to be sure |
246 | $this->open(true); |
247 | if(!$this->writeable) |
701c9c6b |
248 | return $this->set_error(_("Addressbook is read-only")); |
5100704d |
249 | |
d50a8c48 |
250 | // Lock the file |
251 | if(!$this->lock()) |
701c9c6b |
252 | return $this->set_error(_("Could not lock datafile")); |
d50a8c48 |
253 | |
254 | // Write |
5100704d |
255 | $r = fwrite($this->filehandle, $data); |
5100704d |
256 | |
d50a8c48 |
257 | // Unlock file |
258 | $this->unlock(); |
259 | |
260 | // Test write result and exit if OK |
261 | if($r > 0) return true; |
262 | |
263 | // Fail |
701c9c6b |
264 | $this->set_error(_("Write to addressbook failed")); |
5100704d |
265 | return false; |
266 | } |
267 | |
d50a8c48 |
268 | // Delete address |
269 | function remove($alias) { |
270 | if(!$this->writeable) |
701c9c6b |
271 | return $this->set_error(_("Addressbook is read-only")); |
d50a8c48 |
272 | |
273 | // Lock the file to make sure we're the only process working |
274 | // on it. |
275 | if(!$this->lock()) |
701c9c6b |
276 | return $this->set_error(_("Could not lock datafile")); |
d50a8c48 |
277 | |
278 | // Read file into memory, ignoring nicknames to delete |
279 | $this->open(); |
280 | @rewind($this->filehandle); |
281 | $i = 0; |
282 | $rows = array(); |
91be2362 |
283 | while($row = @fgetcsv($this->filehandle, 2048, '|')) { |
d50a8c48 |
284 | if(!in_array($row[0], $alias)) |
285 | $rows[$i++] = $row; |
286 | } |
287 | |
288 | // Write data back |
507832e7 |
289 | if(!$this->overwrite($rows)) { |
d50a8c48 |
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) |
701c9c6b |
301 | return $this->set_error(_("Addressbook is read-only")); |
d50a8c48 |
302 | |
303 | // See if user exist |
304 | $ret = $this->lookup($alias); |
305 | if(empty($ret)) |
701c9c6b |
306 | return $this->set_error(sprintf(_("User '%s' does not exist"), |
d50a8c48 |
307 | $alias)); |
308 | |
309 | // Lock the file to make sure we're the only process working |
310 | // on it. |
311 | if(!$this->lock()) |
701c9c6b |
312 | return $this->set_error(_("Could not lock datafile")); |
d50a8c48 |
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(); |
91be2362 |
320 | while($row = @fgetcsv($this->filehandle, 2048, '|')) { |
d50a8c48 |
321 | if(strtolower($row[0]) != strtolower($alias)) { |
322 | $rows[$i++] = $row; |
323 | } else { |
91be2362 |
324 | $rows[$i++] = array(0 => $userdata['nickname'], |
325 | 1 => $userdata['firstname'], |
326 | 2 => $userdata['lastname'], |
327 | 3 => $userdata['email'], |
328 | 4 => $userdata['label']); |
d50a8c48 |
329 | } |
330 | } |
331 | |
332 | // Write data back |
507832e7 |
333 | if(!$this->overwrite($rows)) { |
d50a8c48 |
334 | $this->unlock(); |
335 | return false; |
336 | } |
337 | |
338 | $this->unlock(); |
339 | return true; |
340 | } |
341 | |
342 | } // End of class abook_local_file |
5100704d |
343 | ?> |