Changed language-code link.
[squirrelmail.git] / functions / abook_local_file.php
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
49 if(!empty($param["name"]))
50 $this->sname = $param["name"];
51
52 $this->open(true);
53 } else {
54 $this->set_error("Invalid argument to constructor");
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 }
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 }
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
248 // Lock the file
249 if(!$this->lock())
250 return $this->set_error(_("Could not lock datafile"));
251
252 // Write
253 $r = fwrite($this->filehandle, $data);
254
255 // Unlock file
256 $this->unlock();
257
258 // Test write result and exit if OK
259 if($r > 0) return true;
260
261 // Fail
262 $this->set_error(_("Write to addressbook failed"));
263 return false;
264 }
265
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
341 ?>