1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #ifndef HAVE_AMALGAMATION
44 EMPTY_FILE(imap_cache
)
48 static char * encname(struct mailbox
*mp
, const char *name
, int same
,
50 static char * encuid(struct mailbox
*mp
, unsigned long uid
);
51 static FILE * clean(struct mailbox
*mp
, struct cw
*cw
);
52 static unsigned long * builds(long *contentelem
);
53 static void purge(struct mailbox
*mp
, struct message
*m
, long mc
,
54 struct cw
*cw
, const char *name
);
55 static int longlt(const void *a
, const void *b
);
56 static void remve(unsigned long n
);
57 static FILE * cache_queue1(struct mailbox
*mp
, char const *mode
,
59 static enum okay
dequeue1(struct mailbox
*mp
);
61 static const char infofmt
[] = "%c %lu %d %lu %ld";
64 ((f) & (MSAVED|MDELETED|MREAD|MBOXED|MNEW|MFLAGGED|MANSWERED|MDRAFTED))
66 static const char README1
[] = "\
67 This is a cache directory maintained by " UAGENT
"(1).\n\
68 You should not change any files within.\n\
69 Nevertheless, the structure is as follows: Each subdirectory of the\n\
70 current directory represents an IMAP account, and each subdirectory\n\
71 below that represents a mailbox. Each mailbox directory contains a file\n\
72 named UIDVALIDITY which describes the validity in relation to the version\n\
73 on the server. Other files have names corresponding to their IMAP UID.\n";
74 static const char README2
[] = "\n\
75 The first 128 bytes of these files are used to store message attributes; the\n\
76 following data is equivalent to compress(1) output. So if you have to save a\n\
77 message by hand because of an emergency, throw away the first 128 bytes and\n\
78 decompress the rest, as e.g. 'dd if=MESSAGEFILE skip=1 bs=128 | zcat' does.\n";
79 static const char README3
[] = "\n\
80 Files named QUEUE contain data that will be sent do the IMAP server next\n\
81 time a connection is made in online mode.\n";
82 static const char README4
[] = "\n\
83 You can safely delete any file or directory here, unless it contains a QUEUE\n\
84 file that is not empty; " UAGENT
" will download the data again and will also\n\
85 write new cache entries if configured in this way. If you do not wish to use\n\
86 the cache anymore, delete the entire directory and unset the 'imap-cache'\n\
87 variable in " UAGENT
"(1).\n";
88 static const char README5
[] = "\n\
89 For more information about " UAGENT
"(1), visit\n\
90 <http://sdaoden.users.sourceforge.net/code.html>.\n"; /* TODO MAGIC CONSTANT */
93 encname(struct mailbox
*mp
, const char *name
, int same
, const char *box
)
95 char *cachedir
, *eaccount
, *ename
, *res
;
100 ename
= urlxenc(name
, TRU1
);
101 if (mp
->mb_cache_directory
&& same
&& box
== NULL
) {
102 res
= salloc(resz
= strlen(mp
->mb_cache_directory
) + strlen(ename
) + 2);
103 snprintf(res
, resz
, "%s%s%s", mp
->mb_cache_directory
,
104 (*ename
? "/" : ""), ename
);
106 if ((cachedir
= ok_vlook(imap_cache
)) == NULL
||
107 (cachedir
= file_expand(cachedir
)) == NULL
) {
111 eaccount
= urlxenc(mp
->mb_imap_account
, TRU1
);
113 emailbox
= urlxenc(box
, TRU1
);
114 else if (asccasecmp(mp
->mb_imap_mailbox
, "INBOX"))
115 emailbox
= urlxenc(mp
->mb_imap_mailbox
, TRU1
);
118 res
= salloc(resz
= strlen(cachedir
) + strlen(eaccount
) +
119 strlen(emailbox
) + strlen(ename
) + 4);
120 snprintf(res
, resz
, "%s/%s/%s%s%s", cachedir
, eaccount
, emailbox
,
121 (*ename
? "/" : ""), ename
);
129 encuid(struct mailbox
*mp
, unsigned long uid
)
134 snprintf(buf
, sizeof buf
, "%lu", uid
);
135 cp
= encname(mp
, buf
, 1, NULL
);
141 getcache1(struct mailbox
*mp
, struct message
*m
, enum needspec need
,
145 long n
= 0, size
= 0, xsize
, xtime
, xlines
= -1, lines
= 0;
146 int lastc
= EOF
, i
, xflag
, inheader
= 1;
153 if (setflags
== 0 && ((mp
->mb_type
!= MB_IMAP
&& mp
->mb_type
!= MB_CACHE
) ||
156 if ((fp
= Fopen(encuid(mp
, m
->m_uid
), "r")) == NULL
)
159 fcntl_lock(fileno(fp
), FLOCK_READ
);
160 if (fscanf(fp
, infofmt
, &b
, (unsigned long*)&xsize
, &xflag
,
161 (unsigned long*)&xtime
, &xlines
) < 4)
163 if (need
!= NEED_UNSPEC
) {
166 if (need
== NEED_HEADER
)
170 if (need
== NEED_HEADER
|| need
== NEED_BODY
)
180 if (fseek(fp
, INITSKIP
, SEEK_SET
) < 0)
183 if (fseek(mp
->mb_otf
, 0L, SEEK_END
) < 0) {
187 offset
= ftell(mp
->mb_otf
);
188 while (inheader
&& (n
= zread(zp
, iob
, sizeof iob
)) > 0) {
190 for (i
= 0; i
< n
; i
++) {
191 if (iob
[i
] == '\n') {
198 fwrite(iob
, 1, n
, mp
->mb_otf
);
200 if (n
> 0 && need
== NEED_BODY
) {
201 while ((n
= zread(zp
, iob
, sizeof iob
)) > 0) {
203 for (i
= 0; i
< n
; i
++)
206 fwrite(iob
, 1, n
, mp
->mb_otf
);
210 if (zfree(zp
) < 0 || n
< 0 || ferror(fp
) || ferror(mp
->mb_otf
))
215 m
->m_block
= mailx_blockof(offset
);
216 m
->m_offset
= mailx_offsetof(offset
);
222 m
->m_flag
= xflag
| MNOFROM
;
224 m
->m_flag
|= MHIDDEN
;
227 if (xlines
> 0 && m
->m_xlines
<= 0)
228 m
->m_xlines
= xlines
;
232 if (xflag
== MREAD
&& xlines
> 0)
233 m
->m_flag
|= MFULLYCACHED
;
234 if (need
== NEED_BODY
) {
235 m
->m_have
|= HAVE_HEADER
| HAVE_BODY
;
237 m
->m_xlines
= m
->m_lines
;
242 m
->m_have
|= HAVE_HEADER
;
256 getcache(struct mailbox
*mp
, struct message
*m
, enum needspec need
)
261 rv
= getcache1(mp
, m
, need
, 0);
267 putcache(struct mailbox
*mp
, struct message
*m
)
269 char iob
[32768], *name
, ob
;
272 long n
, cnt
, oldoffset
, osize
, otime
, olines
= -1;
276 if ((mp
->mb_type
!= MB_IMAP
&& mp
->mb_type
!= MB_CACHE
) || m
->m_uid
== 0 ||
277 m
->m_time
== 0 || (m
->m_flag
& (MTOUCH
|MFULLYCACHED
)) == MFULLYCACHED
)
279 if (m
->m_have
& HAVE_BODY
)
281 else if (m
->m_have
& HAVE_HEADER
)
283 else if (m
->m_have
== HAVE_NOTHING
)
287 if ((oldoffset
= ftell(mp
->mb_itf
)) < 0) /* XXX weird err hdling */
289 if ((obuf
= Fopen(name
= encuid(mp
, m
->m_uid
), "r+")) == NULL
) {
290 if ((obuf
= Fopen(name
, "w")) == NULL
)
292 fcntl_lock(fileno(obuf
), FLOCK_WRITE
); /* XXX err hdl */
294 fcntl_lock(fileno(obuf
), FLOCK_READ
); /* XXX err hdl */
295 if (fscanf(obuf
, infofmt
, &ob
, (unsigned long*)&osize
, &oflag
,
296 (unsigned long*)&otime
, &olines
) >= 4 && ob
!= '\0' &&
297 (ob
== 'B' || (ob
== 'H' && c
!= 'B'))) {
298 if (m
->m_xlines
<= 0 && olines
> 0)
299 m
->m_xlines
= olines
;
300 if ((c
!= 'N' && (size_t)osize
!= m
->m_xsize
) ||
301 oflag
!= (int)USEBITS(m
->m_flag
) || otime
!= m
->m_time
||
302 (m
->m_xlines
> 0 && olines
!= m
->m_xlines
)) {
305 fprintf(obuf
, infofmt
, ob
, (unsigned long)m
->m_xsize
,
306 USEBITS(m
->m_flag
), (unsigned long)m
->m_time
, m
->m_xlines
);
314 ftruncate(fileno(obuf
), 0);
316 if ((ibuf
= setinput(mp
, m
, NEED_UNSPEC
)) == NULL
) {
322 fseek(obuf
, INITSKIP
, SEEK_SET
);
326 n
= (cnt
> (long)sizeof iob
) ? (long)sizeof iob
: cnt
;
328 if ((size_t)n
!= fread(iob
, 1, n
, ibuf
) ||
329 n
!= (long)zwrite(zp
, iob
, n
)) {
341 fprintf(obuf
, infofmt
, c
, (unsigned long)m
->m_xsize
, USEBITS(m
->m_flag
),
342 (unsigned long)m
->m_time
, m
->m_xlines
);
348 if (c
== 'B' && USEBITS(m
->m_flag
) == MREAD
)
349 m
->m_flag
|= MFULLYCACHED
;
351 if (Fclose(obuf
) != 0) {
352 m
->m_flag
&= ~MFULLYCACHED
;
355 (void)fseek(mp
->mb_itf
, oldoffset
, SEEK_SET
);
361 initcache(struct mailbox
*mp
)
369 if (mp
->mb_cache_directory
!= NULL
)
370 free(mp
->mb_cache_directory
);
371 mp
->mb_cache_directory
= NULL
;
372 if ((name
= encname(mp
, "", 1, NULL
)) == NULL
)
374 mp
->mb_cache_directory
= sstrdup(name
);
375 if ((uvname
= encname(mp
, "UIDVALIDITY", 1, NULL
)) == NULL
)
377 if (cwget(&cw
) == STOP
)
379 if ((uvfp
= Fopen(uvname
, "r+")) == NULL
||
380 (fcntl_lock(fileno(uvfp
), FLOCK_READ
), 0) ||
381 fscanf(uvfp
, "%lu", &uv
) != 1 || uv
!= mp
->mb_uidvalidity
) {
382 if ((uvfp
= clean(mp
, &cw
)) == NULL
)
388 fcntl_lock(fileno(uvfp
), FLOCK_WRITE
);
389 fprintf(uvfp
, "%lu\n", mp
->mb_uidvalidity
);
390 if (ferror(uvfp
) || Fclose(uvfp
) != 0) {
392 mp
->mb_uidvalidity
= 0;
401 purgecache(struct mailbox
*mp
, struct message
*m
, long mc
)
407 if ((name
= encname(mp
, "", 1, NULL
)) == NULL
)
409 if (cwget(&cw
) == STOP
)
411 purge(mp
, m
, mc
, &cw
, name
);
418 clean(struct mailbox
*mp
, struct cw
*cw
)
420 char *cachedir
, *eaccount
, *buf
;
421 char const *emailbox
;
428 if ((cachedir
= ok_vlook(imap_cache
)) == NULL
||
429 (cachedir
= file_expand(cachedir
)) == NULL
)
431 eaccount
= urlxenc(mp
->mb_imap_account
, TRU1
);
432 if (asccasecmp(mp
->mb_imap_mailbox
, "INBOX"))
433 emailbox
= urlxenc(mp
->mb_imap_mailbox
, TRU1
);
436 buf
= salloc(bufsz
= strlen(cachedir
) + strlen(eaccount
) +
437 strlen(emailbox
) + 40);
438 if (makedir(cachedir
) != OKAY
)
440 snprintf(buf
, bufsz
, "%s/README", cachedir
);
441 if ((fp
= Fopen(buf
, "wx")) != NULL
) {
450 snprintf(buf
, bufsz
, "%s/%s", cachedir
, eaccount
);
451 if (makedir(buf
) != OKAY
)
453 snprintf(buf
, bufsz
, "%s/%s/%s", cachedir
, eaccount
, emailbox
);
454 if (makedir(buf
) != OKAY
)
458 if ((dirp
= opendir(".")) == NULL
)
460 while ((dp
= readdir(dirp
)) != NULL
) {
461 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
462 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
467 fp
= Fopen("UIDVALIDITY", "w");
469 if (cwret(cw
) == STOP
) {
470 fputs("Fatal: Cannot change back to current directory.\n", stderr
);
478 static unsigned long *
479 builds(long *contentelem
)
481 unsigned long n
, *contents
= NULL
;
482 long contentalloc
= 0;
489 if ((dirp
= opendir(".")) == NULL
)
491 while ((dp
= readdir(dirp
)) != NULL
) {
492 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
493 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
495 n
= strtoul(dp
->d_name
, &x
, 10);
498 if (*contentelem
>= contentalloc
- 1)
499 contents
= srealloc(contents
,
500 (contentalloc
+= 200) * sizeof *contents
);
501 contents
[(*contentelem
)++] = n
;
504 if (*contentelem
> 0) {
505 contents
[*contentelem
] = 0;
506 qsort(contents
, *contentelem
, sizeof *contents
, longlt
);
514 purge(struct mailbox
*mp
, struct message
*m
, long mc
, struct cw
*cw
,
517 unsigned long *contents
;
518 long i
, j
, contentelem
;
524 contents
= builds(&contentelem
);
525 if (contents
!= NULL
) {
527 while (j
< contentelem
) {
528 if (i
< mc
&& m
[i
].m_uid
== contents
[j
]) {
531 } else if (i
< mc
&& m
[i
].m_uid
< contents
[j
])
534 remve(contents
[j
++]);
538 if (cwret(cw
) == STOP
) {
539 fputs("Fatal: Cannot change back to current directory.\n", stderr
);
547 longlt(const void *a
, const void *b
)
549 union {long l
; int i
;} u
;
552 u
.l
= *(long const*)a
- *(long const*)b
;
553 u
.i
= (u
.l
< 0) ? -1 : ((u
.l
> 0) ? 1 : 0);
559 remve(unsigned long n
)
564 snprintf(buf
, sizeof buf
, "%lu", n
);
570 delcache(struct mailbox
*mp
, struct message
*m
)
575 fn
= encuid(mp
, m
->m_uid
);
576 if (fn
&& unlink(fn
) == 0)
577 m
->m_flag
|= MUNLINKED
;
582 cache_setptr(enum fedit_mode fm
, int transparent
)
585 int i
, omsgCount
= 0;
587 unsigned long *contents
;
589 struct message
*omessage
= NULL
;
595 omsgCount
= msgCount
;
597 if (mb
.mb_cache_directory
!= NULL
) {
598 free(mb
.mb_cache_directory
);
599 mb
.mb_cache_directory
= NULL
;
601 if ((name
= encname(&mb
, "", 1, NULL
)) == NULL
)
603 mb
.mb_cache_directory
= sstrdup(name
);
604 if (cwget(&cw
) == STOP
)
608 contents
= builds(&contentelem
);
609 msgCount
= contentelem
;
610 message
= scalloc(msgCount
+ 1, sizeof *message
);
611 if (cwret(&cw
) == STOP
) {
612 fputs("Fatal: Cannot change back to current directory.\n", stderr
);
618 for (i
= 0; i
< msgCount
; i
++) {
619 message
[i
].m_uid
= contents
[i
];
620 getcache1(&mb
, &message
[i
], NEED_UNSPEC
, 3);
625 if (contents
!= NULL
)
627 mb
.mb_type
= MB_CACHE
;
628 mb
.mb_perm
= ((options
& OPT_R_FLAG
) || (fm
& FEDIT_RDONLY
)) ? 0 : MB_DELE
;
630 transflags(omessage
, omsgCount
, 1);
632 if (omessage
!= NULL
)
643 cache_list(struct mailbox
*mp
, const char *base
, int strip
, FILE *fp
)
645 char *name
, *cachedir
, *eaccount
;
648 const char *cp
, *bp
, *sp
;
653 if ((cachedir
= ok_vlook(imap_cache
)) == NULL
||
654 (cachedir
= file_expand(cachedir
)) == NULL
)
656 eaccount
= urlxenc(mp
->mb_imap_account
, TRU1
);
657 name
= salloc(namesz
= strlen(cachedir
) + strlen(eaccount
) + 2);
658 snprintf(name
, namesz
, "%s/%s", cachedir
, eaccount
);
659 if ((dirp
= opendir(name
)) == NULL
)
661 while ((dp
= readdir(dirp
)) != NULL
) {
662 if (dp
->d_name
[0] == '.')
664 cp
= sp
= urlxdec(dp
->d_name
);
665 for (bp
= base
; *bp
&& *bp
== *sp
; bp
++)
669 cp
= strip
? sp
: cp
;
670 fprintf(fp
, "%s\n", *cp
? cp
: "INBOX");
680 cache_remove(const char *name
)
686 int pathsize
, pathend
, n
;
690 if ((dir
= encname(&mb
, "", 0, imap_fileof(name
))) == NULL
)
692 pathend
= strlen(dir
);
693 path
= smalloc(pathsize
= pathend
+ 30);
694 memcpy(path
, dir
, pathend
);
695 path
[pathend
++] = '/';
696 path
[pathend
] = '\0';
697 if ((dirp
= opendir(path
)) == NULL
) {
701 while ((dp
= readdir(dirp
)) != NULL
) {
702 if (dp
->d_name
[0] == '.' && (dp
->d_name
[1] == '\0' ||
703 (dp
->d_name
[1] == '.' && dp
->d_name
[2] == '\0')))
705 n
= strlen(dp
->d_name
) + 1;
706 if (pathend
+ n
> pathsize
)
707 path
= srealloc(path
, pathsize
= pathend
+ n
+ 30);
708 memcpy(path
+ pathend
, dp
->d_name
, n
);
709 if (stat(path
, &st
) < 0 || (st
.st_mode
& S_IFMT
) != S_IFREG
)
711 if (unlink(path
) < 0) {
720 path
[pathend
] = '\0';
721 rmdir(path
); /* no error on failure, might contain submailboxes */
729 cache_rename(const char *old
, const char *new)
731 char *olddir
, *newdir
;
735 if ((olddir
= encname(&mb
, "", 0, imap_fileof(old
))) == NULL
||
736 (newdir
= encname(&mb
, "",0, imap_fileof(new))) == NULL
)
738 if (rename(olddir
, newdir
) < 0) {
748 cached_uidvalidity(struct mailbox
*mp
)
755 if ((uvname
= encname(mp
, "UIDVALIDITY", 1, NULL
)) == NULL
) {
759 if ((uvfp
= Fopen(uvname
, "r")) == NULL
||
760 (fcntl_lock(fileno(uvfp
), FLOCK_READ
), 0) ||
761 fscanf(uvfp
, "%lu", &uv
) != 1)
771 cache_queue1(struct mailbox
*mp
, char const *mode
, char **xname
)
777 if ((name
= encname(mp
, "QUEUE", 0, NULL
)) == NULL
)
779 if ((fp
= Fopen(name
, mode
)) != NULL
)
780 fcntl_lock(fileno(fp
), FLOCK_WRITE
);
789 cache_queue(struct mailbox
*mp
)
794 fp
= cache_queue1(mp
, "a", NULL
);
796 fputs("Cannot queue IMAP command. Retry when online.\n", stderr
);
802 cache_dequeue(struct mailbox
*mp
)
805 char *cachedir
, *eaccount
, *buf
, *oldbox
;
811 if ((cachedir
= ok_vlook(imap_cache
)) == NULL
||
812 (cachedir
= file_expand(cachedir
)) == NULL
)
814 eaccount
= urlxenc(mp
->mb_imap_account
, TRU1
);
815 buf
= salloc(bufsz
= strlen(cachedir
) + strlen(eaccount
) + 2);
816 snprintf(buf
, bufsz
, "%s/%s", cachedir
, eaccount
);
817 if ((dirp
= opendir(buf
)) == NULL
)
819 oldbox
= mp
->mb_imap_mailbox
;
820 while ((dp
= readdir(dirp
)) != NULL
) {
821 if (dp
->d_name
[0] == '.')
823 /* FIXME MUST BLOCK SIGNALS IN ORDER TO ENSURE PROPER RESTORE!
824 * (but wuuuuh, what a shit!) */
825 mp
->mb_imap_mailbox
= sstrdup(urlxdec(dp
->d_name
));
827 { char *x
= mp
->mb_imap_mailbox
;
828 mp
->mb_imap_mailbox
= oldbox
;
839 dequeue1(struct mailbox
*mp
)
841 FILE *fp
= NULL
, *uvfp
= NULL
;
842 char *qname
, *uvname
;
849 fp
= cache_queue1(mp
, "r+", &qname
);
850 if (fp
!= NULL
&& fsize(fp
) > 0) {
851 if (imap_select(mp
, &is_size
, &is_count
, mp
->mb_imap_mailbox
) != OKAY
) {
852 fprintf(stderr
, "Cannot select \"%s\" for dequeuing.\n",
853 mp
->mb_imap_mailbox
);
856 if ((uvname
= encname(mp
, "UIDVALIDITY", 0, NULL
)) == NULL
||
857 (uvfp
= Fopen(uvname
, "r")) == NULL
||
858 (fcntl_lock(fileno(uvfp
), FLOCK_READ
), 0) ||
859 fscanf(uvfp
, "%lu", &uv
) != 1 || uv
!= mp
->mb_uidvalidity
) {
860 fprintf(stderr
, "Unique identifiers for \"%s\" are out of date. "
861 "Cannot commit IMAP commands.\n", mp
->mb_imap_mailbox
);
863 fputs("Saving IMAP commands to dead.letter\n", stderr
);
864 savedeadletter(fp
, 0);
865 ftruncate(fileno(fp
), 0);
873 printf("Committing IMAP commands for \"%s\"\n", mp
->mb_imap_mailbox
);
874 imap_dequeue(mp
, fp
);
884 #endif /* HAVE_IMAP */