/[imapfilter]/imapfilter/file.c
ViewVC logotype

Annotation of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.47 - (hide annotations)
Fri Feb 21 18:31:29 2003 UTC (21 years, 1 month ago) by lefcha
Branch: MAIN
Changes since 1.46: +5 -6 lines
File MIME type: text/plain
DEBUG messages go to stderr.

1 lefcha 1.1 #include <stdio.h>
2 lefcha 1.28 #include <unistd.h>
3 lefcha 1.1 #include <errno.h>
4 lefcha 1.5 #include <sys/types.h>
5 lefcha 1.1 #include <regex.h>
6     #include <string.h>
7     #include <stdlib.h>
8     #include <limits.h>
9 lefcha 1.10 #include <sys/stat.h>
10 lefcha 1.28 #include <fcntl.h>
11 lefcha 1.29 #include <time.h>
12 lefcha 1.1
13     #include "config.h"
14     #include "imapfilter.h"
15 lefcha 1.13 #include "data.h"
16 lefcha 1.1
17    
18 lefcha 1.6 extern char logfile[PATH_MAX];
19 lefcha 1.11 extern unsigned int options;
20 lefcha 1.46 extern char charset[CHARSET_LEN];
21 lefcha 1.34 extern unsigned int flags;
22     extern unsigned int interval;
23 lefcha 1.28 extern long timeout;
24 lefcha 1.29 extern char *home;
25    
26     #ifdef ENCRYPTED_PASSWORDS
27 lefcha 1.38 char *passphr; /* Master password to access the passwords
28     file. */
29 lefcha 1.28 #endif
30 lefcha 1.27
31 lefcha 1.1
32     /*
33 lefcha 1.13 * Find the path to configuration file, open it and call parse_config().
34 lefcha 1.1 */
35     int read_config(char *cfg)
36     {
37     int r;
38 lefcha 1.29 FILE *fd;
39 lefcha 1.23 char *c = NULL;
40 lefcha 1.24
41 lefcha 1.1 if (!cfg) {
42 lefcha 1.38 cfg = c = (char *)xmalloc(PATH_MAX * sizeof(char));
43 lefcha 1.1 snprintf(cfg, PATH_MAX, "%s/%s", home, ".imapfilterrc");
44     }
45     #ifdef DEBUG
46 lefcha 1.47 fprintf(stderr, "debug: configuration file: '%s'\n", cfg);
47 lefcha 1.1 #endif
48 lefcha 1.13 #ifdef CHECK_PERMISSIONS
49 lefcha 1.28 check_file_perms(cfg, S_IRUSR | S_IWUSR);
50 lefcha 1.12 #endif
51 lefcha 1.29 fd = fopen(cfg, "r");
52 lefcha 1.1
53 lefcha 1.29 if (!fd)
54 lefcha 1.17 fatal(ERROR_FILE_OPEN, "imapfilter: opening config file %s; %s\n",
55 lefcha 1.11 cfg, strerror(errno));
56 lefcha 1.1
57 lefcha 1.23 if (c)
58 lefcha 1.29 xfree(c);
59 lefcha 1.23
60 lefcha 1.29 if ((r = parse_config(fd)))
61 lefcha 1.17 fatal(ERROR_CONFIG_PARSE,
62     "imapfilter: parse error in config file at row %d\n", r);
63 lefcha 1.1
64 lefcha 1.29 fclose(fd);
65 lefcha 1.1
66 lefcha 1.11 #ifdef DEBUG
67 lefcha 1.47 fprintf(stderr, "debug: options: %0#10x '%s'\n", options, charset);
68 lefcha 1.11 #endif
69    
70 lefcha 1.17 return 0;
71 lefcha 1.1 }
72    
73 lefcha 1.6
74 lefcha 1.10 /*
75 lefcha 1.13 * Parse configuration file.
76 lefcha 1.1 */
77 lefcha 1.29 int parse_config(FILE * fd)
78 lefcha 1.1 {
79 lefcha 1.17 int i, r = 0;
80 lefcha 1.1 unsigned int row = 0;
81     char line[LINE_MAX];
82 lefcha 1.27 regex_t creg[13];
83 lefcha 1.39 regmatch_t match[11];
84 lefcha 1.27 const char *reg[13] = {
85 lefcha 1.15 "^([[:blank:]]*\n|#.*\n)$",
86 lefcha 1.36
87     "^[[:blank:]]*ACCOUNT[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
88     "(([[:graph:]]+):([[:graph:]]*)|([[:graph:]]+))@([[:alnum:].-]+)"
89     "(:[[:digit:]]+)?[[:blank:]]*([[:blank:]]SSL|[[:blank:]]SSL2|"
90     "[[:blank:]]SSL3|[[:blank:]]TLS1)?[[:blank:]]*\n$",
91    
92     "^[[:blank:]]*FOLDER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
93     "([[:print:]]+)[[:blank:]]*\n$",
94    
95     "^[[:blank:]]*FILTER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]*"
96     "([[:blank:]]OR|[[:blank:]]AND)?[[:blank:]]*\n$",
97    
98     "^[[:blank:]]*ACTION[[:blank:]]+(DELETE|"
99     "COPY[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
100     "MOVE[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
101     "RCOPY[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
102     "(\"[[:print:]]*\"|[[:graph:]]+)|"
103     "RMOVE[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
104     "(\"[[:print:]]*\"|[[:graph:]]+)|"
105 lefcha 1.39 "FLAG[[:blank:]]+(REPLACE|ADD|REMOVE)[[:blank:]]+"
106     "([[:alpha:],]+)|"
107 lefcha 1.36 "LIST)[[:blank:]]*([[:graph:]]*)[[:blank:]]*\n$",
108    
109     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
110     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
111     "(ANSWERED|DELETED|DRAFT|FLAGGED|NEW|OLD|RECENT|SEEN|UNANSWERED|"
112     "UNDELETED|UNDRAFT|UNFLAGGED|UNSEEN)[[:blank:]]*\n$",
113    
114     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
115     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
116     "(BCC|BODY|CC|FROM|SUBJECT|TEXT|TO)[[:blank:]]+(\"[[:print:]]*\"|"
117     "[[:graph:]]+)[[:blank:]]*\n$",
118    
119     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
120     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
121     "(HEADER)[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]+"
122     "(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$",
123    
124     "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
125     "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
126     "(LARGER|SMALLER|OLDER|NEWER)[[:blank:]]+([[:digit:]]+)"
127     "[[:blank:]]*\n$",
128    
129     "^[[:blank:]]*JOB[[:blank:]]+([[:alnum:],_-]+)[[:blank:]]+"
130     "([[:alnum:],_-]+)[[:blank:]]*\n$",
131    
132 lefcha 1.46 "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(LOGFILE|CHARSET)"
133     "[[:blank:]]*=[[:blank:]]*(\"[[:print:]]*\"|[[:graph:]]+)"
134     "[[:blank:]]*\n$",
135 lefcha 1.36
136 lefcha 1.44 "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(ERRORS|EXPUNGE|HEADERS|"
137     "NAMESPACE|SUBSCRIBE|WARNING)[[:blank:]]*=[[:blank:]]*(YES|NO)"
138     "[[:blank:]]*\n$",
139 lefcha 1.36
140     "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(DAEMON|TIMEOUT)"
141     "[[:blank:]]*=[[:blank:]]*([[:digit:]]+)\n$"
142 lefcha 1.1 };
143    
144 lefcha 1.27 for (i = 0; i < 13; i++)
145 lefcha 1.14 regcomp(&creg[i], reg[i], REG_EXTENDED | REG_ICASE);
146 lefcha 1.1
147 lefcha 1.43 /* First process all the variables. This is done because some variables'
148     arguments are used before the other commands are processed. */
149     while (fgets(line, LINE_MAX - 1, fd))
150     if (!regexec(&creg[10], line, 4, match, 0) ||
151     !regexec(&creg[11], line, 4, match, 0) ||
152     !regexec(&creg[12], line, 4, match, 0))
153     set_options(line, match);
154    
155     /* Then rewind and process everything else. */
156     fseek(fd, 0L, SEEK_SET);
157 lefcha 1.29 while (fgets(line, LINE_MAX - 1, fd)) {
158 lefcha 1.1 row++;
159 lefcha 1.15 if (!regexec(&creg[0], line, 0, match, 0))
160     continue;
161 lefcha 1.36 else if (!regexec(&creg[1], line, 9, match, 0))
162 lefcha 1.13 set_account(line, match);
163 lefcha 1.15 else if (!regexec(&creg[2], line, 3, match, 0))
164 lefcha 1.17 r = set_mboxgrp(line, match);
165 lefcha 1.15 else if (!regexec(&creg[3], line, 3, match, 0))
166 lefcha 1.17 r = set_filter(line, match);
167 lefcha 1.39 else if (!regexec(&creg[4], line, 11, match, 0))
168 lefcha 1.17 r = set_action(line, match);
169 lefcha 1.42 else if (!regexec(&creg[5], line, 5, match, 0))
170     r = set_mask(line, match, MASK_MATCH_1);
171     else if (!regexec(&creg[6], line, 6, match, 0))
172     r = set_mask(line, match, MASK_MATCH_2);
173     else if (!regexec(&creg[7], line, 7, match, 0))
174     r = set_mask(line, match, MASK_MATCH_3);
175     else if (!regexec(&creg[8], line, 6, match, 0))
176     r = set_mask(line, match, MASK_MATCH_4);
177 lefcha 1.15 else if (!regexec(&creg[9], line, 3, match, 0))
178 lefcha 1.17 r = set_job(line, match);
179 lefcha 1.43 /* Skip variable processing. */
180 lefcha 1.26 else if (!regexec(&creg[10], line, 4, match, 0) ||
181 lefcha 1.27 !regexec(&creg[11], line, 4, match, 0) ||
182 lefcha 1.43 !regexec(&creg[12], line, 4, match, 0));
183 lefcha 1.17 else
184 lefcha 1.18 return row;
185    
186 lefcha 1.17 if (r == ERROR_CONFIG_PARSE)
187     return row;
188 lefcha 1.1 }
189 lefcha 1.3
190 lefcha 1.27 for (i = 0; i < 13; i++)
191 lefcha 1.12 regfree(&creg[i]);
192 lefcha 1.18
193 lefcha 1.15 destroy_data();
194 lefcha 1.6
195 lefcha 1.8 return 0;
196 lefcha 1.1 }
197    
198    
199     /*
200 lefcha 1.13 * Set other options found in config file.
201 lefcha 1.11 */
202     void set_options(char *line, regmatch_t * match)
203     {
204 lefcha 1.41 if (!strncasecmp(line + match[2].rm_so, "logfile", 7)) {
205 lefcha 1.46 if (!*logfile) {
206     if (*(line + match[3].rm_so) == '"' &&
207     *(line + match[3].rm_eo - 1) == '"')
208     strncat(logfile, line + match[3].rm_so + 1,
209     min((match[3].rm_eo - match[3].rm_so - 2),
210     PATH_MAX - 1));
211     else
212     strncat(logfile, line + match[3].rm_so,
213     min((match[3].rm_eo - match[3].rm_so),
214     PATH_MAX - 1));
215     }
216     } else if (!strncasecmp(line + match[2].rm_so, "charset", 7)) {
217     if (*(line + match[3].rm_so) == '"' &&
218     *(line + match[3].rm_eo - 1) == '"')
219     strncat(charset, line + match[3].rm_so + 1,
220     min((match[3].rm_eo - match[3].rm_so - 2),
221     CHARSET_LEN - 1));
222     else
223     strncat(charset, line + match[3].rm_so,
224     min((match[3].rm_eo - match[3].rm_so), CHARSET_LEN - 1));
225 lefcha 1.44 } else if (!strncasecmp(line + match[2].rm_so, "errors", 6)) {
226     if (!strncasecmp(line + match[3].rm_so, "yes", 3))
227     options |= OPTION_ERRORS;
228     else
229     options &= ~(OPTION_ERRORS);
230 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "expunge", 7)) {
231     if (!strncasecmp(line + match[3].rm_so, "yes", 3))
232 lefcha 1.40 options |= OPTION_EXPUNGE;
233     else
234     options &= ~(OPTION_EXPUNGE);
235 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "header", 6)) {
236     if (!strncasecmp(line + match[3].rm_so, "yes", 3))
237 lefcha 1.19 options |= OPTION_HEADERS;
238 lefcha 1.25 else
239     options &= ~(OPTION_HEADERS);
240 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "namespace", 9)) {
241     if (!strncasecmp(line + match[3].rm_so, "yes", 3))
242 lefcha 1.28 options |= OPTION_NAMESPACE;
243     else
244     options &= ~(OPTION_NAMESPACE);
245 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "subscribe", 9)) {
246 lefcha 1.43 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
247 lefcha 1.40 options |= OPTION_SUBSCRIBE;
248 lefcha 1.38 else
249 lefcha 1.40 options &= ~(OPTION_SUBSCRIBE);
250 lefcha 1.43 } else if (!strncasecmp(line + match[2].rm_so, "warning", 7)) {
251     if (!strncasecmp(line + match[3].rm_so, "yes", 3))
252     options |= OPTION_WARNING;
253     else
254     options &= ~(OPTION_WARNING);
255 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "timeout", 7)) {
256 lefcha 1.27 errno = 0;
257     timeout = strtol(line + match[3].rm_so, NULL, 10);
258     if (errno)
259     timeout = 0;
260 lefcha 1.41 } else if (!strncasecmp(line + match[2].rm_so, "daemon", 6) &&
261 lefcha 1.34 !(options & OPTION_DAEMON_MODE)) {
262     options |= OPTION_DAEMON_MODE;
263     errno = 0;
264     interval = strtoul(line + match[3].rm_so, NULL, 10);
265     if (errno)
266     interval = 0;
267 lefcha 1.19 }
268 lefcha 1.1 }
269 lefcha 1.29
270    
271     #ifdef ENCRYPTED_PASSWORDS
272     /*
273     * Open password file and call parse_passwords().
274     */
275     int read_passwords(void)
276     {
277     FILE *fd;
278     char pwfile[PATH_MAX];
279 lefcha 1.38
280 lefcha 1.34 if (!(flags & FLAG_BLANK_PASSWORD))
281 lefcha 1.36 return ERROR_CONFIG_PARSE;
282 lefcha 1.38
283     passphr = (char *)smalloc(PASSPHRASE_LEN);
284 lefcha 1.37 *passphr = 0;
285 lefcha 1.29
286     snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
287     #ifdef DEBUG
288 lefcha 1.47 fprintf(stderr, "debug: passwords file: '%s'\n", pwfile);
289 lefcha 1.29 #endif
290 lefcha 1.38
291 lefcha 1.29 if (!exists_file(pwfile))
292 lefcha 1.36 return ERROR_FILE_OPEN;
293 lefcha 1.38
294 lefcha 1.29 #ifdef CHECK_PERMISSIONS
295     check_file_perms(pwfile, S_IRUSR | S_IWUSR);
296     #endif
297    
298     fd = fopen(pwfile, "r");
299    
300     if (!fd)
301     fatal(ERROR_FILE_OPEN, "imapfilter: opening passwords file %s; %s\n",
302     pwfile, strerror(errno));
303    
304     parse_passwords(fd);
305    
306     fclose(fd);
307    
308     return 0;
309     }
310    
311    
312     /*
313     * Parse unencrypted password file.
314     */
315     int parse_passwords(FILE * fd)
316     {
317     int t = 3;
318     char *pe = NULL;
319     char user[USERNAME_LEN], serv[SERVER_LEN];
320     unsigned char *buf;
321     char *c, *cp, *line;
322     regex_t creg;
323     regmatch_t match[4];
324     const char *reg = "([[:alnum:].-]+) ([[:graph:]]+) ([[:graph:]]+)";
325 lefcha 1.36 int r;
326 lefcha 1.29
327     do {
328 lefcha 1.36 fseek(fd, 0L, SEEK_SET);
329 lefcha 1.29 printf("Enter master passphrase: ");
330     get_password(passphr, PASSPHRASE_LEN);
331 lefcha 1.36 } while ((r = decrypt_passwords(&buf, fd)) && --t);
332 lefcha 1.38
333 lefcha 1.29 if (!t)
334     return ERROR_PASSPHRASE;
335    
336 lefcha 1.31 c = cp = sstrdup(buf);
337 lefcha 1.38
338 lefcha 1.29 regcomp(&creg, reg, REG_EXTENDED | REG_ICASE);
339    
340 lefcha 1.45 line = strtok_r(c, "\n", &c);
341     while (line && !regexec(&creg, line, 4, match, 0)) {
342 lefcha 1.29 user[0] = serv[0] = 0;
343 lefcha 1.38
344 lefcha 1.29 strncat(serv, line + match[1].rm_so,
345     min(match[1].rm_eo - match[1].rm_so, SERVER_LEN - 1));
346     strncat(user, line + match[2].rm_so,
347     min(match[2].rm_eo - match[2].rm_so, USERNAME_LEN - 1));
348 lefcha 1.38
349     if ((pe = (char *)find_password(user, serv)))
350 lefcha 1.29 strncat(pe, line + match[3].rm_so,
351     min(match[3].rm_eo - match[3].rm_so, PASSWORD_LEN - 1));
352 lefcha 1.45
353     line = strtok_r(NULL, "\n", &c);
354 lefcha 1.29 }
355 lefcha 1.38
356 lefcha 1.29 regfree(&creg);
357 lefcha 1.38 sfree(cp);
358 lefcha 1.31 sfree(buf);
359 lefcha 1.29
360     return 0;
361     }
362    
363    
364     /*
365     * Store encrypted passwords to file.
366     */
367     int store_passwords(account_t * accts[])
368     {
369     char pwfile[PATH_MAX];
370     FILE *fd;
371    
372     snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
373 lefcha 1.38
374 lefcha 1.32 create_file(pwfile, S_IRUSR | S_IWUSR);
375 lefcha 1.29
376     fd = fopen(pwfile, "w");
377    
378     if (!fd)
379     fatal(ERROR_FILE_OPEN, "imapfilter: opening passwords file %s; %s\n",
380     pwfile, strerror(errno));
381 lefcha 1.38
382 lefcha 1.29 encrypt_passwords(fd, accts);
383 lefcha 1.38
384 lefcha 1.29 fclose(fd);
385    
386     return 0;
387     }
388 lefcha 1.47 #endif /* ENCRYPTED_PASSWORDS */
389 lefcha 1.31
390    
391     /*
392     * Create $HOME/.imapfilter directory.
393     */
394     int create_homedir(void)
395     {
396 lefcha 1.33 char *hdn = ".imapfilter";
397 lefcha 1.38
398 lefcha 1.31 if (home)
399     if (chdir(home))
400     error("imapfilter: could not change directory; %s\n",
401     strerror(errno));
402    
403 lefcha 1.36 if (!exists_dir(hdn))
404     if (mkdir(hdn, S_IRUSR | S_IWUSR | S_IXUSR))
405     error("imapfilter: could not create directory %s; %s\n", hdn,
406     strerror(errno));
407    
408 lefcha 1.31 return 0;
409     }
410    
411    
412     /*
413     * Check if a file exists.
414     */
415     int exists_file(char *fname)
416     {
417     struct stat fs;
418 lefcha 1.38
419 lefcha 1.31 if (access(fname, F_OK))
420     return 0;
421 lefcha 1.38
422 lefcha 1.31 stat(fname, &fs);
423     if (!S_ISREG(fs.st_mode)) {
424     error("imapfilter: file %s not a regular file\n", fname);
425     return ERROR_FILE_OPEN;
426     }
427     return 1;
428     }
429    
430    
431 lefcha 1.38 /*
432 lefcha 1.31 * Check if a directory exists.
433     */
434     int exists_dir(char *dname)
435     {
436     struct stat ds;
437 lefcha 1.38
438 lefcha 1.31 if (access(dname, F_OK))
439     return 0;
440 lefcha 1.38
441 lefcha 1.31 stat(dname, &ds);
442     if (!S_ISDIR(ds.st_mode)) {
443     error("imapfilter: file %s not a directory\n", dname);
444     return ERROR_FILE_OPEN;
445     }
446     return 1;
447     }
448    
449    
450     /*
451 lefcha 1.36 * Create a file with the specified permissions.
452 lefcha 1.31 */
453     int create_file(char *fname, mode_t mode)
454     {
455     int fd = 0;
456 lefcha 1.38
457 lefcha 1.33 if (!exists_file(fname)) {
458     fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, mode);
459 lefcha 1.31 if (fd == -1) {
460     error("imapfilter: could not create file %s; %s\n", fname,
461     strerror(errno));
462     return ERROR_FILE_OPEN;
463     }
464     close(fd);
465 lefcha 1.33 }
466 lefcha 1.31 return 0;
467     }
468    
469    
470     #ifdef CHECK_PERMISSIONS
471     /*
472     * Check the permissions of a file.
473     */
474     int check_file_perms(char *fname, mode_t mode)
475     {
476     struct stat fs;
477 lefcha 1.38
478 lefcha 1.31 if (stat(fname, &fs)) {
479     error("imapfilter: getting file %s status; %s\n", fname,
480     strerror(errno));
481     return ERROR_TRIVIAL;
482     }
483     if (!S_ISREG(fs.st_mode)) {
484     error("imapfilter: file %s not a regular file\n", fname);
485     return ERROR_TRIVIAL;
486     }
487     if ((fs.st_mode & 00777) != mode) {
488     error("imapfilter: warning: improper file %s permissions\n"
489     "imapfilter: warning: file's mode should be %o not %o\n",
490     fname, mode, fs.st_mode & 00777);
491     return ERROR_TRIVIAL;
492     }
493     return 0;
494     }
495    
496    
497     /*
498     * Check the permissions of a directory.
499     */
500     int check_dir_perms(char *dname, mode_t mode)
501     {
502     struct stat ds;
503    
504     if (stat(dname, &ds)) {
505     error("imapfilter: getting file %s status; %s\n", dname,
506     strerror(errno));
507     return ERROR_TRIVIAL;
508     }
509     if (!S_ISDIR(ds.st_mode)) {
510     error("imapfilter: file %s not a directory\n", dname);
511     return ERROR_TRIVIAL;
512     }
513     if ((ds.st_mode & 00777) != mode) {
514     error("imapfilter: warning: improper dir %s permissions\n"
515     "imapfilter: warning: file's mode should be %o not %o\n",
516     dname, mode, ds.st_mode & 00777);
517     return ERROR_TRIVIAL;
518     }
519     return 0;
520     }
521 lefcha 1.38
522 lefcha 1.47 #endif /* CHECK_PERMISSIONS */

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26