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 { |
22 | var $btype = "local"; |
23 | var $bname = "local_file"; |
24 | |
25 | var $filename = ""; |
26 | var $filehandle = 0; |
27 | var $create = false; |
28 | var $umask; |
29 | |
30 | // ========================== Private ======================= |
31 | |
32 | // Constructor |
33 | function abook_local_file($param) { |
34 | $this->sname = _("Personal address book"); |
35 | $this->umask = Umask(); |
36 | |
37 | if(is_array($param)) { |
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")); |
43 | |
44 | $this->filename = $param["filename"]; |
45 | |
46 | if($param["create"]) |
47 | $this->create = true; |
48 | if(isset($param["umask"])) |
49 | $this->umask = $param["umask"]; |
50 | |
d50a8c48 |
51 | if(!empty($param["name"])) |
52 | $this->sname = $param["name"]; |
53 | |
5100704d |
54 | $this->open(true); |
55 | } else { |
1e62e50e |
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) { |
65 | $this->error = ""; |
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) |
75 | return $this->set_error("$file: " . |
76 | _("No such file or directory")); |
77 | |
78 | // Close old file, if any |
79 | if($this->filehandle) $this->close(); |
80 | |
81 | // Open file. First try to open for reading and writing, |
82 | // but fall back to read only. |
83 | umask($this->umask); |
84 | $fh = @fopen($file, "a+"); |
85 | if($fh) { |
86 | $this->filehandle = &$fh; |
87 | $this->filename = $file; |
88 | $this->writeable = true; |
89 | } else { |
90 | $fh = @fopen($file, "r"); |
91 | if($fh) { |
92 | $this->filehandle = &$fh; |
93 | $this->filename = $file; |
94 | $this->writeable = false; |
95 | } else { |
96 | return $this->set_error("$file: "._("Open failed")); |
97 | } |
98 | } |
99 | |
100 | return true; |
101 | } |
102 | |
103 | // Close the file and forget the filehandle |
104 | function close() { |
105 | @fclose($this->filehandle); |
106 | $this->filehandle = 0; |
107 | $this->filename = ""; |
108 | $this->writable = false; |
109 | } |
d50a8c48 |
110 | |
111 | // Lock the datafile - try 20 times in 5 seconds |
112 | function lock() { |
113 | for($i = 0 ; $i < 20 ; $i++) { |
114 | if(flock($this->filehandle, 2 + 4)) |
115 | return true; |
116 | else |
117 | usleep(250000); |
118 | } |
119 | return false; |
120 | } |
121 | |
122 | // Lock the datafile |
123 | function unlock() { |
124 | return flock($this->filehandle, 3); |
125 | } |
126 | |
127 | // Overwrite the file with data from $rows |
128 | // NOTE! Previous locks are broken by this function |
129 | function overwrite($rows) { |
130 | $newfh = @fopen($this->filename, "w"); |
131 | if(!$newfh) |
132 | return $this->set_error("$file: "._("Open failed")); |
133 | |
134 | for($i = 0 ; $i < sizeof($rows) ; $i++) { |
135 | if(is_array($rows[$i])) |
136 | fwrite($newfh, join("|", $rows[$i])."\n"); |
137 | } |
138 | |
139 | fclose($newfh); |
140 | $this->unlock(); |
141 | $this->open(true); |
142 | return true; |
143 | } |
5100704d |
144 | |
145 | // ========================== Public ======================== |
146 | |
147 | // Search the file |
148 | function search($expr) { |
149 | |
150 | // To be replaced by advanded search expression parsing |
151 | if(is_array($expr)) return; |
152 | |
153 | // Make regexp from glob'ed expression |
154 | $expr = ereg_replace("\?", ".", $expr); |
155 | $expr = ereg_replace("\*", ".*", $expr); |
156 | |
157 | $res = array(); |
158 | if(!$this->open()) |
159 | return false; |
160 | |
161 | @rewind($this->filehandle); |
162 | |
163 | while ($row = @fgetcsv($this->filehandle, 2048, "|")) { |
164 | $line = join(" ", $row); |
165 | if(eregi($expr, $line)) { |
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)); |
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 | |
190 | while ($row = @fgetcsv($this->filehandle, 2048, "|")) { |
191 | if(strtolower($row[0]) == $alias) { |
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); |
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 | |
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)); |
221 | } |
222 | return $res; |
223 | } |
224 | |
225 | // Add address |
226 | function add($userdata) { |
227 | if(!$this->writeable) |
228 | return $this->set_error(_("Addressbook is read-only")); |
229 | |
230 | // See if user exist already |
231 | $ret = $this->lookup($userdata["nickname"]); |
232 | if(!empty($ret)) |
233 | return $this->set_error(sprintf(_("User '%s' already exist"), |
234 | $ret["nickname"])); |
235 | |
236 | // Here is the data to write |
237 | $data = sprintf("%s|%s|%s|%s|%s", $userdata["nickname"], |
238 | $userdata["firstname"], $userdata["lastname"], |
239 | $userdata["email"], $userdata["label"]); |
240 | // Strip linefeeds |
241 | $data = ereg_replace("[\r\n]", " ", $data); |
242 | // Add linefeed at end |
243 | $data = $data."\n"; |
244 | |
245 | // Reopen file, just to be sure |
246 | $this->open(true); |
247 | if(!$this->writeable) |
248 | return $this->set_error(_("Addressbook is read-only")); |
249 | |
d50a8c48 |
250 | // Lock the file |
251 | if(!$this->lock()) |
252 | return $this->set_error(_("Could not lock datafile")); |
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 |
5100704d |
264 | $this->set_error(_("Write to addressbook failed")); |
265 | return false; |
266 | } |
267 | |
d50a8c48 |
268 | // Delete address |
269 | function remove($alias) { |
270 | if(!$this->writeable) |
271 | return $this->set_error(_("Addressbook is read-only")); |
272 | |
273 | // Lock the file to make sure we're the only process working |
274 | // on it. |
275 | if(!$this->lock()) |
276 | return $this->set_error(_("Could not lock datafile")); |
277 | |
278 | // Read file into memory, ignoring nicknames to delete |
279 | $this->open(); |
280 | @rewind($this->filehandle); |
281 | $i = 0; |
282 | $rows = array(); |
283 | while($row = @fgetcsv($this->filehandle, 2048, "|")) { |
284 | if(!in_array($row[0], $alias)) |
285 | $rows[$i++] = $row; |
286 | } |
287 | |
288 | // Write data back |
289 | if(!$this->overwrite(&$rows)) { |
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) |
301 | return $this->set_error(_("Addressbook is read-only")); |
302 | |
303 | // See if user exist |
304 | $ret = $this->lookup($alias); |
305 | if(empty($ret)) |
306 | return $this->set_error(sprintf(_("User '%s' does not exist"), |
307 | $alias)); |
308 | |
309 | // Lock the file to make sure we're the only process working |
310 | // on it. |
311 | if(!$this->lock()) |
312 | return $this->set_error(_("Could not lock datafile")); |
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(); |
320 | while($row = @fgetcsv($this->filehandle, 2048, "|")) { |
321 | if(strtolower($row[0]) != strtolower($alias)) { |
322 | $rows[$i++] = $row; |
323 | } else { |
324 | $rows[$i++] = array(0 => $userdata["nickname"], |
325 | 1 => $userdata["firstname"], |
326 | 2 => $userdata["lastname"], |
327 | 3 => $userdata["email"], |
328 | 4 => $userdata["label"]); |
329 | } |
330 | } |
331 | |
332 | // Write data back |
333 | if(!$this->overwrite(&$rows)) { |
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 | ?> |