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