5100704d |
1 | <?php |
4b4abf93 |
2 | |
35586184 |
3 | /** |
4 | * abook_local_file.php |
5 | * |
33aab559 |
6 | * @copyright 1999-2024 The SquirrelMail Project Team |
4b4abf93 |
7 | * @license http://opensource.org/licenses/gpl-license.php GNU Public License |
a9d318b0 |
8 | * @version $Id$ |
d6c32258 |
9 | * @package squirrelmail |
a9d318b0 |
10 | * @subpackage addressbook |
35586184 |
11 | */ |
5100704d |
12 | |
d6c32258 |
13 | /** |
147e5af3 |
14 | * Backend for address book as a pipe separated file |
15 | * |
16 | * Stores the address book in a local file |
17 | * |
18 | * An array with the following elements must be passed to |
19 | * the class constructor (elements marked ? are optional): |
20 | *<pre> |
21 | * filename => path to addressbook file |
22 | * ? create => if true: file is created if it does not exist. |
23 | * ? umask => umask set before opening file. |
675357d2 |
24 | * ? name => name of address book. |
91e0dccc |
25 | * ? detect_writeable => detect address book access permissions by |
675357d2 |
26 | * checking file permissions. |
27 | * ? writeable => allow writing into address book. Used only when |
28 | * detect_writeable is set to false. |
e59a9c41 |
29 | * ? listing => enable/disable listing |
147e5af3 |
30 | *</pre> |
31 | * NOTE. This class should not be used directly. Use the |
32 | * "AddressBook" class instead. |
8f6f9ba5 |
33 | * @package squirrelmail |
d6c32258 |
34 | */ |
35586184 |
35 | class abook_local_file extends addressbook_backend { |
4272758c |
36 | /** |
37 | * Backend type |
91e0dccc |
38 | * @var string |
4272758c |
39 | */ |
06b4facd |
40 | var $btype = 'local'; |
4272758c |
41 | /** |
42 | * Backend name |
43 | * @var string |
44 | */ |
06b4facd |
45 | var $bname = 'local_file'; |
46 | |
4272758c |
47 | /** |
48 | * File used to store data |
49 | * @var string |
50 | */ |
51 | var $filename = ''; |
52 | /** |
53 | * File handle |
54 | * @var object |
55 | */ |
06b4facd |
56 | var $filehandle = 0; |
4272758c |
57 | /** |
58 | * Create file, if it not present |
59 | * @var bool |
60 | */ |
61 | var $create = false; |
62 | /** |
63 | * Detect, if address book is writeable by checking file permisions |
64 | * @var bool |
65 | */ |
66 | var $detect_writeable = true; |
67 | /** |
68 | * Control write access to address book |
69 | * |
70 | * Option does not have any effect, if 'detect_writeable' is 'true' |
71 | * @var bool |
72 | */ |
73 | var $writeable = false; |
e59a9c41 |
74 | /** |
75 | * controls listing of address book |
76 | * @var bool |
77 | */ |
78 | var $listing = true; |
4272758c |
79 | /** |
80 | * Umask of the file |
81 | * @var string |
82 | */ |
06b4facd |
83 | var $umask; |
7311c377 |
84 | /** |
85 | * Sets max entry size (number of bytes used for all address book fields |
86 | * (including escapes) + 4 delimiters + 1 linefeed) |
87 | * @var integer |
88 | * @since 1.5.2 |
89 | */ |
90 | var $line_length = 2048; |
06b4facd |
91 | |
92 | /* ========================== Private ======================= */ |
93 | |
147e5af3 |
94 | /** |
d84568cf |
95 | * Constructor (PHP5 style, required in some future version of PHP) |
147e5af3 |
96 | * @param array $param backend options |
97 | * @return bool |
98 | */ |
d84568cf |
99 | function __construct($param) { |
232fb714 |
100 | $this->sname = _("Personal Address Book"); |
06b4facd |
101 | $this->umask = Umask(); |
102 | |
103 | if(is_array($param)) { |
104 | if(empty($param['filename'])) { |
105 | return $this->set_error('Invalid parameters'); |
106 | } |
107 | if(!is_string($param['filename'])) { |
108 | return $this->set_error($param['filename'] . ': '. |
109 | _("Not a file name")); |
110 | } |
111 | |
112 | $this->filename = $param['filename']; |
113 | |
147e5af3 |
114 | if(isset($param['create'])) { |
115 | $this->create = $param['create']; |
06b4facd |
116 | } |
117 | if(isset($param['umask'])) { |
118 | $this->umask = $param['umask']; |
119 | } |
4272758c |
120 | if(isset($param['name'])) { |
06b4facd |
121 | $this->sname = $param['name']; |
122 | } |
4272758c |
123 | if(isset($param['detect_writeable'])) { |
124 | $this->detect_writeable = $param['detect_writeable']; |
125 | } |
126 | if(!empty($param['writeable'])) { |
127 | $this->writeable = $param['writeable']; |
128 | } |
e59a9c41 |
129 | if(isset($param['listing'])) { |
130 | $this->listing = $param['listing']; |
131 | } |
7311c377 |
132 | if(isset($param['line_length']) && ! empty($param['line_length'])) { |
133 | $this->line_length = (int) $param['line_length']; |
134 | } |
62f7daa5 |
135 | |
06b4facd |
136 | $this->open(true); |
137 | } else { |
138 | $this->set_error('Invalid argument to constructor'); |
139 | } |
140 | } |
141 | |
d84568cf |
142 | /** |
143 | * Constructor (PHP4 style, kept for compatibility reasons) |
144 | * @param array $param backend options |
145 | * @return bool |
146 | */ |
147 | function abook_local_file($param) { |
148 | return self::__construct($param); |
149 | } |
150 | |
147e5af3 |
151 | /** |
152 | * Open the addressbook file and store the file pointer. |
62f7daa5 |
153 | * Use $file as the file to open, or the class' own |
154 | * filename property. If $param is empty and file is |
147e5af3 |
155 | * open, do nothing. |
156 | * @param bool $new is file already opened |
157 | * @return bool |
158 | */ |
06b4facd |
159 | function open($new = false) { |
160 | $this->error = ''; |
161 | $file = $this->filename; |
162 | $create = $this->create; |
0a496c76 |
163 | $fopenmode = (($this->writeable && sq_is_writable($file)) ? 'a+' : 'r'); |
62f7daa5 |
164 | |
06b4facd |
165 | /* Return true is file is open and $new is unset */ |
166 | if($this->filehandle && !$new) { |
167 | return true; |
168 | } |
62f7daa5 |
169 | |
06b4facd |
170 | /* Check that new file exitsts */ |
171 | if((!(file_exists($file) && is_readable($file))) && !$create) { |
172 | return $this->set_error("$file: " . _("No such file or directory")); |
173 | } |
62f7daa5 |
174 | |
06b4facd |
175 | /* Close old file, if any */ |
176 | if($this->filehandle) { $this->close(); } |
62f7daa5 |
177 | |
06b4facd |
178 | umask($this->umask); |
4272758c |
179 | if (! $this->detect_writeable) { |
180 | $fh = @fopen($file,$fopenmode); |
181 | if ($fh) { |
182 | $this->filehandle = &$fh; |
183 | $this->filename = $file; |
184 | } else { |
185 | return $this->set_error("$file: " . _("Open failed")); |
186 | } |
06b4facd |
187 | } else { |
4272758c |
188 | /* Open file. First try to open for reading and writing, |
189 | * but fall back to read only. */ |
190 | $fh = @fopen($file, 'a+'); |
06b4facd |
191 | if($fh) { |
192 | $this->filehandle = &$fh; |
193 | $this->filename = $file; |
4272758c |
194 | $this->writeable = true; |
06b4facd |
195 | } else { |
4272758c |
196 | $fh = @fopen($file, 'r'); |
197 | if($fh) { |
198 | $this->filehandle = &$fh; |
199 | $this->filename = $file; |
200 | $this->writeable = false; |
201 | } else { |
202 | return $this->set_error("$file: " . _("Open failed")); |
203 | } |
06b4facd |
204 | } |
205 | } |
206 | return true; |
207 | } |
208 | |
147e5af3 |
209 | /** Close the file and forget the filehandle */ |
06b4facd |
210 | function close() { |
211 | @fclose($this->filehandle); |
212 | $this->filehandle = 0; |
213 | $this->filename = ''; |
214 | $this->writable = false; |
215 | } |
216 | |
147e5af3 |
217 | /** Lock the datafile - try 20 times in 5 seconds */ |
06b4facd |
218 | function lock() { |
219 | for($i = 0 ; $i < 20 ; $i++) { |
62f7daa5 |
220 | if(flock($this->filehandle, 2 + 4)) |
06b4facd |
221 | return true; |
222 | else |
223 | usleep(250000); |
224 | } |
225 | return false; |
226 | } |
227 | |
147e5af3 |
228 | /** Unlock the datafile */ |
06b4facd |
229 | function unlock() { |
230 | return flock($this->filehandle, 3); |
231 | } |
232 | |
147e5af3 |
233 | /** |
234 | * Overwrite the file with data from $rows |
235 | * NOTE! Previous locks are broken by this function |
236 | * @param array $rows new data |
237 | * @return bool |
238 | */ |
06b4facd |
239 | function overwrite(&$rows) { |
01265fba |
240 | $this->unlock(); |
dabef6fd |
241 | $newfh = @fopen($this->filename.'.tmp', 'w'); |
242 | |
06b4facd |
243 | if(!$newfh) { |
dabef6fd |
244 | return $this->set_error($this->filename. '.tmp:' . _("Open failed")); |
06b4facd |
245 | } |
62f7daa5 |
246 | |
dabef6fd |
247 | for($i = 0, $cnt=sizeof($rows) ; $i < $cnt ; $i++) { |
06b4facd |
248 | if(is_array($rows[$i])) { |
dabef6fd |
249 | for($j = 0, $cnt_part=count($rows[$i]) ; $j < $cnt_part ; $j++) { |
77ec28e9 |
250 | $rows[$i][$j] = $this->quotevalue($rows[$i][$j]); |
251 | } |
3ecad5e6 |
252 | $tmpwrite = sq_fwrite($newfh, join('|', $rows[$i]) . "\n"); |
253 | if ($tmpwrite === FALSE) { |
dabef6fd |
254 | return $this->set_error($this->filename . '.tmp:' . _("Write failed")); |
255 | } |
06b4facd |
256 | } |
62f7daa5 |
257 | } |
06b4facd |
258 | |
259 | fclose($newfh); |
baa59994 |
260 | if (!@copy($this->filename . '.tmp' , $this->filename)) { |
dabef6fd |
261 | return $this->set_error($this->filename . ':' . _("Unable to update")); |
baa59994 |
262 | } |
dabef6fd |
263 | @unlink($this->filename . '.tmp'); |
53d36779 |
264 | @chmod($this->filename, 0600); |
06b4facd |
265 | $this->unlock(); |
266 | $this->open(true); |
267 | return true; |
268 | } |
62f7daa5 |
269 | |
06b4facd |
270 | /* ========================== Public ======================== */ |
62f7daa5 |
271 | |
147e5af3 |
272 | /** |
273 | * Search the file |
274 | * @param string $expr search expression |
275 | * @return array search results |
276 | */ |
06b4facd |
277 | function search($expr) { |
278 | |
279 | /* To be replaced by advanded search expression parsing */ |
280 | if(is_array($expr)) { return; } |
62f7daa5 |
281 | |
327e2d96 |
282 | // don't allow wide search when listing is disabled. |
283 | if ($expr=='*' && ! $this->listing) |
284 | return array(); |
285 | |
63389330 |
286 | // Make regexp from glob'ed expression |
287 | $expr = preg_quote($expr); |
288 | $expr = str_replace(array('\\?', '\\*'), array('.', '.*'), $expr); |
62f7daa5 |
289 | |
06b4facd |
290 | $res = array(); |
291 | if(!$this->open()) { |
292 | return false; |
293 | } |
294 | @rewind($this->filehandle); |
62f7daa5 |
295 | |
7311c377 |
296 | while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) { |
297 | if (count($row)<5) { |
298 | /** |
299 | * address book is corrupted. |
300 | */ |
301 | global $oTemplate; |
302 | error_box(_("Address book is corrupted. Required fields are missing.")); |
303 | $oTemplate->display('footer.tpl'); |
304 | die(); |
305 | } else { |
7311c377 |
306 | /** |
307 | * TODO: regexp search is supported only in local_file backend. |
308 | * Do we check format of regexp or ignore errors? |
309 | */ |
b7910e12 |
310 | // errors on preg_match call are suppressed in order to prevent display of regexp compilation errors |
63389330 |
311 | if (@preg_match('/' . $expr . '/i', $row[0]) // nickname |
312 | || @preg_match('/' . $expr . '/i', $row[1]) // firstname |
313 | || @preg_match('/' . $expr . '/i', $row[2]) // lastname |
314 | || @preg_match('/' . $expr . '/i', $row[3])) { // email |
7311c377 |
315 | array_push($res, array('nickname' => $row[0], |
316 | 'name' => $this->fullname($row[1], $row[2]), |
317 | 'firstname' => $row[1], |
318 | 'lastname' => $row[2], |
319 | 'email' => $row[3], |
320 | 'label' => $row[4], |
321 | 'backend' => $this->bnum, |
322 | 'source' => &$this->sname)); |
323 | } |
06b4facd |
324 | } |
325 | } |
62f7daa5 |
326 | |
06b4facd |
327 | return $res; |
328 | } |
62f7daa5 |
329 | |
147e5af3 |
330 | /** |
503c7650 |
331 | * Lookup an address by the indicated field. |
332 | * |
333 | * @param string $value The value to look up |
334 | * @param integer $field The field to look in, should be one |
335 | * of the SM_ABOOK_FIELD_* constants |
336 | * defined in include/constants.php |
337 | * (OPTIONAL; defaults to nickname field) |
bf55ebab |
338 | * NOTE: uniqueness is only guaranteed |
339 | * when the nickname field is used here; |
340 | * otherwise, the first matching address |
341 | * is returned. |
503c7650 |
342 | * |
343 | * @return array Array with lookup results when the value |
344 | * was found, an empty array if the value was |
345 | * not found. |
346 | * |
147e5af3 |
347 | */ |
503c7650 |
348 | function lookup($value, $field=SM_ABOOK_FIELD_NICKNAME) { |
349 | if(empty($value)) { |
06b4facd |
350 | return array(); |
351 | } |
352 | |
503c7650 |
353 | $value = strtolower($value); |
62f7daa5 |
354 | |
06b4facd |
355 | $this->open(); |
356 | @rewind($this->filehandle); |
62f7daa5 |
357 | |
7311c377 |
358 | while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) { |
359 | if (count($row)<5) { |
360 | /** |
361 | * address book is corrupted. |
362 | */ |
363 | global $oTemplate; |
364 | error_box(_("Address book is corrupted. Required fields are missing.")); |
365 | $oTemplate->display('footer.tpl'); |
366 | die(); |
367 | } else { |
503c7650 |
368 | if(strtolower($row[$field]) == $value) { |
7311c377 |
369 | return array('nickname' => $row[0], |
370 | 'name' => $this->fullname($row[1], $row[2]), |
371 | 'firstname' => $row[1], |
372 | 'lastname' => $row[2], |
373 | 'email' => $row[3], |
374 | 'label' => $row[4], |
375 | 'backend' => $this->bnum, |
376 | 'source' => &$this->sname); |
377 | } |
06b4facd |
378 | } |
379 | } |
62f7daa5 |
380 | |
06b4facd |
381 | return array(); |
382 | } |
383 | |
147e5af3 |
384 | /** |
385 | * List all addresses |
386 | * @return array list of all addresses |
387 | */ |
06b4facd |
388 | function list_addr() { |
389 | $res = array(); |
e59a9c41 |
390 | |
391 | if(isset($this->listing) && !$this->listing) { |
392 | return array(); |
393 | } |
394 | |
06b4facd |
395 | $this->open(); |
396 | @rewind($this->filehandle); |
62f7daa5 |
397 | |
7311c377 |
398 | while ($row = @fgetcsv($this->filehandle, $this->line_length, '|')) { |
399 | if (count($row)<5) { |
400 | /** |
401 | * address book is corrupted. Don't be nice to people that |
402 | * violate address book formating. |
403 | */ |
404 | global $oTemplate; |
405 | error_box(_("Address book is corrupted. Required fields are missing.")); |
406 | $oTemplate->display('footer.tpl'); |
407 | die(); |
408 | } else { |
409 | array_push($res, array('nickname' => $row[0], |
410 | 'name' => $this->fullname($row[1], $row[2]), |
411 | 'firstname' => $row[1], |
412 | 'lastname' => $row[2], |
413 | 'email' => $row[3], |
414 | 'label' => $row[4], |
415 | 'backend' => $this->bnum, |
416 | 'source' => &$this->sname)); |
417 | } |
06b4facd |
418 | } |
419 | return $res; |
420 | } |
421 | |
147e5af3 |
422 | /** |
423 | * Add address |
424 | * @param array $userdata new data |
425 | * @return bool |
426 | */ |
06b4facd |
427 | function add($userdata) { |
428 | if(!$this->writeable) { |
35235328 |
429 | return $this->set_error(_("Address book is read-only")); |
06b4facd |
430 | } |
431 | /* See if user exists already */ |
432 | $ret = $this->lookup($userdata['nickname']); |
433 | if(!empty($ret)) { |
2706a0b1 |
434 | // i18n: don't use html formating in translation |
435 | return $this->set_error(sprintf(_("User \"%s\" already exists"),$ret['nickname'])); |
06b4facd |
436 | } |
62f7daa5 |
437 | |
06b4facd |
438 | /* Here is the data to write */ |
77ec28e9 |
439 | $data = $this->quotevalue($userdata['nickname']) . '|' . |
440 | $this->quotevalue($userdata['firstname']) . '|' . |
8419c13b |
441 | $this->quotevalue((!empty($userdata['lastname'])?$userdata['lastname']:'')) . '|' . |
77ec28e9 |
442 | $this->quotevalue($userdata['email']) . '|' . |
8419c13b |
443 | $this->quotevalue((!empty($userdata['label'])?$userdata['label']:'')); |
77ec28e9 |
444 | |
06b4facd |
445 | /* Strip linefeeds */ |
b7910e12 |
446 | $nl_str = array("\r","\n"); |
447 | $data = str_replace($nl_str, ' ', $data); |
7311c377 |
448 | |
449 | /** |
450 | * Make sure that entry fits into allocated record space. |
451 | * One byte is reserved for linefeed |
452 | */ |
453 | if (strlen($data) >= $this->line_length) { |
454 | return $this->set_error(_("Address book entry is too big")); |
455 | } |
456 | |
06b4facd |
457 | /* Add linefeed at end */ |
458 | $data = $data . "\n"; |
62f7daa5 |
459 | |
06b4facd |
460 | /* Reopen file, just to be sure */ |
461 | $this->open(true); |
462 | if(!$this->writeable) { |
35235328 |
463 | return $this->set_error(_("Address book is read-only")); |
06b4facd |
464 | } |
62f7daa5 |
465 | |
06b4facd |
466 | /* Lock the file */ |
467 | if(!$this->lock()) { |
468 | return $this->set_error(_("Could not lock datafile")); |
469 | } |
62f7daa5 |
470 | |
06b4facd |
471 | /* Write */ |
3ecad5e6 |
472 | $r = sq_fwrite($this->filehandle, $data); |
62f7daa5 |
473 | |
06b4facd |
474 | /* Unlock file */ |
475 | $this->unlock(); |
62f7daa5 |
476 | |
3ecad5e6 |
477 | /* Test write result */ |
478 | if($r === FALSE) { |
147e5af3 |
479 | /* Fail */ |
35235328 |
480 | $this->set_error(_("Write to address book failed")); |
147e5af3 |
481 | return FALSE; |
482 | } |
62f7daa5 |
483 | |
3ecad5e6 |
484 | return TRUE; |
06b4facd |
485 | } |
486 | |
147e5af3 |
487 | /** |
488 | * Delete address |
489 | * @param string $alias alias that has to be deleted |
490 | * @return bool |
491 | */ |
06b4facd |
492 | function remove($alias) { |
493 | if(!$this->writeable) { |
35235328 |
494 | return $this->set_error(_("Address book is read-only")); |
06b4facd |
495 | } |
62f7daa5 |
496 | |
06b4facd |
497 | /* Lock the file to make sure we're the only process working |
498 | * on it. */ |
499 | if(!$this->lock()) { |
500 | return $this->set_error(_("Could not lock datafile")); |
501 | } |
62f7daa5 |
502 | |
06b4facd |
503 | /* Read file into memory, ignoring nicknames to delete */ |
504 | @rewind($this->filehandle); |
505 | $i = 0; |
506 | $rows = array(); |
7311c377 |
507 | while($row = @fgetcsv($this->filehandle, $this->line_length, '|')) { |
06b4facd |
508 | if(!in_array($row[0], $alias)) { |
509 | $rows[$i++] = $row; |
510 | } |
511 | } |
62f7daa5 |
512 | |
06b4facd |
513 | /* Write data back */ |
514 | if(!$this->overwrite($rows)) { |
515 | $this->unlock(); |
516 | return false; |
517 | } |
62f7daa5 |
518 | |
06b4facd |
519 | $this->unlock(); |
520 | return true; |
521 | } |
522 | |
147e5af3 |
523 | /** |
524 | * Modify address |
525 | * @param string $alias modified alias |
526 | * @param array $userdata new data |
527 | * @return bool true, if operation successful |
528 | */ |
06b4facd |
529 | function modify($alias, $userdata) { |
530 | if(!$this->writeable) { |
35235328 |
531 | return $this->set_error(_("Address book is read-only")); |
06b4facd |
532 | } |
62f7daa5 |
533 | |
06b4facd |
534 | /* See if user exists */ |
535 | $ret = $this->lookup($alias); |
536 | if(empty($ret)) { |
2706a0b1 |
537 | // i18n: don't use html formating in translation |
538 | return $this->set_error(sprintf(_("User \"%s\" does not exist"),$alias)); |
06b4facd |
539 | } |
849164a1 |
540 | |
541 | /* If the alias changed, see if the new alias exists */ |
542 | if (strtolower($alias) != strtolower($userdata['nickname'])) { |
543 | $ret = $this->lookup($userdata['nickname']); |
544 | if (!empty($ret)) { |
545 | return $this->set_error(sprintf(_("User \"%s\" already exists"), $userdata['nickname'])); |
546 | } |
547 | } |
548 | |
06b4facd |
549 | /* Lock the file to make sure we're the only process working |
550 | * on it. */ |
551 | if(!$this->lock()) { |
552 | return $this->set_error(_("Could not lock datafile")); |
553 | } |
62f7daa5 |
554 | |
7311c377 |
555 | /* calculate userdata size */ |
556 | $data = $this->quotevalue($userdata['nickname']) . '|' |
557 | . $this->quotevalue($userdata['firstname']) . '|' |
558 | . $this->quotevalue((!empty($userdata['lastname'])?$userdata['lastname']:'')) . '|' |
559 | . $this->quotevalue($userdata['email']) . '|' |
560 | . $this->quotevalue((!empty($userdata['label'])?$userdata['label']:'')); |
561 | /* make sure that it fits into allocated space */ |
562 | if (strlen($data) >= $this->line_length) { |
563 | return $this->set_error(_("Address book entry is too big")); |
564 | } |
565 | |
62f7daa5 |
566 | /* Read file into memory, modifying the data for the |
06b4facd |
567 | * user identified by $alias */ |
568 | $this->open(true); |
569 | @rewind($this->filehandle); |
570 | $i = 0; |
571 | $rows = array(); |
7311c377 |
572 | while($row = @fgetcsv($this->filehandle, $this->line_length, '|')) { |
06b4facd |
573 | if(strtolower($row[0]) != strtolower($alias)) { |
574 | $rows[$i++] = $row; |
575 | } else { |
576 | $rows[$i++] = array(0 => $userdata['nickname'], |
577 | 1 => $userdata['firstname'], |
8419c13b |
578 | 2 => (!empty($userdata['lastname'])?$userdata['lastname']:''), |
62f7daa5 |
579 | 3 => $userdata['email'], |
8419c13b |
580 | 4 => (!empty($userdata['label'])?$userdata['label']:'')); |
06b4facd |
581 | } |
582 | } |
62f7daa5 |
583 | |
06b4facd |
584 | /* Write data back */ |
585 | if(!$this->overwrite($rows)) { |
586 | $this->unlock(); |
587 | return false; |
588 | } |
62f7daa5 |
589 | |
06b4facd |
590 | $this->unlock(); |
591 | return true; |
592 | } |
62f7daa5 |
593 | |
147e5af3 |
594 | /** |
595 | * Function for quoting values before saving |
596 | * @param string $value string that has to be quoted |
597 | * @param string quoted string |
598 | */ |
77ec28e9 |
599 | function quotevalue($value) { |
600 | /* Quote the field if it contains | or ". Double quotes need to |
601 | * be replaced with "" */ |
bbb2bab5 |
602 | if(stristr($value, '"') || stristr($value, '|')) { |
77ec28e9 |
603 | $value = '"' . str_replace('"', '""', $value) . '"'; |
604 | } |
605 | return $value; |
606 | } |
6b0fe53b |
607 | } |