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

Contents of /imapfilter/data.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.25 - (show annotations)
Tue Jun 18 20:56:23 2002 UTC (21 years, 10 months ago) by lefcha
Branch: MAIN
Changes since 1.24: +75 -29 lines
File MIME type: text/plain
Added alias/key to accounts and rcopy/rmove action.

1 #include <stdio.h>
2 #include <errno.h>
3 #include <sys/types.h>
4 #include <regex.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <limits.h>
8 #include <sys/stat.h>
9 #include <ctype.h>
10 #include <time.h>
11
12 #include "config.h"
13 #include "imapfilter.h"
14 #include "data.h"
15
16
17 extern unsigned int options;
18 extern unsigned int flags;
19
20 account_t *accounts = NULL; /* First node of accounts linked list. */
21 filter_t *filters = NULL; /* First node of filters tree. */
22
23 static mboxgrp_t *mboxgrps = NULL; /* First node of mailbox-groups
24 tree. */
25
26 static account_t *cur_acct = NULL; /* Current account. */
27 static filter_t *cur_fltr = NULL; /* Current filter. */
28
29
30 /*
31 * Set new account's variables to safe values.
32 */
33 void init_account(account_t * node)
34 {
35 node->next = NULL;
36 node->key[0] = node->server[0] = node->username[0] = node->password[0] = 0;
37 node->passwdattr = PASSWORD_NONE;
38 node->port = 143;
39 node->ssl = SSL_DISABLED;
40 node->mboxes = NULL;
41 }
42
43
44 /*
45 * Append account node to linked list.
46 */
47 void append_account(account_t * node)
48 {
49 account_t *pos;
50 account_t **app;
51
52 APPEND_LINKED_LIST(accounts, node, pos, app);
53 }
54
55
56 /*
57 * A new account entry was declared. Create it and set it's variables
58 * accordingly.
59 */
60 int set_account(char *line, regmatch_t * match)
61 {
62 int n;
63 char p[6];
64 account_t *node;
65
66 node = (account_t *) create_node(sizeof(account_t));
67 node->password = (char *) smalloc(PASSWORD_LEN);
68
69 init_account(node);
70
71 strncat(node->key, line + match[1].rm_so,
72 min(match[1].rm_eo - match[1].rm_so, KEY_LEN - 1));
73 #ifdef DEBUG
74 printf("debug: ACCOUNT: '%s'\n", node->key);
75 #endif
76
77 if (match[3].rm_so != -1) {
78 strncat(node->username, line + match[3].rm_so,
79 min(match[3].rm_eo - match[3].rm_so, USERNAME_LEN - 1));
80 } else {
81 strncat(node->username, line + match[5].rm_so,
82 min(match[5].rm_eo - match[5].rm_so, USERNAME_LEN - 1));
83 }
84 if (strchr(node->username, '%'))
85 if (string_decode(node->username))
86 return ERROR_CONFIG_PARSE;
87 #ifdef DEBUG
88 printf("debug: USERNAME: '%s'\n", node->username);
89 #endif
90
91 if (match[4].rm_so != -1) {
92 strncat(node->password, line + match[4].rm_so,
93 min(match[4].rm_eo - match[4].rm_so, PASSWORD_LEN - 1));
94 if (strchr(node->password, '%'))
95 if (string_decode(node->password))
96 return ERROR_CONFIG_PARSE;
97 node->passwdattr = PASSWORD_PLAIN;
98 } else
99 flags |= FLAG_BLANK_PASSWORD;
100 #ifdef DEBUG
101 printf("debug: PASSWORD: '%s'\n", node->password);
102 #endif
103
104 strncat(node->server, line + match[6].rm_so,
105 min(match[6].rm_eo - match[6].rm_so, SERVER_LEN - 1));
106 #ifdef DEBUG
107 printf("debug: SERVER: '%s'\n", node->server);
108 #endif
109
110 if (match[7].rm_so != -1) {
111 n = min(match[7].rm_eo - match[7].rm_so - 1, 5);
112 xstrncpy(p, line + match[7].rm_so + 1, n);
113 p[n] = 0;
114 node->port = strtoul(p, NULL, 10);
115 #ifdef DEBUG
116 printf("debug: PORT: %d\n", node->port);
117 #endif
118 }
119 if (match[8].rm_so != -1) {
120 if (match[7].rm_so == -1)
121 node->port = 993;
122
123 if (strcasestr(line + match[8].rm_so, "ssl3"))
124 node->ssl = SSL_SSL_V3;
125 else if (strcasestr(line + match[8].rm_so, "tls1"))
126 node->ssl = SSL_TLS_V1;
127 else
128 node->ssl = SSL_SSL_V2;
129 }
130 append_account(node);
131 cur_acct = node;
132
133 return 0;
134 }
135
136
137 #ifdef ENCRYPTED_PASSWORDS
138 /*
139 * Find accounts without a password (candicates for password encryption).
140 */
141 char *find_password(char *user, char *serv)
142 {
143 account_t *a;
144
145 for (a = accounts; a; a = a->next)
146 if (a->passwdattr == PASSWORD_NONE && !strcmp(a->server, serv) &&
147 !strcmp(a->username, user)) {
148 a->passwdattr = PASSWORD_ENCRYPTED;
149 return a->password;
150 }
151 return NULL;
152 }
153 #endif
154
155 /*
156 * Set new mailbox-group's variables to safe values.
157 */
158 void init_mboxgrp(mboxgrp_t * node)
159 {
160 node->left = node->right = NULL;
161 node->key[0] = 0;
162 node->mboxes[0] = NULL;
163 }
164
165
166 /*
167 * Insert mailbox-group node in tree.
168 */
169 void insert_mboxgrp(mboxgrp_t * node)
170 {
171 int cmp;
172 mboxgrp_t *pos;
173 mboxgrp_t **ins;
174
175 INSERT_TREE(mboxgrps, node, pos, ins, cmp);
176 }
177
178
179 /*
180 * A new mailbox-group entry was declared. Create it and set it's variables
181 * accordingly.
182 */
183 int set_mboxgrp(char *line, regmatch_t * match)
184 {
185 mboxgrp_t *node;
186 char mboxs[LINE_MAX];
187
188 mboxs[0] = 0;
189
190 if (!accounts)
191 return ERROR_CONFIG_PARSE;
192
193 node = (mboxgrp_t *) create_node(sizeof(mboxgrp_t));
194
195 init_mboxgrp(node);
196
197 strncat(node->key, line + match[1].rm_so,
198 min(match[1].rm_eo - match[1].rm_so, KEY_LEN - 1));
199
200 #ifdef DEBUG
201 printf("debug: FOLDER: '%s'\n", node->key);
202 #endif
203
204 strncat(mboxs, line + match[2].rm_so,
205 min(match[2].rm_eo - match[2].rm_so, LINE_MAX - 1));
206
207 process_mboxgrp(node, mboxs);
208
209 insert_mboxgrp(node);
210
211 return 0;
212 }
213
214
215 /*
216 * Calls set_mbox() in order to create mailboxes that are part of
217 * the mailbox-group.
218 */
219 void process_mboxgrp(mboxgrp_t * node, char *mboxs)
220 {
221 unsigned int i = 0;
222 char *tok;
223
224 while (i < MBOXGRP_MBOXES_MAX - 1 && (tok = strsep(&mboxs, ","))) {
225 node->mboxes[i] = (mbox_t *) set_mbox(tok);
226 node->mboxes[++i] = NULL;
227 }
228 }
229
230
231 /*
232 * Find in the mailbox-group tree, the node with the specified key,
233 * and return a pointer to it.
234 */
235 mboxgrp_t *find_mboxgrp(char *key)
236 {
237 int cmp;
238 mboxgrp_t *pos;
239
240 FIND_TREE(mboxgrps, key, pos, cmp);
241 }
242
243
244 /*
245 * Set new mailbox's variables to safe values.
246 */
247 void init_mbox(mbox_t * node)
248 {
249 node->next = NULL;
250 node->name[0] = 0;
251 node->filters[0] = NULL;
252 }
253
254
255 /*
256 * Append mailbox node to linked list.
257 */
258 void append_mbox(mbox_t * node)
259 {
260 mbox_t *pos;
261 mbox_t **ins;
262
263 APPEND_LINKED_LIST(cur_acct->mboxes, node, pos, ins);
264 }
265
266
267 /*
268 * A new mailbox was declared, create it and set it's variables accordingly.
269 */
270 mbox_t *set_mbox(char *name)
271 {
272 mbox_t *node;
273
274 node = (mbox_t *) create_node(sizeof(mbox_t));
275
276 init_mbox(node);
277
278 if (*name == '"' && *(name + strlen(name) - 1) == '"')
279 strncat(node->name, name + 1, min(strlen(name) - 2, MBOX_NAME_LEN - 1));
280 else
281 strncat(node->name, name, min(strlen(name), MBOX_NAME_LEN - 1));
282
283 #ifdef DEBUG
284 printf("debug: MBOX: '%s'\n", node->name);
285 #endif
286
287 append_mbox(node);
288
289 return node;
290 }
291
292
293 /*
294 * Set new filter's variables to safe values.
295 */
296 void init_filter(filter_t * node)
297 {
298 node->left = node->right = NULL;
299 node->key[0] = 0;
300 node->mode = FILTER_MODE_AND;
301 node->action.type = 0;
302 node->action.raccount = NULL;
303 node->action.destmbox[0] = node->action.args[0] = 0;
304 node->masks = NULL;
305 node->masknum = node->ormasknum = node->masklen = 0;
306 }
307
308
309 /*
310 * Insert filter node to tree.
311 */
312 void insert_filter(filter_t * node)
313 {
314 int cmp;
315 filter_t *pos;
316 filter_t **ins;
317
318 INSERT_TREE(filters, node, pos, ins, cmp);
319 }
320
321
322 /*
323 * A filter entry was declared, create it and set it's variables accordingly.
324 */
325 int set_filter(char *line, regmatch_t * match)
326 {
327 filter_t *node;
328
329 if (cur_fltr && !cur_fltr->action.type)
330 return ERROR_CONFIG_PARSE;
331
332 node = (filter_t *) create_node(sizeof(filter_t));
333
334 init_filter(node);
335
336 strncat(node->key, line + match[1].rm_so,
337 min(match[1].rm_eo - match[1].rm_so, KEY_LEN - 1));
338
339 if (match[2].rm_so != -1) {
340 if (!strncasecmp(line + match[2].rm_so + 1, "or", 2))
341 node->mode = FILTER_MODE_OR;
342 else
343 node->mode = FILTER_MODE_AND;
344 }
345 #ifdef DEBUG
346 printf("debug: FILTER: '%s' %s\n", node->key,
347 (node->mode == FILTER_MODE_OR ? "OR" : "AND"));
348 #endif
349
350 insert_filter(node);
351 cur_fltr = node;
352
353 return 0;
354 }
355
356
357 /*
358 * Find in the filter tree, the node with the specified key and
359 * return a pointer to it.
360 */
361 filter_t *find_filter(char *key)
362 {
363 int cmp;
364 filter_t *pos;
365
366 FIND_TREE(filters, key, pos, cmp);
367 }
368
369
370 /*
371 * Assign an action to the last declared filter.
372 */
373 int set_action(char *line, regmatch_t * match)
374 {
375 char *c;
376 account_t *a;
377
378 if (!cur_fltr)
379 return ERROR_CONFIG_PARSE;
380
381 if (!strncasecmp(line + match[1].rm_so, "delete", 6))
382 cur_fltr->action.type = FILTER_ACTION_DELETE;
383 else if (!strncasecmp(line + match[1].rm_so, "copy", 4)) {
384 cur_fltr->action.type = FILTER_ACTION_COPY;
385 if (*(line + match[2].rm_so) == '"' && *(line + match[2].rm_eo - 1) == '"')
386 strncat(cur_fltr->action.destmbox, line + match[2].rm_so + 1,
387 min(match[2].rm_eo - match[2].rm_so - 2, MBOX_NAME_LEN - 1));
388 else
389 strncat(cur_fltr->action.destmbox, line + match[2].rm_so,
390 min(match[2].rm_eo - match[2].rm_so, MBOX_NAME_LEN - 1));
391 } else if (!strncasecmp(line + match[1].rm_so, "move", 4)) {
392 cur_fltr->action.type = FILTER_ACTION_MOVE;
393 if (*(line + match[3].rm_so) == '"' && *(line + match[3].rm_eo - 1) == '"')
394 strncat(cur_fltr->action.destmbox, line + match[3].rm_so + 1,
395 min(match[3].rm_eo - match[3].rm_so - 2, MBOX_NAME_LEN - 1));
396 else
397 strncat(cur_fltr->action.destmbox, line + match[3].rm_so,
398 min(match[3].rm_eo - match[3].rm_so, MBOX_NAME_LEN - 1));
399 } else if (!strncasecmp(line + match[1].rm_so, "rcopy", 5)) {
400 cur_fltr->action.type = FILTER_ACTION_RCOPY;
401 for (a = accounts; a; a = a->next)
402 if (!strncasecmp(line + match[4].rm_so, a->key, strlen(a->key)))
403 cur_fltr->action.raccount = a;
404 if (!cur_fltr->action.raccount)
405 return ERROR_CONFIG_PARSE;
406
407 if (*(line + match[5].rm_so) == '"' && *(line + match[5].rm_eo - 1) == '"')
408 strncat(cur_fltr->action.destmbox, line + match[5].rm_so + 1,
409 min(match[5].rm_eo - match[5].rm_so - 2, MBOX_NAME_LEN - 1));
410 else
411 strncat(cur_fltr->action.destmbox, line + match[5].rm_so,
412 min(match[5].rm_eo - match[5].rm_so, MBOX_NAME_LEN - 1));
413 } else if (!strncasecmp(line + match[1].rm_so, "rmove", 5)) {
414 cur_fltr->action.type = FILTER_ACTION_RMOVE;
415 for (a = accounts; a; a = a->next)
416 if (!strncasecmp(line + match[6].rm_so, a->key, strlen(a->key)))
417 cur_fltr->action.raccount = a;
418 if (!cur_fltr->action.raccount)
419 return ERROR_CONFIG_PARSE;
420
421 if (*(line + match[7].rm_so) == '"' && *(line + match[7].rm_eo - 1) == '"')
422 strncat(cur_fltr->action.destmbox, line + match[7].rm_so + 1,
423 min(match[7].rm_eo - match[7].rm_so - 2, MBOX_NAME_LEN - 1));
424 else
425 strncat(cur_fltr->action.destmbox, line + match[7].rm_so,
426 min(match[7].rm_eo - match[7].rm_so, MBOX_NAME_LEN - 1));
427 } else if (!strncasecmp(line + match[1].rm_so, "list", 4))
428 cur_fltr->action.type = FILTER_ACTION_LIST;
429
430 if (match[8].rm_so != -1) {
431 strncat(cur_fltr->action.args, line + match[8].rm_so,
432 min(match[8].rm_eo - match[8].rm_so, ARGS_LEN - 2));
433 while ((c = strchr(cur_fltr->action.args, ',')))
434 *c = ' ';
435 }
436
437 #ifdef DEBUG
438 printf("debug: ACTION: %d '%s' '%s' '%s'\n", cur_fltr->action.type,
439 cur_fltr->action.raccount->key, cur_fltr->action.destmbox,
440 cur_fltr->action.args);
441 #endif
442 return 0;
443
444 }
445
446
447 /*
448 * Set new mask's variables to safe values.
449 */
450 void init_mask(mask_t * node)
451 {
452 node->next = NULL;
453 node->body[0] = 0;
454 node->type = 0;
455 }
456
457
458 /*
459 * Append mask node to linked list.
460 */
461 void append_mask(mask_t * node)
462 {
463 mask_t *pos;
464 mask_t **app;
465
466 APPEND_LINKED_LIST(cur_fltr->masks, node, pos, app);
467 }
468
469
470 /*
471 * A new mask entry was declared, create it and set it's
472 * variables accordingly.
473 */
474 int set_mask(char *line, regmatch_t * match)
475 {
476 int n, i, f = 0;
477 mask_t *node;
478 char *bp;
479
480 if (!cur_fltr)
481 return ERROR_CONFIG_PARSE;
482
483 node = (mask_t *) create_node(sizeof(mask_t));
484
485 init_mask(node);
486
487 bp = node->body;
488
489 /* If specified set mask's type. */
490 if (match[2].rm_so != -1 && cur_fltr->masks) {
491 if (!strncasecmp(line + match[2].rm_so, "or", 2)) {
492 node->type = MASK_TYPE_OR;
493 if ((cur_fltr->mode = FILTER_MODE_OR))
494 cur_fltr->ormasknum++;
495 } else
496 node->type = MASK_TYPE_AND;
497 }
498 /* Add NOT if specified. */
499 if (match[3].rm_so != -1) {
500 n = min(match[3].rm_eo - match[3].rm_so,
501 MASK_BODY_LEN - (bp - node->body) - 1);
502 xstrncpy(bp, line + match[3].rm_so, n);
503 string_upper(bp, n);
504 *(bp + n - 1) = ' '; /* In case it's '\t'. */
505 *(bp + n) = 0;
506 bp += n;
507 }
508 /* Keyword of the search key. */
509 n = min(match[4].rm_eo - match[4].rm_so,
510 MASK_BODY_LEN - (bp - node->body) - 3);
511 xstrncpy(bp, line + match[4].rm_so, n);
512 string_upper(bp, n);
513 *(bp + n) = 0;
514 bp += n;
515
516 /* Body of the search key (string/number). */
517 for (i = 5; i <= 6; i++)
518 if (match[i].rm_so != -1) {
519 *(bp++) = ' ';
520
521 /* Add '"' if not supplied and search key not a number. */
522 if (match[6].rm_so == -1 &&
523 (strstr(node->body, "LARGER") ||
524 strstr(node->body, "SMALLER") ||
525 strstr(node->body, "OLDER") ||
526 strstr(node->body, "NEWER")))
527 f = 1;
528 else if (*(line + match[i].rm_so) != '"')
529 *(bp++) = '"';
530
531 *bp = 0;
532
533 n = min(match[i].rm_eo - match[i].rm_so,
534 MASK_BODY_LEN - (bp - node->body) - 2);
535 xstrncpy(bp, line + match[i].rm_so, n);
536 *(bp + n) = 0;
537 bp += n;
538
539 if (*(line + match[i].rm_so) != '"' && !f)
540 *(bp++) = '"';
541 *bp = 0;
542 }
543 if (f && (strstr(node->body, "OLDER") || strstr(node->body, "NEWER"))) {
544 convert_date(node);
545 bp = node->body + strlen(node->body);
546 }
547 append_mask(node);
548
549 cur_fltr->masknum++;
550 cur_fltr->masklen += (bp - node->body);
551
552
553 #ifdef DEBUG
554 printf("debug: MASK: '%s'\n", node->body);
555 #endif
556
557 return 0;
558 }
559
560
561 /*
562 * Converts masks related to date filtering, because IMAP servers do not
563 * understand for example "OLDER 3", but "BEFORE 18-Oct-2001" (if
564 * hypothetically current date was 21-Oct-2001).
565 */
566 void convert_date(mask_t * node)
567 {
568 char *cp, *c;
569 char s[16];
570 time_t t;
571 struct tm *bt;
572
573 cp = xstrdup(node->body);
574 node->body[0] = 0;
575
576 if (strstr(cp, "NOT"))
577 strncat(node->body, "NOT ", 4);
578
579 if ((c = strstr(cp, "OLDER")))
580 strncat(node->body, "BEFORE ", 7);
581 else if ((c = strstr(cp, "NEWER")))
582 strncat(node->body, "SINCE ", 6);
583
584 c += 6;
585
586 t = time(NULL) - (time_t) (strtoul(c, NULL, 10) * 24 * 60 * 60);
587 bt = localtime(&t);
588
589 if (strftime(s, 15, "%d-%b-%Y", bt))
590 strncat(node->body, s, 15);
591
592 xfree(cp);
593 }
594
595
596 /*
597 * A new job was declared, link filters with mailbox-groups.
598 */
599 int set_job(char *line, regmatch_t * match)
600 {
601 int n;
602 char *ftok, *gtok, *fltr, *mbgrp, *f, *g;
603 filter_t *cf;
604 mboxgrp_t *cg;
605
606 if (!accounts || !filters || !cur_fltr->action.type)
607 return ERROR_CONFIG_PARSE;
608
609 n = match[1].rm_eo - match[1].rm_so;
610 fltr = (char *) xmalloc(n + 1);
611
612 f = xstrncpy(fltr, line + match[1].rm_so, n);
613 f[n] = 0;
614
615 n = match[2].rm_eo - match[2].rm_so;
616 mbgrp = (char *) xmalloc(n + 1);
617
618 /* Go through filters. */
619 while ((ftok = strsep(&f, ","))) {
620 cf = (filter_t *) find_filter(ftok);
621 if (!cf)
622 return ERROR_CONFIG_PARSE;
623
624 g = xstrncpy(mbgrp, line + match[2].rm_so, n);
625 g[n] = 0;
626
627 /* Go through mailbox groups. */
628 while ((gtok = strsep(&g, ","))) {
629 cg = (mboxgrp_t *) find_mboxgrp(gtok);
630 if (!cg)
631 return ERROR_CONFIG_PARSE;
632 link_mbox_filter(cf, cg);
633 }
634 }
635
636 xfree(fltr);
637 xfree(mbgrp);
638
639 return 0;
640 }
641
642
643 /*
644 * Link a filter with a mailbox.
645 */
646 void link_mbox_filter(filter_t * cf, mboxgrp_t * cg)
647 {
648 int i, j, f;
649
650 for (i = 0; cg->mboxes[i]; i++) {
651 for (f = j = 0; cg->mboxes[i]->filters[j]; j++)
652 if (j == MBOX_FILTERS_MAX - 1 ||
653 !strcmp(cf->key, cg->mboxes[i]->filters[j]->key))
654 f = 1;
655
656 if (f)
657 continue;
658
659 cg->mboxes[i]->filters[j] = cf;
660 cg->mboxes[i]->filters[j + 1] = NULL;
661
662 }
663
664 #ifdef DEBUG
665 printf("debug: JOB: '%s' '%s'\n", cf->key, cg->key);
666 #endif
667 }
668
669
670 /*
671 * Free allocated memory of data structures that are not needed anymore.
672 */
673 void destroy_data(void)
674 {
675 destroy_mboxgrp(mboxgrps);
676 }
677
678
679 /*
680 * Go through the mailbox-group tree, and free the allocated memory of
681 * each node.
682 */
683 void destroy_mboxgrp(mboxgrp_t * node)
684 {
685 if (node->left) {
686 destroy_mboxgrp(node->left);
687 node->left = NULL;
688 }
689 if (node->right) {
690 destroy_mboxgrp(node->right);
691 node->right = NULL;
692 }
693 #ifdef DEBUG
694 printf("debug: deleting FOLDER: '%s'\n", node->key);
695 #endif
696
697 xfree(node);
698 }
699
700
701 /*
702 * Convert a string of specified size to upper case.
703 */
704 void string_upper(char *str, size_t size)
705 {
706 unsigned int i;
707
708 for (i = 0; i < size; i++, str++)
709 *str = toupper(*str);
710 }
711
712
713 /*
714 * Decode the character triplet, consisting of the character '%'
715 * followed by two hexadecimal digits to its corresponding ASCII
716 * character (described in Section 2.2 of RFC 1738).
717 */
718 int string_decode(char *str)
719 {
720 char *c;
721 char hex[3];
722
723 c = str;
724
725 while (*c) {
726 if (*c == '%') {
727 if (!isxdigit(*(c + 1)) || !isxdigit(*(c + 2)))
728 return ERROR_CONFIG_PARSE;
729
730 xstrncpy(hex, ++c, 2);
731 hex[2] = 0;
732
733 if (!isprint(*str = (char) strtoul(hex, NULL, 16)))
734 return ERROR_CONFIG_PARSE;
735
736 str++;
737 c += 2;
738 } else
739 *(str++) = *(c++);
740 }
741 *str = 0;
742
743 return 0;
744 }
745
746
747 /*
748 * Convert the names of personal mailboxes using the namespace specified
749 * by the mail server.
750 */
751 char *apply_namespace(char *mbox, char *prefix, char delim)
752 {
753 static char m[MBOX_NAME_LEN];
754 char *c;
755
756 if ((!prefix[0] && !delim) || (!prefix[0] && delim == '/')
757 || !strcasecmp(mbox, "INBOX"))
758 return mbox;
759
760 m[0] = 0;
761 strncat(m, prefix, MBOX_NAME_LEN - 1);
762 strncat(m, mbox, MBOX_NAME_LEN - strlen(m) - 1);
763
764 c = m;
765 while ((c = strchr(c, '/')))
766 *(c++) = delim;
767
768 #ifdef DEBUG
769 printf("debug: MAILBOX: '%s'\n", m);
770 #endif
771
772 return m;
773 }

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26