Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F120826107
imapd.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
87 KB
Referenced Files
None
Subscribers
None
imapd.c
View Options
/* imapd.c -- IMAP server protocol parsing
*
* (C) Copyright 1994 by Carnegie Mellon University
*
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies and that
* both that copyright notice and this permission notice appear in
* supporting documentation, and that the name of CMU not be
* used in advertising or publicity pertaining to distribution of the
* software without specific, written prior permission.
*
* CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
* CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*
*/
#include
<stdio.h>
#include
<string.h>
#include
<time.h>
#include
<signal.h>
#include
<sys/types.h>
#include
<sys/param.h>
#include
<sys/stat.h>
#include
<syslog.h>
#include
<com_err.h>
#include
<netdb.h>
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<arpa/inet.h>
#include
"acl.h"
#include
"util.h"
#include
"auth.h"
#include
"acte.h"
#include
"config.h"
#include
"version.h"
#include
"charset.h"
#include
"sysexits.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"imapd.h"
#include
"xmalloc.h"
extern
int
optind
;
extern
char
*
optarg
;
extern
int
errno
;
struct
buf
{
char
*
s
;
int
alloc
;
};
char
*
imapd_userid
;
int
imapd_userisadmin
;
struct
mailbox
*
imapd_mailbox
;
int
imapd_exists
;
struct
sockaddr_in
imapd_localaddr
,
imapd_remoteaddr
;
int
imapd_haveaddr
=
0
;
char
imapd_clienthost
[
250
]
=
"[local]"
;
struct
protstream
*
imapd_out
,
*
imapd_in
;
static
struct
mailbox
mboxstruct
;
static
struct
fetchargs
zerofetchargs
;
static
char
*
monthname
[]
=
{
"jan"
,
"feb"
,
"mar"
,
"apr"
,
"may"
,
"jun"
,
"jul"
,
"aug"
,
"sep"
,
"oct"
,
"nov"
,
"dec"
};
static
int
mailboxdata
(),
listdata
(),
lsubdata
();
main
(
argc
,
argv
,
envp
)
int
argc
;
char
**
argv
;
char
**
envp
;
{
char
hostname
[
MAXHOSTNAMELEN
+
1
];
int
salen
;
struct
hostent
*
hp
;
int
timeout
;
imapd_in
=
prot_new
(
0
,
0
);
imapd_out
=
prot_new
(
1
,
1
);
setproctitle_init
(
argc
,
argv
,
envp
);
config_init
(
"imapd"
);
signal
(
SIGPIPE
,
SIG_IGN
);
gethostname
(
hostname
,
sizeof
(
hostname
));
/* Find out name of client host */
salen
=
sizeof
(
imapd_remoteaddr
);
if
(
getpeername
(
0
,
&
imapd_remoteaddr
,
&
salen
)
==
0
&&
imapd_remoteaddr
.
sin_family
==
AF_INET
)
{
if
(
hp
=
gethostbyaddr
((
char
*
)
&
imapd_remoteaddr
.
sin_addr
,
sizeof
(
imapd_remoteaddr
.
sin_addr
),
AF_INET
))
{
if
(
strlen
(
hp
->
h_name
)
+
30
>
sizeof
(
imapd_clienthost
))
{
hp
->
h_name
[
sizeof
(
imapd_clienthost
)
-30
]
=
'\0'
;
}
strcpy
(
imapd_clienthost
,
hp
->
h_name
);
}
else
{
imapd_clienthost
[
0
]
=
'\0'
;
}
strcat
(
imapd_clienthost
,
"["
);
strcat
(
imapd_clienthost
,
inet_ntoa
(
imapd_remoteaddr
.
sin_addr
));
strcat
(
imapd_clienthost
,
"]"
);
salen
=
sizeof
(
imapd_localaddr
);
if
(
getsockname
(
0
,
&
imapd_localaddr
,
&
salen
)
==
0
)
{
imapd_haveaddr
=
1
;
}
}
proc_register
(
"imapd"
,
imapd_clienthost
,
(
char
*
)
0
,
(
char
*
)
0
);
/* Set inactivity timer */
timeout
=
config_getint
(
"timeout"
,
30
);
if
(
timeout
<
30
)
timeout
=
30
;
prot_settimeout
(
imapd_in
,
timeout
*
60
);
prot_printf
(
imapd_out
,
"* OK %s Cyrus IMAP4 %s server ready
\r\n
"
,
hostname
,
CYRUS_VERSION
);
cmdloop
();
}
usage
()
{
prot_printf
(
imapd_out
,
"* BYE usage: imapd
\r\n
"
);
prot_flush
(
imapd_out
);
exit
(
EX_USAGE
);
}
/*
* Cleanly shut down and exit
*/
shut_down
(
code
)
int
code
;
{
proc_cleanup
();
if
(
imapd_mailbox
)
{
index_closemailbox
(
imapd_mailbox
);
mailbox_close
(
imapd_mailbox
);
}
prot_flush
(
imapd_out
);
exit
(
code
);
}
fatal
(
s
,
code
)
char
*
s
;
int
code
;
{
static
int
recurse_code
=
0
;
if
(
recurse_code
)
{
/* We were called recursively. Just give up */
exit
(
recurse_code
);
}
recurse_code
=
code
;
prot_printf
(
imapd_out
,
"* BYE Fatal error: %s
\r\n
"
,
s
);
prot_flush
(
imapd_out
);
shut_down
(
code
);
}
/*
* Top-level command loop parsing
*/
cmdloop
()
{
int
c
;
int
usinguid
,
havepartition
,
havenamespace
;
static
struct
buf
tag
,
cmd
,
arg1
,
arg2
,
arg3
,
arg4
;
char
*
p
;
for
(;;)
{
prot_flush
(
imapd_out
);
/* Parse tag */
c
=
getword
(
&
tag
);
if
(
c
==
EOF
)
{
if
(
p
=
prot_error
(
imapd_in
))
{
syslog
(
LOG_WARNING
,
"PROTERR: %s"
,
p
);
prot_printf
(
imapd_out
,
"* BYE %s
\r\n
"
,
p
);
}
shut_down
(
0
);
}
if
(
c
!=
' '
||
!
is_atom
(
tag
.
s
)
||
(
tag
.
s
[
0
]
==
'*'
&&
!
tag
.
s
[
1
]))
{
prot_printf
(
imapd_out
,
"* BAD Invalid tag
\r\n
"
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
}
/* Parse command name */
c
=
getword
(
&
cmd
);
if
(
!
cmd
.
s
[
0
])
{
prot_printf
(
imapd_out
,
"%s BAD Null command
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
}
if
(
islower
(
cmd
.
s
[
0
]))
cmd
.
s
[
0
]
=
toupper
(
cmd
.
s
[
0
]);
for
(
p
=
&
cmd
.
s
[
1
];
*
p
;
p
++
)
{
if
(
isupper
(
*
p
))
*
p
=
tolower
(
*
p
);
}
/* Only Authenticate/Login/Logout/Noop allowed when not logged in */
if
(
!
imapd_userid
&&
!
strchr
(
"ALNC"
,
cmd
.
s
[
0
]))
goto
nologin
;
switch
(
cmd
.
s
[
0
])
{
case
'A'
:
if
(
!
strcmp
(
cmd
.
s
,
"Authenticate"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
!
is_atom
(
arg1
.
s
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid authenticate mechanism
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
if
(
imapd_userid
)
{
prot_printf
(
imapd_out
,
"%s BAD Already authenticated
\r\n
"
,
tag
.
s
);
continue
;
}
cmd_authenticate
(
tag
.
s
,
arg1
.
s
);
}
else
if
(
!
imapd_userid
)
goto
nologin
;
else
if
(
!
strcmp
(
cmd
.
s
,
"Append"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
cmd_append
(
tag
.
s
,
arg1
.
s
);
}
else
goto
badcmd
;
break
;
case
'B'
:
if
(
!
strcmp
(
cmd
.
s
,
"Bboard"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_select
(
tag
.
s
,
cmd
.
s
,
arg1
.
s
);
}
else
goto
badcmd
;
break
;
case
'C'
:
if
(
!
strcmp
(
cmd
.
s
,
"Capability"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_capability
(
tag
.
s
);
}
else
if
(
!
imapd_userid
)
goto
nologin
;
else
if
(
!
strcmp
(
cmd
.
s
,
"Check"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_noop
(
tag
.
s
,
cmd
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Copy"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
copy
:
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
||
!
is_sequence
(
arg1
.
s
))
goto
badsequence
;
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_copy
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
usinguid
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Create"
))
{
havepartition
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
' '
)
{
havepartition
=
1
;
c
=
getword
(
&
arg2
);
if
(
!
is_atom
(
arg2
.
s
))
goto
badpartition
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_create
(
tag
.
s
,
arg1
.
s
,
havepartition
?
arg2
.
s
:
0
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Close"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_close
(
tag
.
s
);
}
else
goto
badcmd
;
break
;
case
'D'
:
if
(
!
strcmp
(
cmd
.
s
,
"Delete"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_delete
(
tag
.
s
,
arg1
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Deleteacl"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg3
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_setacl
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
arg3
.
s
,
(
char
*
)
0
);
}
else
goto
badcmd
;
break
;
case
'E'
:
if
(
!
strcmp
(
cmd
.
s
,
"Expunge"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_expunge
(
tag
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Examine"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_select
(
tag
.
s
,
cmd
.
s
,
arg1
.
s
);
}
else
goto
badcmd
;
break
;
case
'F'
:
if
(
!
strcmp
(
cmd
.
s
,
"Fetch"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
fetch
:
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
||
!
is_sequence
(
arg1
.
s
))
goto
badsequence
;
cmd_fetch
(
tag
.
s
,
arg1
.
s
,
usinguid
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Find"
))
{
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_find
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
);
}
else
goto
badcmd
;
break
;
case
'G'
:
if
(
!
strcmp
(
cmd
.
s
,
"Getacl"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_getacl
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Getquota"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_getquota
(
tag
.
s
,
arg1
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Getquotaroot"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_getquotaroot
(
tag
.
s
,
arg1
.
s
);
}
else
goto
badcmd
;
break
;
case
'L'
:
if
(
!
strcmp
(
cmd
.
s
,
"Login"
))
{
if
(
c
!=
' '
||
(
c
=
getastring
(
&
arg1
))
!=
' '
)
{
goto
missingargs
;
}
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
if
(
imapd_userid
)
{
prot_printf
(
imapd_out
,
"%s BAD Already logged in
\r\n
"
,
tag
.
s
);
continue
;
}
cmd_login
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Logout"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
prot_printf
(
imapd_out
,
"* BYE Server terminating connection
\r\n
"
);
prot_printf
(
imapd_out
,
"%s OK Logout completed
\r\n
"
,
tag
.
s
);
shut_down
(
0
);
}
else
if
(
!
imapd_userid
)
goto
nologin
;
else
if
(
!
strcmp
(
cmd
.
s
,
"List"
))
{
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_list
(
tag
.
s
,
0
,
arg1
.
s
,
arg2
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Lsub"
))
{
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_list
(
tag
.
s
,
1
,
arg1
.
s
,
arg2
.
s
);
}
else
goto
badcmd
;
break
;
case
'M'
:
if
(
!
strcmp
(
cmd
.
s
,
"Myrights"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_myrights
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
);
}
else
goto
badcmd
;
break
;
case
'N'
:
if
(
!
strcmp
(
cmd
.
s
,
"Noop"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_noop
(
tag
.
s
,
cmd
.
s
);
}
else
if
(
!
imapd_userid
)
goto
nologin
;
else
goto
badcmd
;
break
;
case
'P'
:
if
(
!
strcmp
(
cmd
.
s
,
"Partial"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg2
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg3
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg4
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_partial
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
arg3
.
s
,
arg4
.
s
);
}
else
goto
badcmd
;
break
;
case
'R'
:
if
(
!
strcmp
(
cmd
.
s
,
"Rename"
))
{
havepartition
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
' '
)
{
havepartition
=
1
;
c
=
getword
(
&
arg3
);
if
(
!
is_atom
(
arg3
.
s
))
goto
badpartition
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_rename
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
havepartition
?
arg3
.
s
:
0
);
}
else
goto
badcmd
;
break
;
case
'S'
:
if
(
!
strcmp
(
cmd
.
s
,
"Store"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
store
:
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
||
!
is_sequence
(
arg1
.
s
))
goto
badsequence
;
c
=
getword
(
&
arg2
);
if
(
c
!=
' '
)
goto
badsequence
;
cmd_store
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
usinguid
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Select"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_select
(
tag
.
s
,
cmd
.
s
,
arg1
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Search"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
search
:
cmd_search
(
tag
.
s
,
usinguid
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Subscribe"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
havenamespace
=
0
;
c
=
getastring
(
&
arg1
);
if
(
c
==
' '
)
{
havenamespace
=
1
;
c
=
getastring
(
&
arg2
);
}
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
if
(
havenamespace
)
{
cmd_changesub
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
1
);
}
else
{
cmd_changesub
(
tag
.
s
,
(
char
*
)
0
,
arg1
.
s
,
1
);
}
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Setacl"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg3
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg4
);
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_setacl
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
arg3
.
s
,
arg4
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Setquota"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
cmd_setquota
(
tag
.
s
,
arg1
.
s
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Status"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
cmd_status
(
tag
.
s
,
arg1
.
s
);
}
else
goto
badcmd
;
break
;
case
'U'
:
if
(
!
strcmp
(
cmd
.
s
,
"Uid"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
1
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
lcase
(
arg1
.
s
);
if
(
!
strcmp
(
arg1
.
s
,
"fetch"
))
{
goto
fetch
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"store"
))
{
goto
store
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"search"
))
{
goto
search
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"copy"
))
{
goto
copy
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Unrecognized UID subcommand
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
}
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Unsubscribe"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
havenamespace
=
0
;
c
=
getastring
(
&
arg1
);
if
(
c
==
' '
)
{
havenamespace
=
1
;
c
=
getastring
(
&
arg2
);
}
if
(
c
==
EOF
)
goto
missingargs
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
if
(
havenamespace
)
{
cmd_changesub
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
0
);
}
else
{
cmd_changesub
(
tag
.
s
,
(
char
*
)
0
,
arg1
.
s
,
0
);
}
}
else
goto
badcmd
;
break
;
default
:
badcmd
:
prot_printf
(
imapd_out
,
"%s BAD Unrecognized command
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
}
continue
;
nologin
:
prot_printf
(
imapd_out
,
"%s BAD Please login first
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
nomailbox
:
prot_printf
(
imapd_out
,
"%s BAD Please select a mailbox first
\r\n
"
,
tag
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
missingargs
:
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
extraargs
:
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
badsequence
:
prot_printf
(
imapd_out
,
"%s BAD Invalid sequence in %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
badpartition
:
prot_printf
(
imapd_out
,
"%s BAD Invalid partition name in %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
if
(
c
!=
'\n'
)
eatline
();
continue
;
}
}
/*
* Perform a LOGIN command
*/
cmd_login
(
tag
,
user
,
passwd
)
char
*
tag
;
char
*
user
;
char
*
passwd
;
{
char
*
canon_user
;
int
userlen
;
char
*
reply
=
0
;
char
*
val
;
char
buf
[
MAX_MAILBOX_PATH
];
FILE
*
logfile
;
canon_user
=
auth_canonifyid
(
user
);
if
(
!
canon_user
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s plaintext %s invalid user"
,
imapd_clienthost
,
beautify_string
(
user
));
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_INVALID_USER
));
return
;
}
if
(
!
strcmp
(
canon_user
,
"anonymous"
))
{
if
(
config_getswitch
(
"allowanonymouslogin"
,
0
))
{
passwd
=
beautify_string
(
passwd
);
if
(
strlen
(
passwd
)
>
500
)
passwd
[
500
]
=
'\0'
;
syslog
(
LOG_NOTICE
,
"login: %s anonymous %s"
,
imapd_clienthost
,
passwd
);
reply
=
"Anonymous access granted"
;
}
else
{
syslog
(
LOG_NOTICE
,
"badlogin: %s anonymous login refused"
,
imapd_clienthost
);
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_ANONYMOUS_NOT_PERMITTED
));
return
;
}
}
else
if
(
login_plaintext
(
canon_user
,
passwd
,
&
reply
)
!=
0
)
{
if
(
reply
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s plaintext %s %s"
,
imapd_clienthost
,
canon_user
,
reply
);
}
sleep
(
3
);
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_INVALID_LOGIN
));
return
;
}
else
{
syslog
(
LOG_NOTICE
,
"login: %s %s plaintext %s"
,
imapd_clienthost
,
canon_user
,
reply
?
reply
:
""
);
}
auth_setid
(
canon_user
);
imapd_userid
=
strsave
(
canon_user
);
proc_register
(
"imapd"
,
imapd_clienthost
,
imapd_userid
,
(
char
*
)
0
);
val
=
config_getstring
(
"admins"
,
""
);
userlen
=
strlen
(
canon_user
);
while
(
*
val
)
{
if
(
!
strncmp
(
val
,
canon_user
,
userlen
)
&&
(
!
val
[
userlen
]
||
isspace
(
val
[
userlen
])))
{
break
;
}
while
(
*
val
&&
!
isspace
(
*
val
))
val
++
;
while
(
*
val
&&
isspace
(
*
val
))
val
++
;
}
if
(
*
val
!=
'\0'
)
imapd_userisadmin
=
1
;
if
(
!
reply
)
reply
=
"User logged in"
;
/* Create telemetry log */
sprintf
(
buf
,
"%s%s%s/%u"
,
config_dir
,
FNAME_LOGDIR
,
imapd_userid
,
getpid
());
logfile
=
fopen
(
buf
,
"w"
);
if
(
logfile
)
{
prot_setlog
(
imapd_in
,
fileno
(
logfile
));
prot_setlog
(
imapd_out
,
fileno
(
logfile
));
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
reply
);
return
;
};
/*
* Perform an AUTHENTICATE command
*/
cmd_authenticate
(
tag
,
authtype
)
char
*
tag
;
char
*
authtype
;
{
char
*
canon_user
;
int
userlen
;
int
r
;
struct
acte_server
*
mech
;
int
(
*
authproc
)();
int
outputlen
;
char
*
output
;
int
inputlen
;
static
struct
buf
input
;
void
*
state
;
char
*
reply
=
0
;
int
protlevel
;
char
*
user
;
char
*
(
*
encodefunc
)();
char
*
(
*
decodefunc
)();
int
maxplain
;
char
*
val
;
char
buf
[
MAX_MAILBOX_PATH
];
FILE
*
logfile
;
lcase
(
authtype
);
r
=
login_authenticate
(
authtype
,
&
mech
,
&
authproc
);
if
(
!
r
)
{
r
=
mech
->
start
(
"imap"
,
authproc
,
ACTE_PROT_ANY
,
PROT_BUFSIZE
,
imapd_haveaddr
?
&
imapd_localaddr
:
0
,
imapd_haveaddr
?
&
imapd_remoteaddr
:
0
,
&
outputlen
,
&
output
,
&
state
,
&
reply
);
}
if
(
r
&&
r
!=
ACTE_DONE
)
{
if
(
reply
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s %s %s"
,
imapd_clienthost
,
authtype
,
reply
);
}
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_INVALID_LOGIN
));
return
;
}
while
(
r
==
0
)
{
printauthready
(
outputlen
,
output
);
inputlen
=
getbase64string
(
&
input
);
if
(
inputlen
==
-1
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid base64 string
\r\n
"
,
tag
);
mech
->
free_state
(
state
);
return
;
}
r
=
mech
->
auth
(
state
,
inputlen
,
input
.
s
,
&
outputlen
,
&
output
,
&
reply
);
}
if
(
r
!=
ACTE_DONE
)
{
mech
->
free_state
(
state
);
if
(
reply
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s %s %s"
,
imapd_clienthost
,
authtype
,
reply
);
}
sleep
(
3
);
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_INVALID_LOGIN
));
return
;
}
mech
->
query_state
(
state
,
&
user
,
&
protlevel
,
&
encodefunc
,
&
decodefunc
,
&
maxplain
);
canon_user
=
auth_canonifyid
(
user
);
if
(
!
canon_user
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s %s %s bad userid"
,
imapd_clienthost
,
authtype
,
beautify_string
(
user
));
mech
->
free_state
(
state
);
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_INVALID_USER
));
return
;
}
auth_setid
(
canon_user
);
imapd_userid
=
strsave
(
canon_user
);
proc_register
(
"imapd"
,
imapd_clienthost
,
imapd_userid
,
(
char
*
)
0
);
val
=
config_getstring
(
"admins"
,
""
);
userlen
=
strlen
(
canon_user
);
while
(
*
val
)
{
if
(
!
strncmp
(
val
,
canon_user
,
userlen
)
&&
(
!
val
[
userlen
]
||
isspace
(
val
[
userlen
])))
{
break
;
}
while
(
*
val
&&
!
isspace
(
*
val
))
val
++
;
while
(
*
val
&&
isspace
(
*
val
))
val
++
;
}
if
(
*
val
!=
'\0'
)
imapd_userisadmin
=
1
;
if
(
!
reply
)
reply
=
"User logged in"
;
syslog
(
LOG_NOTICE
,
"login: %s %s %s %s"
,
imapd_clienthost
,
canon_user
,
authtype
,
reply
?
reply
:
""
);
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
reply
);
if
(
encodefunc
||
decodefunc
)
{
prot_setfunc
(
imapd_in
,
decodefunc
,
state
,
0
);
prot_setfunc
(
imapd_out
,
encodefunc
,
state
,
maxplain
);
}
else
{
mech
->
free_state
(
state
);
}
/* Create telemetry log */
sprintf
(
buf
,
"%s%s%s/%u"
,
config_dir
,
FNAME_LOGDIR
,
imapd_userid
,
getpid
());
logfile
=
fopen
(
buf
,
"w"
);
if
(
logfile
)
{
prot_setlog
(
imapd_in
,
fileno
(
logfile
));
prot_setlog
(
imapd_out
,
fileno
(
logfile
));
}
return
;
};
/*
* Perform a NOOP command
*/
cmd_noop
(
tag
,
cmd
)
char
*
tag
;
char
*
cmd
;
{
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
1
);
}
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
cmd
);
};
/*
* Perform a CAPABILITY command
*/
cmd_capability
(
tag
)
char
*
tag
;
{
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
prot_printf
(
imapd_out
,
"* CAPABILITY IMAP4 STATUS
\r\n
%s OK Capability completed
\r\n
"
,
tag
);
};
/*
* Parse and perform an APPEND command.
* The command has been parsed up to and including
* the mailbox name.
*/
#define FLAGGROW 10
cmd_append
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
c
;
char
**
flag
=
0
;
int
nflags
=
0
,
flagalloc
=
0
;
static
struct
buf
arg
;
char
*
p
;
time_t
internaldate
=
time
(
0
);
unsigned
size
=
0
;
int
r
;
char
inboxname
[
MAX_MAILBOX_PATH
];
struct
mailbox
mailbox
;
/* Parse flags */
c
=
getword
(
&
arg
);
if
(
c
==
'('
&&
!
arg
.
s
[
0
])
{
do
{
c
=
getword
(
&
arg
);
if
(
arg
.
s
[
0
]
==
'\\'
)
{
lcase
(
arg
.
s
);
if
(
!
strcmp
(
arg
.
s
,
"
\\
seen"
)
&&
!
strcmp
(
arg
.
s
,
"
\\
answered"
)
&&
!
strcmp
(
arg
.
s
,
"
\\
flagged"
)
&&
!
strcmp
(
arg
.
s
,
"
\\
draft"
)
&&
!
strcmp
(
arg
.
s
,
"
\\
deleted"
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid system flag in Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
}
else
if
(
!
is_atom
(
arg
.
s
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid flag name %s in Append command
\r\n
"
,
tag
,
arg
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
if
(
nflags
==
flagalloc
)
{
flagalloc
+=
FLAGGROW
;
flag
=
(
char
**
)
xrealloc
((
char
*
)
flag
,
flagalloc
*
sizeof
(
char
*
));
}
flag
[
nflags
++
]
=
strsave
(
arg
.
s
);
}
while
(
c
==
' '
);
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing space or ) after flag name in Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing space after flag list in Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
c
=
getword
(
&
arg
);
}
/* Parse internaldate */
if
(
c
==
'\"'
&&
!
arg
.
s
[
0
])
{
prot_ungetc
(
c
,
imapd_in
);
c
=
getdatetime
(
&
internaldate
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid date-time in Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
c
=
getword
(
&
arg
);
}
if
(
arg
.
s
[
0
]
!=
'{'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
/* Read size from literal */
for
(
p
=
arg
.
s
+
1
;
*
p
&&
isdigit
(
*
p
);
p
++
)
{
size
=
size
*
10
+
*
p
-
'0'
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
*
p
!=
'}'
||
p
[
1
]
||
c
!=
'\n'
||
p
==
arg
.
s
+
1
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid literal in Append command
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
if
(
size
<
2
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_MESSAGE_NOBLANKLINE
));
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
/* Set up the append */
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
append_setup
(
&
mailbox
,
inboxname
,
MAILBOX_FORMAT_NORMAL
,
ACL_INSERT
,
size
);
}
else
{
r
=
append_setup
(
&
mailbox
,
name
,
MAILBOX_FORMAT_NORMAL
,
ACL_INSERT
,
size
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s%s
\r\n
"
,
tag
,
(
r
==
IMAP_MAILBOX_NONEXISTENT
&&
mboxlist_createmailboxcheck
(
name
,
0
,
imapd_userisadmin
,
imapd_userid
,
(
char
**
)
0
,
(
char
**
)
0
)
==
0
)
?
"[TRYCREATE] "
:
""
,
error_message
(
r
));
goto
freeflags
;
}
/* Tell client to send the message */
prot_printf
(
imapd_out
,
"+ go ahead
\r\n
"
);
prot_flush
(
imapd_out
);
/* Perform the rest of the append */
r
=
append_fromstream
(
&
mailbox
,
imapd_in
,
size
,
internaldate
,
flag
,
nflags
,
imapd_userid
);
mailbox_close
(
&
mailbox
);
/* Parse newline terminating command */
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"* BAD Junk after literal in APPEND command
\r\n
"
);
eatline
();
}
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Append completed
\r\n
"
,
tag
);
}
freeflags
:
while
(
nflags
--
)
{
free
(
flag
[
nflags
]);
}
if
(
flag
)
free
((
char
*
)
flag
);
}
/*
* Perform a SELECT/EXAMINE/BBOARD command
*/
cmd_select
(
tag
,
cmd
,
name
)
char
*
tag
;
char
*
cmd
;
char
*
name
;
{
struct
mailbox
mailbox
;
char
inboxname
[
MAX_MAILBOX_PATH
];
int
r
=
0
;
int
i
;
int
usage
;
int
doclose
=
0
;
inboxname
[
0
]
=
'\0'
;
if
(
imapd_mailbox
)
{
index_closemailbox
(
imapd_mailbox
);
mailbox_close
(
imapd_mailbox
);
imapd_mailbox
=
0
;
}
if
(
cmd
[
0
]
==
'B'
)
{
/* BBoard namespace is empty */
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
mailbox_open_header
(
inboxname
,
&
mailbox
);
}
else
{
r
=
mailbox_open_header
(
name
,
&
mailbox
);
}
if
(
!
r
)
{
doclose
=
1
;
r
=
mailbox_open_index
(
&
mailbox
);
}
if
(
!
r
&&
!
(
mailbox
.
myrights
&
ACL_READ
))
{
r
=
(
imapd_userisadmin
||
(
mailbox
.
myrights
&
ACL_LOOKUP
))
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
}
if
(
!
r
&&
chdir
(
mailbox
.
path
))
{
syslog
(
LOG_ERR
,
"IOERROR: changing directory to %s: %m"
,
mailbox
.
path
);
r
=
IMAP_IOERROR
;
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
if
(
doclose
)
mailbox_close
(
&
mailbox
);
return
;
}
mboxstruct
=
mailbox
;
imapd_mailbox
=
&
mboxstruct
;
index_newmailbox
(
imapd_mailbox
,
cmd
[
0
]
==
'E'
);
/* Examine command puts mailbox in read-only mode */
if
(
cmd
[
0
]
==
'E'
)
{
imapd_mailbox
->
myrights
&=
~
(
ACL_SEEN
|
ACL_WRITE
|
ACL_DELETE
);
}
if
(
imapd_mailbox
->
myrights
&
ACL_DELETE
)
{
/* Warn if mailbox is close to or over quota */
mailbox_read_quota
(
&
imapd_mailbox
->
quota
);
if
(
imapd_mailbox
->
quota
.
limit
>
0
)
{
usage
=
imapd_mailbox
->
quota
.
used
*
100
/
(
imapd_mailbox
->
quota
.
limit
*
QUOTA_UNITS
);
if
(
usage
>=
100
)
{
prot_printf
(
imapd_out
,
"* NO [ALERT] %s
\r\n
"
,
error_message
(
IMAP_NO_OVERQUOTA
));
}
else
if
(
usage
>
config_getint
(
"quotawarn"
,
90
))
{
prot_printf
(
imapd_out
,
"* NO [ALERT] "
);
prot_printf
(
imapd_out
,
error_message
(
IMAP_NO_CLOSEQUOTA
),
usage
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
}
}
prot_printf
(
imapd_out
,
"%s OK [READ-%s] %s completed
\r\n
"
,
tag
,
(
imapd_mailbox
->
myrights
&
(
ACL_WRITE
|
ACL_DELETE
))
?
"WRITE"
:
"ONLY"
,
cmd
);
proc_register
(
"imapd"
,
imapd_clienthost
,
imapd_userid
,
inboxname
[
0
]
?
inboxname
:
name
);
syslog
(
LOG_INFO
,
"open: user %s opened %s"
,
imapd_userid
,
name
);
}
/*
* Perform a CLOSE command
*/
cmd_close
(
tag
)
char
*
tag
;
{
int
r
;
if
(
!
(
imapd_mailbox
->
myrights
&
ACL_DELETE
))
r
=
0
;
else
{
r
=
mailbox_expunge
(
imapd_mailbox
,
1
,
(
int
(
*
)())
0
,
(
char
*
)
0
);
}
index_closemailbox
(
imapd_mailbox
);
mailbox_close
(
imapd_mailbox
);
imapd_mailbox
=
0
;
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Close completed
\r\n
"
,
tag
);
}
}
/*
* Parse and perform a FETCH/UID FETCH command
* The command has been parsed up to and including
* the sequence
*/
cmd_fetch
(
tag
,
sequence
,
usinguid
)
char
*
tag
;
char
*
sequence
;
int
usinguid
;
{
char
*
cmd
=
usinguid
?
"UID Fetch"
:
"Fetch"
;
static
struct
buf
fetchatt
,
fieldname
;
int
c
,
i
;
int
inlist
=
0
;
int
fetchitems
=
0
;
struct
fetchargs
fetchargs
;
char
*
p
,
*
section
;
fetchargs
=
zerofetchargs
;
c
=
getword
(
&
fetchatt
);
if
(
c
==
'('
&&
!
fetchatt
.
s
[
0
])
{
inlist
=
1
;
c
=
getword
(
&
fetchatt
);
}
for
(;;)
{
lcase
(
fetchatt
.
s
);
switch
(
fetchatt
.
s
[
0
])
{
case
'a'
:
if
(
!
inlist
&&
!
strcmp
(
fetchatt
.
s
,
"all"
))
{
fetchitems
|=
FETCH_ALL
;
}
else
goto
badatt
;
break
;
case
'b'
:
if
(
!
strcmp
(
fetchatt
.
s
,
"body"
))
{
fetchitems
|=
FETCH_BODY
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"bodystructure"
))
{
fetchitems
|=
FETCH_BODYSTRUCTURE
;
}
else
if
(
!
strncmp
(
fetchatt
.
s
,
"body["
,
5
)
||
!
strncmp
(
fetchatt
.
s
,
"body.peek["
,
10
))
{
p
=
section
=
fetchatt
.
s
+
5
;
if
(
*
p
==
'p'
)
{
p
=
section
+=
5
;
}
else
{
fetchitems
|=
FETCH_SETSEEN
;
}
while
(
isdigit
(
*
p
)
||
*
p
==
'.'
)
{
if
(
*
p
==
'.'
&&
(
p
==
section
||
!
isdigit
(
p
[
1
])))
break
;
p
++
;
}
if
(
p
==
section
||
*
p
!=
']'
||
p
[
1
])
{
prot_printf
(
imapd_out
,
"%s BAD Invalid body section
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
*
p
=
'\0'
;
appendstrlist
(
&
fetchargs
.
bodysections
,
section
);
}
else
goto
badatt
;
break
;
case
'e'
:
if
(
!
strcmp
(
fetchatt
.
s
,
"envelope"
))
{
fetchitems
|=
FETCH_ENVELOPE
;
}
else
goto
badatt
;
break
;
case
'f'
:
if
(
!
inlist
&&
!
strcmp
(
fetchatt
.
s
,
"fast"
))
{
fetchitems
|=
FETCH_FAST
;
}
else
if
(
!
inlist
&&
!
strcmp
(
fetchatt
.
s
,
"full"
))
{
fetchitems
|=
FETCH_FULL
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"flags"
))
{
fetchitems
|=
FETCH_FLAGS
;
}
else
goto
badatt
;
break
;
case
'i'
:
if
(
!
strcmp
(
fetchatt
.
s
,
"internaldate"
))
{
fetchitems
|=
FETCH_INTERNALDATE
;
}
else
goto
badatt
;
break
;
case
'r'
:
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822"
))
{
fetchitems
|=
FETCH_RFC822
|
FETCH_SETSEEN
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.header"
))
{
fetchitems
|=
FETCH_HEADER
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.peek"
))
{
fetchitems
|=
FETCH_RFC822
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.size"
))
{
fetchitems
|=
FETCH_SIZE
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.text"
))
{
fetchitems
|=
FETCH_TEXT
|
FETCH_SETSEEN
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.text.peek"
))
{
fetchitems
|=
FETCH_TEXT
;
}
else
if
(
!
strcmp
(
fetchatt
.
s
,
"rfc822.header.lines"
)
||
!
strcmp
(
fetchatt
.
s
,
"rfc822.header.lines.not"
))
{
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'('
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required open parenthesis in %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
do
{
c
=
getastring
(
&
fieldname
);
for
(
p
=
fieldname
.
s
;
*
p
;
p
++
)
{
if
(
*
p
<=
' '
||
*
p
&
0x80
||
*
p
==
':'
)
break
;
}
if
(
*
p
||
!*
fieldname
.
s
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid field-name in %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
appendstrlist
(
strlen
(
fetchatt
.
s
)
==
19
?
&
fetchargs
.
headers
:
&
fetchargs
.
headers_not
,
fieldname
.
s
);
if
(
strlen
(
fetchatt
.
s
)
==
19
&&
!
(
fetchitems
&
FETCH_UNCACHEDHEADER
))
{
for
(
i
=
0
;
i
<
mailbox_num_cache_header
;
i
++
)
{
if
(
!
strcasecmp
(
mailbox_cache_header_name
[
i
],
fieldname
.
s
))
break
;
}
if
(
i
==
mailbox_num_cache_header
)
{
fetchitems
|=
FETCH_UNCACHEDHEADER
;
}
}
}
while
(
c
==
' '
);
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required close parenthesis in %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
c
=
prot_getc
(
imapd_in
);
}
else
goto
badatt
;
break
;
case
'u'
:
if
(
!
strcmp
(
fetchatt
.
s
,
"uid"
))
{
fetchitems
|=
FETCH_UID
;
}
else
goto
badatt
;
break
;
default
:
badatt
:
prot_printf
(
imapd_out
,
"%s BAD Invalid %s attribute %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
if
(
inlist
&&
c
==
' '
)
c
=
getword
(
&
fetchatt
);
else
break
;
}
if
(
inlist
&&
c
==
')'
)
{
inlist
=
0
;
c
=
prot_getc
(
imapd_in
);
}
if
(
inlist
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing close parenthesis in %s
\r\n
"
,
tag
,
cmd
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeargs
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to %s
\r\n
"
,
tag
,
cmd
);
eatline
();
goto
freeargs
;
}
if
(
!
fetchitems
&&
!
fetchargs
.
bodysections
&&
!
fetchargs
.
headers
&&
!
fetchargs
.
headers_not
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s
\r\n
"
,
tag
,
cmd
);
goto
freeargs
;
}
if
(
usinguid
)
{
fetchitems
|=
FETCH_UID
;
index_check
(
imapd_mailbox
,
1
,
0
);
}
fetchargs
.
fetchitems
=
fetchitems
;
index_fetch
(
imapd_mailbox
,
sequence
,
usinguid
,
&
fetchargs
);
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
cmd
);
freeargs
:
freestrlist
(
fetchargs
.
bodysections
);
freestrlist
(
fetchargs
.
headers
);
freestrlist
(
fetchargs
.
headers_not
);
}
/*
* Perform a PARTIAL command
*/
cmd_partial
(
tag
,
msgno
,
data
,
start
,
count
)
char
*
tag
;
char
*
msgno
;
char
*
data
;
char
*
start
;
char
*
count
;
{
char
*
p
;
struct
fetchargs
fetchargs
;
int
r
;
char
*
section
;
fetchargs
=
zerofetchargs
;
for
(
p
=
msgno
;
*
p
;
p
++
)
{
if
(
!
isdigit
(
*
p
))
break
;
}
if
(
*
p
||
!*
msgno
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid message number
\r\n
"
,
tag
);
return
;
}
lcase
(
data
);
if
(
!
strcmp
(
data
,
"rfc822"
))
{
fetchargs
.
fetchitems
=
FETCH_RFC822
|
FETCH_SETSEEN
;
}
else
if
(
!
strcmp
(
data
,
"rfc822.header"
))
{
fetchargs
.
fetchitems
=
FETCH_HEADER
;
}
else
if
(
!
strcmp
(
data
,
"rfc822.peek"
))
{
fetchargs
.
fetchitems
=
FETCH_RFC822
;
}
else
if
(
!
strcmp
(
data
,
"rfc822.text"
))
{
fetchargs
.
fetchitems
=
FETCH_TEXT
|
FETCH_SETSEEN
;
}
else
if
(
!
strcmp
(
data
,
"rfc822.text.peek"
))
{
fetchargs
.
fetchitems
=
FETCH_TEXT
;
}
else
if
(
!
strncmp
(
data
,
"body["
,
5
)
||
!
strncmp
(
data
,
"body.peek["
,
10
))
{
p
=
section
=
data
+
5
;
if
(
*
p
==
'p'
)
{
p
=
section
+=
5
;
}
else
{
fetchargs
.
fetchitems
=
FETCH_SETSEEN
;
}
while
(
isdigit
(
*
p
)
||
*
p
==
'.'
)
{
if
(
*
p
==
'.'
&&
(
p
==
section
||
!
isdigit
(
p
[
1
])))
break
;
p
++
;
}
if
(
p
==
section
||
*
p
!=
']'
||
p
[
1
])
{
prot_printf
(
imapd_out
,
"%s BAD Invalid body section
\r\n
"
,
tag
);
freestrlist
(
fetchargs
.
bodysections
);
return
;
}
*
p
=
'\0'
;
appendstrlist
(
&
fetchargs
.
bodysections
,
section
);
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Partial item
\r\n
"
,
tag
);
freestrlist
(
fetchargs
.
bodysections
);
return
;
}
for
(
p
=
start
;
*
p
;
p
++
)
{
if
(
!
isdigit
(
*
p
))
break
;
fetchargs
.
start_octet
=
fetchargs
.
start_octet
*
10
+
*
p
-
'0'
;
}
if
(
*
p
||
!
fetchargs
.
start_octet
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid starting octet
\r\n
"
,
tag
);
freestrlist
(
fetchargs
.
bodysections
);
return
;
}
for
(
p
=
count
;
*
p
;
p
++
)
{
if
(
!
isdigit
(
*
p
))
break
;
fetchargs
.
octet_count
=
fetchargs
.
octet_count
*
10
+
*
p
-
'0'
;
}
if
(
*
p
||
!*
count
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid octet count
\r\n
"
,
tag
);
freestrlist
(
fetchargs
.
bodysections
);
return
;
}
index_fetch
(
imapd_mailbox
,
msgno
,
0
,
&
fetchargs
);
index_check
(
imapd_mailbox
,
0
,
0
);
prot_printf
(
imapd_out
,
"%s OK Partial completed
\r\n
"
,
tag
);
freestrlist
(
fetchargs
.
bodysections
);
}
/*
* Parse and perform a STORE/UID STORE command
* The command has been parsed up to and including
* the FLAGS/+FLAGS/-FLAGS
*/
cmd_store
(
tag
,
sequence
,
operation
,
usinguid
)
char
*
tag
;
char
*
sequence
;
char
*
operation
;
int
usinguid
;
{
char
*
cmd
=
usinguid
?
"UID Store"
:
"Store"
;
struct
storeargs
storeargs
;
static
struct
storeargs
zerostoreargs
;
static
struct
buf
flagname
;
int
len
,
c
;
char
**
flag
=
0
;
int
nflags
=
0
,
flagalloc
=
0
;
int
flagsparsed
=
0
,
inlist
=
0
;
int
r
;
storeargs
=
zerostoreargs
;
lcase
(
operation
);
len
=
strlen
(
operation
);
if
(
len
>
7
&&
!
strcmp
(
operation
+
len
-7
,
".silent"
))
{
storeargs
.
silent
=
1
;
operation
[
len
-7
]
=
'\0'
;
}
if
(
!
strcmp
(
operation
,
"+flags"
))
{
storeargs
.
operation
=
STORE_ADD
;
}
else
if
(
!
strcmp
(
operation
,
"-flags"
))
{
storeargs
.
operation
=
STORE_REMOVE
;
}
else
if
(
!
strcmp
(
operation
,
"flags"
))
{
storeargs
.
operation
=
STORE_REPLACE
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid %s attribute
\r\n
"
,
tag
,
cmd
);
eatline
();
return
;
}
for
(;;)
{
c
=
getword
(
&
flagname
);
if
(
c
==
'('
&&
!
flagname
.
s
[
0
]
&&
!
flagsparsed
&&
!
inlist
)
{
inlist
=
1
;
continue
;
}
if
(
flagname
.
s
[
0
]
==
'\\'
)
{
lcase
(
flagname
.
s
);
if
(
!
strcmp
(
flagname
.
s
,
"
\\
seen"
))
{
storeargs
.
seen
=
1
;
}
else
if
(
!
strcmp
(
flagname
.
s
,
"
\\
answered"
))
{
storeargs
.
system_flags
|=
FLAG_ANSWERED
;
}
else
if
(
!
strcmp
(
flagname
.
s
,
"
\\
flagged"
))
{
storeargs
.
system_flags
|=
FLAG_FLAGGED
;
}
else
if
(
!
strcmp
(
flagname
.
s
,
"
\\
deleted"
))
{
storeargs
.
system_flags
|=
FLAG_DELETED
;
}
else
if
(
!
strcmp
(
flagname
.
s
,
"
\\
draft"
))
{
storeargs
.
system_flags
|=
FLAG_DRAFT
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid system flag in %s command
\r\n
"
,
tag
,
cmd
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
}
else
if
(
!
is_atom
(
flagname
.
s
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid flag name %s in %s command
\r\n
"
,
tag
,
flagname
.
s
,
cmd
);
if
(
c
!=
'\n'
)
eatline
();
goto
freeflags
;
}
else
{
if
(
nflags
==
flagalloc
)
{
flagalloc
+=
FLAGGROW
;
flag
=
(
char
**
)
xrealloc
((
char
*
)
flag
,
flagalloc
*
sizeof
(
char
*
));
}
flag
[
nflags
++
]
=
strsave
(
flagname
.
s
);
}
flagsparsed
++
;
if
(
c
!=
' '
)
break
;
}
if
(
inlist
&&
c
==
')'
)
{
inlist
=
0
;
c
=
prot_getc
(
imapd_in
);
}
if
(
inlist
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing close parenthesis in %s
\r\n
"
,
tag
,
cmd
);
if
(
c
!=
'\n'
)
eatline
();
return
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to %s
\r\n
"
,
tag
,
cmd
);
eatline
();
return
;
}
if
(
!
flagsparsed
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s
\r\n
"
,
tag
,
cmd
);
return
;
}
r
=
index_store
(
imapd_mailbox
,
sequence
,
usinguid
,
&
storeargs
,
flag
,
nflags
);
if
(
usinguid
)
{
index_check
(
imapd_mailbox
,
1
,
0
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
cmd
);
}
freeflags
:
while
(
nflags
--
)
{
free
(
flag
[
nflags
]);
}
if
(
flag
)
free
((
char
*
)
flag
);
}
cmd_search
(
tag
,
usinguid
)
char
*
tag
;
int
usinguid
;
{
int
c
;
int
charset
=
0
;
struct
searchargs
*
searchargs
;
static
struct
searchargs
zerosearchargs
;
searchargs
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
searchargs
=
zerosearchargs
;
c
=
getsearchprogram
(
tag
,
searchargs
,
&
charset
,
1
);
if
(
c
==
EOF
)
{
eatline
();
freesearchargs
(
searchargs
);
return
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to Search
\r\n
"
,
tag
);
eatline
();
freesearchargs
(
searchargs
);
return
;
}
if
(
charset
==
-1
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_UNRECOGNIZED_CHARSET
));
}
else
{
index_search
(
imapd_mailbox
,
searchargs
,
usinguid
);
prot_printf
(
imapd_out
,
"%s OK Search completed
\r\n
"
,
tag
);
}
freesearchargs
(
searchargs
);
}
/*
* Perform a COPY/UID COPY command
*/
cmd_copy
(
tag
,
sequence
,
name
,
usinguid
)
char
*
tag
;
char
*
sequence
;
char
*
name
;
int
usinguid
;
{
char
*
cmd
=
usinguid
?
"UID Copy"
:
"Copy"
;
int
r
;
char
inboxname
[
MAX_MAILBOX_PATH
];
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
index_copy
(
imapd_mailbox
,
sequence
,
usinguid
,
inboxname
);
}
else
{
r
=
index_copy
(
imapd_mailbox
,
sequence
,
usinguid
,
name
);
}
index_check
(
imapd_mailbox
,
usinguid
,
0
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
cmd
);
}
}
/*
* Perform an EXPUNGE command
*/
cmd_expunge
(
tag
)
char
*
tag
;
{
int
r
;
if
(
!
(
imapd_mailbox
->
myrights
&
ACL_DELETE
))
r
=
IMAP_PERMISSION_DENIED
;
else
{
r
=
mailbox_expunge
(
imapd_mailbox
,
1
,
(
int
(
*
)())
0
,
(
char
*
)
0
);
}
index_check
(
imapd_mailbox
,
0
,
0
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Expunge completed
\r\n
"
,
tag
);
}
}
/*
* Perform a CREATE command
*/
cmd_create
(
tag
,
name
,
partition
)
char
*
tag
;
char
*
name
;
char
*
partition
;
{
int
r
;
if
(
partition
&&
!
imapd_userisadmin
)
{
r
=
IMAP_PERMISSION_DENIED
;
}
else
if
(
name
[
0
]
&&
name
[
strlen
(
name
)
-1
]
==
'.'
)
{
prot_printf
(
imapd_out
,
"%s OK Create of non-terminal names is unnecessary
\r\n
"
,
tag
);
return
;
}
else
{
r
=
mboxlist_createmailbox
(
name
,
MAILBOX_FORMAT_NORMAL
,
partition
,
imapd_userisadmin
,
imapd_userid
);
}
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Create completed
\r\n
"
,
tag
);
}
}
/*
* Perform a DELETE command
*/
cmd_delete
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
r
;
r
=
mboxlist_deletemailbox
(
name
,
imapd_userisadmin
,
imapd_userid
,
1
);
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Delete completed
\r\n
"
,
tag
);
}
}
/*
* Perform a RENAME command
*/
cmd_rename
(
tag
,
oldname
,
newname
,
partition
)
char
*
tag
;
char
*
oldname
;
char
*
newname
;
char
*
partition
;
{
int
r
;
if
(
partition
&&
!
imapd_userisadmin
)
{
r
=
IMAP_PERMISSION_DENIED
;
}
else
{
r
=
mboxlist_renamemailbox
(
oldname
,
newname
,
partition
,
imapd_userisadmin
,
imapd_userid
);
}
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK Rename completed
\r\n
"
,
tag
);
}
}
/*
* Perform a FIND command
*/
cmd_find
(
tag
,
namespace
,
pattern
)
char
*
tag
;
char
*
namespace
;
char
*
pattern
;
{
char
*
p
;
lcase
(
namespace
);
for
(
p
=
pattern
;
*
p
;
p
++
)
{
if
(
*
p
==
'%'
)
*
p
=
'?'
;
}
if
(
!
strcmp
(
namespace
,
"mailboxes"
))
{
mboxlist_findsub
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
mailboxdata
);
}
else
if
(
!
strcmp
(
namespace
,
"all.mailboxes"
))
{
mboxlist_findall
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
mailboxdata
);
}
else
if
(
!
strcmp
(
namespace
,
"bboards"
)
||
!
strcmp
(
namespace
,
"all.bboards"
))
{
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid FIND subcommand
\r\n
"
,
tag
);
return
;
}
prot_printf
(
imapd_out
,
"%s OK Find completed
\r\n
"
,
tag
);
}
/*
* Perform a LIST or LSUB command
*/
cmd_list
(
tag
,
subscribed
,
reference
,
pattern
)
char
*
tag
;
int
subscribed
;
char
*
reference
;
char
*
pattern
;
{
char
buf
[
MAX_MAILBOX_PATH
];
/* Handle name-in-reference */
if
(
pattern
[
0
]
==
'.'
)
{
strcpy
(
buf
,
reference
);
if
(
*
reference
&&
reference
[
strlen
(
reference
)
-1
]
==
'.'
)
{
buf
[
strlen
(
reference
)
-1
]
=
'\0'
;
}
strcat
(
buf
,
pattern
);
pattern
=
buf
;
}
if
(
subscribed
)
{
mboxlist_findsub
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
lsubdata
);
lsubdata
((
char
*
)
0
,
0
,
0
);
}
else
{
mboxlist_findall
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
listdata
);
listdata
((
char
*
)
0
,
0
,
0
);
}
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
subscribed
?
"LSUB"
:
"LIST"
);
}
/*
* Perform a SUBSCRIBE (add is nonzero) or
* UNSUBSCRIBE (add is zero) command
*/
cmd_changesub
(
tag
,
namespace
,
name
,
add
)
char
*
tag
;
char
*
namespace
;
char
*
name
;
int
add
;
{
int
r
;
if
(
namespace
)
lcase
(
namespace
);
if
(
!
namespace
||
!
strcmp
(
namespace
,
"mailbox"
))
{
r
=
mboxlist_changesub
(
name
,
imapd_userid
,
add
);
}
else
if
(
!
strcmp
(
namespace
,
"bboard"
))
{
r
=
add
?
IMAP_MAILBOX_NONEXISTENT
:
0
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid %s subcommand
\r\n
"
,
tag
,
add
?
"Subscribe"
:
"Unsubscribe"
);
return
;
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
add
?
"Subscribe"
:
"Unsubscribe"
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
add
?
"Subscribe"
:
"Unsubscribe"
);
}
}
/*
* Perform a GETACL command
*/
cmd_getacl
(
tag
,
namespace
,
name
)
char
*
tag
;
char
*
namespace
;
char
*
name
;
{
char
inboxname
[
MAX_MAILBOX_PATH
];
int
r
,
access
;
char
*
acl
;
char
*
rights
,
*
nextid
;
lcase
(
namespace
);
if
(
!
strcmp
(
namespace
,
"bboard"
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
if
(
!
strcmp
(
namespace
,
"mailbox"
))
{
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
mboxlist_lookup
(
inboxname
,
(
char
**
)
0
,
&
acl
);
}
else
{
r
=
mboxlist_lookup
(
name
,
(
char
**
)
0
,
&
acl
);
}
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Getacl subcommand
\r\n
"
,
tag
);
return
;
}
if
(
!
r
)
{
access
=
acl_myrights
(
acl
);
if
(
imapd_userisadmin
)
{
access
|=
ACL_ADMIN
;
}
else
if
(
!
strchr
(
imapd_userid
,
'.'
)
&&
!
strncasecmp
(
name
,
"user."
,
5
)
&&
!
strncasecmp
(
name
+
5
,
imapd_userid
,
strlen
(
imapd_userid
))
&&
name
[
5
+
strlen
(
imapd_userid
)]
==
'.'
)
{
access
|=
ACL_ADMIN
;
}
if
(
!
(
access
&
(
ACL_READ
|
ACL_ADMIN
)))
{
r
=
(
access
&
ACL_LOOKUP
)
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
}
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
while
(
acl
)
{
rights
=
strchr
(
acl
,
'\t'
);
if
(
!
rights
)
break
;
*
rights
++
=
'\0'
;
nextid
=
strchr
(
rights
,
'\t'
);
if
(
!
nextid
)
break
;
*
nextid
++
=
'\0'
;
prot_printf
(
imapd_out
,
"* ACL MAILBOX "
);
printastring
(
name
);
prot_printf
(
imapd_out
,
" "
);
printastring
(
acl
);
prot_printf
(
imapd_out
,
" "
);
printastring
(
rights
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
acl
=
nextid
;
}
prot_printf
(
imapd_out
,
"%s OK Getacl completed
\r\n
"
,
tag
);
}
/*
* Perform a MYRIGHTS command
*/
cmd_myrights
(
tag
,
namespace
,
name
)
char
*
tag
;
char
*
namespace
;
char
*
name
;
{
char
inboxname
[
MAX_MAILBOX_PATH
];
int
r
,
rights
;
char
*
acl
;
char
str
[
ACL_MAXSTR
];
lcase
(
namespace
);
if
(
!
strcmp
(
namespace
,
"bboard"
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
if
(
!
strcmp
(
namespace
,
"mailbox"
))
{
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
mboxlist_lookup
(
inboxname
,
(
char
**
)
0
,
&
acl
);
}
else
{
r
=
mboxlist_lookup
(
name
,
(
char
**
)
0
,
&
acl
);
}
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Myrights subcommand
\r\n
"
,
tag
);
return
;
}
if
(
!
r
)
{
rights
=
acl_myrights
(
acl
);
/* Add in implicit rights */
if
(
imapd_userisadmin
||
!
strcasecmp
(
name
,
"inbox"
))
{
rights
|=
ACL_LOOKUP
|
ACL_ADMIN
;
}
if
(
!
strchr
(
imapd_userid
,
'.'
)
&&
!
strncasecmp
(
name
,
"user."
,
5
)
&&
!
strncasecmp
(
name
+
5
,
imapd_userid
,
strlen
(
imapd_userid
))
&&
name
[
5
+
strlen
(
imapd_userid
)]
==
'.'
)
{
rights
|=
ACL_LOOKUP
|
ACL_ADMIN
;
}
if
(
!
rights
)
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"* MYRIGHTS MAILBOX "
);
printastring
(
name
);
prot_printf
(
imapd_out
,
" "
);
printastring
(
acl_masktostr
(
rights
,
str
));
prot_printf
(
imapd_out
,
"
\r\n
%s OK Myrights completed
\r\n
"
,
tag
);
}
/*
* Perform a SETACL command
*/
cmd_setacl
(
tag
,
namespace
,
name
,
identifier
,
rights
)
char
*
tag
;
char
*
namespace
;
char
*
name
;
char
*
identifier
;
char
*
rights
;
{
int
r
;
char
*
cmd
=
rights
?
"Setacl"
:
"Deleteacl"
;
lcase
(
namespace
);
if
(
!
strcmp
(
namespace
,
"bboard"
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
else
if
(
!
strcmp
(
namespace
,
"mailbox"
))
{
r
=
mboxlist_setacl
(
name
,
identifier
,
rights
,
imapd_userisadmin
,
imapd_userid
);
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid %s subcommand
\r\n
"
,
tag
,
cmd
);
return
;
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK %s completed
\r\n
"
,
tag
,
cmd
);
}
/*
* Perform a GETQUOTA command
*/
cmd_getquota
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
r
;
struct
quota
quota
;
char
buf
[
MAX_MAILBOX_PATH
];
lcase
(
name
);
quota
.
root
=
name
;
quota
.
file
=
0
;
if
(
!
imapd_userisadmin
)
r
=
IMAP_PERMISSION_DENIED
;
else
{
sprintf
(
buf
,
"%s%s%s"
,
config_dir
,
FNAME_QUOTADIR
,
quota
.
root
);
quota
.
file
=
fopen
(
buf
,
"r+"
);
if
(
!
quota
.
file
)
{
r
=
IMAP_QUOTAROOT_NONEXISTENT
;
}
else
r
=
mailbox_read_quota
(
&
quota
);
}
if
(
!
r
)
{
prot_printf
(
imapd_out
,
"* QUOTA "
);
printastring
(
quota
.
root
);
prot_printf
(
imapd_out
,
" ("
);
if
(
quota
.
limit
>=
0
)
{
prot_printf
(
imapd_out
,
"STORAGE %u %d"
,
quota
.
used
/
QUOTA_UNITS
,
quota
.
limit
);
}
prot_printf
(
imapd_out
,
")
\r\n
"
);
}
if
(
quota
.
file
)
fclose
(
quota
.
file
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK Getquotaroot completed
\r\n
"
,
tag
);
}
/*
* Perform a GETQUOTAROOT command
*/
cmd_getquotaroot
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
char
inboxname
[
MAX_MAILBOX_PATH
];
struct
mailbox
mailbox
;
int
r
;
int
doclose
=
0
;
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
mailbox_open_header
(
inboxname
,
&
mailbox
);
}
else
{
r
=
mailbox_open_header
(
name
,
&
mailbox
);
}
if
(
!
r
)
{
doclose
=
1
;
if
(
!
imapd_userisadmin
&&
!
(
mailbox
.
myrights
&
ACL_READ
))
{
r
=
(
mailbox
.
myrights
&
ACL_LOOKUP
)
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
}
}
if
(
!
r
)
{
prot_printf
(
imapd_out
,
"* QUOTAROOT "
);
printastring
(
name
);
if
(
mailbox
.
quota
.
root
)
{
prot_printf
(
imapd_out
,
" "
);
printastring
(
mailbox
.
quota
.
root
);
r
=
mailbox_read_quota
(
&
mailbox
.
quota
);
if
(
!
r
)
{
prot_printf
(
imapd_out
,
"
\r\n
* QUOTA "
);
printastring
(
mailbox
.
quota
.
root
);
prot_printf
(
imapd_out
,
" ("
);
if
(
mailbox
.
quota
.
limit
>=
0
)
{
prot_printf
(
imapd_out
,
"STORAGE %u %d"
,
mailbox
.
quota
.
used
/
QUOTA_UNITS
,
mailbox
.
quota
.
limit
);
}
prot_putc
(
')'
,
imapd_out
);
}
}
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
if
(
doclose
)
mailbox_close
(
&
mailbox
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK Getquotaroot completed
\r\n
"
,
tag
);
}
/*
* Parse and perform a SETQUOTA command
* The command has been parsed up to the resource list
*/
cmd_setquota
(
tag
,
quotaroot
)
char
*
tag
;
char
*
quotaroot
;
{
int
newquota
=
-1
;
int
badresource
=
0
;
int
c
;
static
struct
buf
arg
;
char
*
p
;
int
r
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'('
)
goto
badlist
;
c
=
getword
(
&
arg
);
if
(
c
!=
')'
||
arg
.
s
[
0
]
!=
'\0'
)
{
for
(;;)
{
if
(
c
!=
' '
)
goto
badlist
;
if
(
strcasecmp
(
arg
.
s
,
"storage"
)
!=
0
)
badresource
=
1
;
c
=
getword
(
&
arg
);
if
(
c
!=
' '
&&
c
!=
')'
)
goto
badlist
;
if
(
arg
.
s
[
0
]
==
'\0'
)
goto
badlist
;
newquota
=
0
;
for
(
p
=
arg
.
s
;
*
p
;
p
++
)
{
if
(
!
isdigit
(
*
p
))
goto
badlist
;
newquota
=
newquota
*
10
+
*
p
-
'0'
;
}
if
(
c
==
')'
)
break
;
}
}
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to SETQUOTA
\r\n
"
,
tag
);
eatline
();
return
;
}
if
(
badresource
)
r
=
IMAP_UNSUPPORTED_QUOTA
;
else
if
(
!
imapd_userisadmin
)
r
=
IMAP_PERMISSION_DENIED
;
else
{
r
=
mboxlist_setquota
(
quotaroot
,
newquota
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK Setquota completed
\r\n
"
,
tag
);
return
;
badlist
:
prot_printf
(
imapd_out
,
"%s BAD Invalid quota list in Setquota
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
}
/*
* Parse and perform a STATUS command
* The command has been parsed up to the attribute list
*/
cmd_status
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
c
;
int
statusitems
=
0
;
static
struct
buf
arg
;
struct
mailbox
mailbox
;
char
inboxname
[
MAX_MAILBOX_PATH
];
int
r
=
0
;
int
doclose
=
0
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'('
)
goto
badlist
;
c
=
getword
(
&
arg
);
if
(
arg
.
s
[
0
]
==
'\0'
)
goto
badlist
;
for
(;;)
{
lcase
(
arg
.
s
);
if
(
!
strcmp
(
arg
.
s
,
"messages"
))
{
statusitems
|=
STATUS_MESSAGES
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"recent"
))
{
statusitems
|=
STATUS_RECENT
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"uid-next"
))
{
statusitems
|=
STATUS_UID_NEXT
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"uid-validity"
))
{
statusitems
|=
STATUS_UID_VALIDITY
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"unseen"
))
{
statusitems
|=
STATUS_UNSEEN
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"unseen"
))
{
statusitems
|=
STATUS_UNSEEN
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"update-number"
))
{
statusitems
|=
STATUS_UPDATE_NUMBER
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Status attribute %s
\r\n
"
,
tag
,
arg
.
s
);
if
(
c
!=
'\n'
)
eatline
();
return
;
}
if
(
c
==
' '
)
c
=
getword
(
&
arg
);
else
break
;
}
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing close parenthesis in Status
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
return
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to Status
\r\n
"
,
tag
);
eatline
();
return
;
}
/*
* Perform a full checkpoint of any open mailbox, in case we're
* doing a STATUS check of the current mailbox.
*/
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
1
);
}
if
(
strcasecmp
(
name
,
"inbox"
)
==
0
&&
!
strchr
(
imapd_userid
,
'.'
)
&&
strlen
(
imapd_userid
)
+
6
<=
MAX_MAILBOX_PATH
)
{
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
imapd_userid
);
r
=
mailbox_open_header
(
inboxname
,
&
mailbox
);
}
else
{
r
=
mailbox_open_header
(
name
,
&
mailbox
);
}
if
(
!
r
)
{
doclose
=
1
;
r
=
mailbox_open_index
(
&
mailbox
);
}
if
(
!
r
&&
!
(
mailbox
.
myrights
&
ACL_READ
))
{
r
=
(
imapd_userisadmin
||
(
mailbox
.
myrights
&
ACL_LOOKUP
))
?
IMAP_PERMISSION_DENIED
:
IMAP_MAILBOX_NONEXISTENT
;
}
if
(
!
r
)
{
r
=
index_status
(
&
mailbox
,
name
,
statusitems
);
}
if
(
doclose
)
mailbox_close
(
&
mailbox
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK Status completed
\r\n
"
,
tag
);
return
;
badlist
:
prot_printf
(
imapd_out
,
"%s BAD Invalid status list in Status
\r\n
"
,
tag
);
if
(
c
!=
'\n'
)
eatline
();
}
/*
* Parse a word
* (token not containing whitespace, parens, or double quotes)
*/
#define BUFGROWSIZE 100
int
getword
(
buf
)
struct
buf
*
buf
;
{
int
c
;
int
len
=
0
;
if
(
buf
->
alloc
==
0
)
{
buf
->
alloc
=
BUFGROWSIZE
;
buf
->
s
=
xmalloc
(
buf
->
alloc
+
1
);
}
for
(;;)
{
c
=
prot_getc
(
imapd_in
);
if
(
c
==
EOF
||
isspace
(
c
)
||
c
==
'('
||
c
==
')'
||
c
==
'\"'
)
{
buf
->
s
[
len
]
=
'\0'
;
return
c
;
}
if
(
len
==
buf
->
alloc
)
{
buf
->
alloc
+=
BUFGROWSIZE
;
buf
->
s
=
xrealloc
(
buf
->
s
,
buf
->
alloc
+
1
);
}
buf
->
s
[
len
++
]
=
c
;
}
}
/*
* Parse an astring
* (atom, quoted-string, or literal)
*/
int
getastring
(
buf
)
struct
buf
*
buf
;
{
int
c
;
int
i
,
len
=
0
;
int
sawdigit
=
0
;
if
(
buf
->
alloc
==
0
)
{
buf
->
alloc
=
BUFGROWSIZE
;
buf
->
s
=
xmalloc
(
buf
->
alloc
+
1
);
}
c
=
prot_getc
(
imapd_in
);
switch
(
c
)
{
case
EOF
:
case
' '
:
case
'('
:
case
')'
:
case
'\r'
:
case
'\n'
:
/* Invalid starting character */
buf
->
s
[
0
]
=
'\0'
;
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
default
:
/*
* Atom -- server is liberal in accepting specials other
* than whitespace, parens, or double quotes
*/
for
(;;)
{
if
(
c
==
EOF
||
isspace
(
c
)
||
c
==
'('
||
c
==
')'
||
c
==
'\"'
)
{
buf
->
s
[
len
]
=
'\0'
;
return
c
;
}
if
(
len
==
buf
->
alloc
)
{
buf
->
alloc
+=
BUFGROWSIZE
;
buf
->
s
=
xrealloc
(
buf
->
s
,
buf
->
alloc
+
1
);
}
buf
->
s
[
len
++
]
=
c
;
c
=
prot_getc
(
imapd_in
);
}
case
'\"'
:
/*
* Quoted-string. Server is liberal in accepting qspecials
* other than double-quote, CR, and LF.
*/
for
(;;)
{
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\\'
)
{
c
=
prot_getc
(
imapd_in
);
}
else
if
(
c
==
'\"'
)
{
buf
->
s
[
len
]
=
'\0'
;
return
prot_getc
(
imapd_in
);
}
else
if
(
c
==
EOF
||
c
==
'\r'
||
c
==
'\n'
)
{
buf
->
s
[
len
]
=
'\0'
;
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
if
(
len
==
buf
->
alloc
)
{
buf
->
alloc
+=
BUFGROWSIZE
;
buf
->
s
=
xrealloc
(
buf
->
s
,
buf
->
alloc
+
1
);
}
buf
->
s
[
len
++
]
=
c
;
}
case
'{'
:
/* Literal */
buf
->
s
[
0
]
=
'\0'
;
while
((
c
=
prot_getc
(
imapd_in
))
!=
EOF
&&
isdigit
(
c
))
{
sawdigit
=
1
;
len
=
len
*
10
+
c
-
'0'
;
}
if
(
!
sawdigit
||
c
!=
'}'
)
{
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
if
(
len
>=
buf
->
alloc
)
{
buf
->
alloc
=
len
+
1
;
buf
->
s
=
xrealloc
(
buf
->
s
,
buf
->
alloc
+
1
);
}
prot_printf
(
imapd_out
,
"+ go ahead
\r\n
"
);
prot_flush
(
imapd_out
);
for
(
i
=
0
;
i
<
len
;
i
++
)
{
c
=
prot_getc
(
imapd_in
);
if
(
c
==
EOF
)
{
buf
->
s
[
len
]
=
'\0'
;
return
EOF
;
}
buf
->
s
[
i
]
=
c
;
}
buf
->
s
[
len
]
=
'\0'
;
if
(
strlen
(
buf
->
s
)
!=
len
)
return
EOF
;
/* Disallow imbedded NUL */
return
prot_getc
(
imapd_in
);
}
}
#define XX 127
/*
* Table for decoding base64
*/
static
const
char
index_64
[
256
]
=
{
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
62
,
XX
,
XX
,
XX
,
63
,
52
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
26
,
27
,
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
};
#define CHAR64(c) (index_64[(unsigned char)(c)])
/*
* Parse a base64_string
*/
int
getbase64string
(
buf
)
struct
buf
*
buf
;
{
int
c1
,
c2
,
c3
,
c4
;
int
i
,
len
=
0
;
if
(
buf
->
alloc
==
0
)
{
buf
->
alloc
=
BUFGROWSIZE
;
buf
->
s
=
xmalloc
(
buf
->
alloc
+
1
);
}
for
(;;)
{
c1
=
prot_getc
(
imapd_in
);
if
(
c1
==
'\r'
)
{
c1
=
prot_getc
(
imapd_in
);
if
(
c1
!=
'\n'
)
{
eatline
();
return
-1
;
}
return
len
;
}
else
if
(
c1
==
'\n'
)
return
len
;
if
(
CHAR64
(
c1
)
==
XX
)
{
eatline
();
return
-1
;
}
c2
=
prot_getc
(
imapd_in
);
if
(
CHAR64
(
c2
)
==
XX
)
{
if
(
c2
!=
'\n'
)
eatline
();
return
-1
;
}
c3
=
prot_getc
(
imapd_in
);
if
(
c3
!=
'='
&&
CHAR64
(
c3
)
==
XX
)
{
if
(
c3
!=
'\n'
)
eatline
();
return
-1
;
}
c4
=
prot_getc
(
imapd_in
);
if
(
c4
!=
'='
&&
CHAR64
(
c4
)
==
XX
)
{
if
(
c4
!=
'\n'
)
eatline
();
return
-1
;
}
if
(
len
+
3
>=
buf
->
alloc
)
{
buf
->
alloc
=
len
+
BUFGROWSIZE
;
buf
->
s
=
xrealloc
(
buf
->
s
,
buf
->
alloc
+
1
);
}
buf
->
s
[
len
++
]
=
((
CHAR64
(
c1
)
<<
2
)
|
((
CHAR64
(
c2
)
&
0x30
)
>>
4
));
if
(
c3
==
'='
)
{
c1
=
prot_getc
(
imapd_in
);
if
(
c1
==
'\r'
)
c1
=
prot_getc
(
imapd_in
);
if
(
c1
!=
'\n'
)
{
eatline
();
return
-1
;
}
if
(
c4
!=
'='
)
return
-1
;
return
len
;
}
buf
->
s
[
len
++
]
=
(((
CHAR64
(
c2
)
&
0xf
)
<<
4
)
|
((
CHAR64
(
c3
)
&
0x3c
)
>>
2
));
if
(
c4
==
'='
)
{
c1
=
prot_getc
(
imapd_in
);
if
(
c1
==
'\r'
)
c1
=
prot_getc
(
imapd_in
);
if
(
c1
!=
'\n'
)
{
eatline
();
return
-1
;
}
return
len
;
}
buf
->
s
[
len
++
]
=
(((
CHAR64
(
c3
)
&
0x3
)
<<
6
)
|
CHAR64
(
c4
));
}
}
/*
* Parse a search program
*/
int
getsearchprogram
(
tag
,
searchargs
,
charset
,
parsecharset
)
char
*
tag
;
struct
searchargs
*
searchargs
;
int
*
charset
;
int
parsecharset
;
{
int
c
;
do
{
c
=
getsearchcriteria
(
tag
,
searchargs
,
charset
,
parsecharset
);
parsecharset
=
0
;
}
while
(
c
==
' '
);
return
c
;
}
/*
* Parse a search criteria
*/
int
getsearchcriteria
(
tag
,
searchargs
,
charset
,
parsecharset
)
char
*
tag
;
struct
searchargs
*
searchargs
;
int
*
charset
;
int
parsecharset
;
{
static
struct
buf
criteria
,
arg
;
static
struct
searchargs
zerosearchargs
;
struct
searchargs
*
sub1
,
*
sub2
;
char
*
p
,
*
str
;
int
c
,
i
,
flag
,
size
;
time_t
start
,
end
;
c
=
getword
(
&
criteria
);
lcase
(
criteria
.
s
);
switch
(
criteria
.
s
[
0
])
{
case
'\0'
:
if
(
c
!=
'('
)
goto
badcri
;
c
=
getsearchprogram
(
tag
,
searchargs
,
charset
,
0
);
if
(
c
==
EOF
)
return
EOF
;
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required close paren in Search command
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
c
=
prot_getc
(
imapd_in
);
break
;
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
case
'*'
:
if
(
is_sequence
(
criteria
.
s
))
{
appendstrlist
(
&
searchargs
->
sequence
,
criteria
.
s
);
}
else
goto
badcri
;
break
;
case
'a'
:
if
(
!
strcmp
(
criteria
.
s
,
"answered"
))
{
searchargs
->
system_flags_set
|=
FLAG_ANSWERED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"all"
))
{
break
;
}
else
goto
badcri
;
break
;
case
'b'
:
if
(
!
strcmp
(
criteria
.
s
,
"before"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
before
||
searchargs
->
before
>
start
)
{
searchargs
->
before
=
start
;
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"bcc"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
bcc
,
str
);
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"body"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
body
,
str
);
}
}
else
goto
badcri
;
break
;
case
'c'
:
if
(
!
strcmp
(
criteria
.
s
,
"cc"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
cc
,
str
);
}
}
else
if
(
parsecharset
&&
!
strcmp
(
criteria
.
s
,
"charset"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
!=
' '
)
goto
missingarg
;
lcase
(
arg
.
s
);
*
charset
=
charset_lookupname
(
arg
.
s
);
}
else
goto
badcri
;
break
;
case
'd'
:
if
(
!
strcmp
(
criteria
.
s
,
"deleted"
))
{
searchargs
->
system_flags_set
|=
FLAG_DELETED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"draft"
))
{
searchargs
->
system_flags_set
|=
FLAG_DRAFT
;
}
else
goto
badcri
;
break
;
case
'f'
:
if
(
!
strcmp
(
criteria
.
s
,
"flagged"
))
{
searchargs
->
system_flags_set
|=
FLAG_FLAGGED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"from"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
from
,
str
);
}
}
else
goto
badcri
;
break
;
case
'h'
:
if
(
!
strcmp
(
criteria
.
s
,
"header"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
!=
' '
)
goto
missingarg
;
appendstrlist
(
&
searchargs
->
header_name
,
arg
.
s
);
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
header
,
str
);
}
}
else
goto
badcri
;
break
;
case
'k'
:
if
(
!
strcmp
(
criteria
.
s
,
"keyword"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
if
(
!
is_atom
(
arg
.
s
))
goto
badflag
;
lcase
(
arg
.
s
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
imapd_mailbox
->
flagname
[
flag
]
&&
!
strcmp
(
imapd_mailbox
->
flagname
[
flag
],
arg
.
s
))
break
;
}
if
(
flag
==
MAX_USER_FLAGS
)
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
break
;
}
searchargs
->
user_flags_set
[
flag
/
32
]
|=
1
<<
(
flag
&
31
);
}
else
goto
badcri
;
break
;
case
'l'
:
if
(
!
strcmp
(
criteria
.
s
,
"larger"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
size
=
0
;
for
(
p
=
arg
.
s
;
*
p
&&
isdigit
(
*
p
);
p
++
)
{
size
=
size
*
10
+
*
p
-
'0'
;
}
if
(
!
arg
.
s
||
*
p
)
goto
badnumber
;
if
(
size
>
searchargs
->
larger
)
searchargs
->
larger
=
size
;
}
else
goto
badcri
;
break
;
case
'n'
:
if
(
!
strcmp
(
criteria
.
s
,
"not"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
sub1
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
sub1
=
zerosearchargs
;
c
=
getsearchcriteria
(
tag
,
sub1
,
charset
,
0
);
if
(
c
==
EOF
)
{
freesearchargs
(
sub1
);
return
EOF
;
}
#if 0
/* Have to pay attenton to DeMorgan's Law */
/* Pull the trivial stuff into searchargs */
if (sub1->smaller && sub1->smaller > searchargs->larger)
searchargs->larger = sub1->smaller - 1;
if (sub1->larger && sub1->larger < searchargs->smaller)
searchargs->smaller = sub1->larger + 1;
if (sub1->before && sub1->before > searchargs->after)
searchargs->after = sub1->before - 1;
if (sub1->after && sub1->after < searchargs->before)
searchargs->before = sub1->after + 1;
if (sub1->sentbefore && sub1->sentbefore > searchargs->sentafter)
searchargs->sentafter = sub1->sentbefore - 1;
if (sub1->sentafter && sub1->sentafter < searchargs->sentbefore)
searchargs->sentbefore = sub1->sentafter + 1;
searchargs->system_flags_set |= sub1->system_flags_unset;
searchargs->system_flags_unset |= sub1->system_flags_set;
searchargs->peruser_flags_set |= sub1->peruser_flags_unset;
searchargs->peruser_flags_unset |= sub1->peruser_flags_set;
searchargs->recent_set |= sub1->recent_unset;
searchargs->recent_unset |= sub1->recent_set;
for (i = 0; i < MAX_USER_FLAGS/32; i++) {
searchargs->user_flags_set[i] |= sub1->user_flags_unset[i];
searchargs->user_flags_unset[i] |= sub1->user_flags_set[i];
sub1->user_flags_set[i] = sub1->user_flags_unset[i] = 0;
}
/* See if we have to append this to the sublist */
if (sub1->sequence || sub1->uidsequence || sub1->from ||
sub1->to || sub1->cc || sub1->bcc || sub1->subject ||
sub1->body || sub1->text || sub1->header || sub1->sublist) {
/* Clear out the trival stuff we moved up */
sub1->smaller = sub1->larger = 0;
sub1->before = sub1->after = 0;
sub1->sentbefore = sub1->sentafter = 0;
sub1->system_flags_set = sub1->system_flags_unset = 0;
sub1->peruser_flags_set = sub1->peruser_flags_unset = 0;
sub1->recent_set = sub1->recent_unset = 0;
appendsearchargs(searchargs, sub1, (struct searchargs *)0);
}
else freesearchargs(sub1);
#else
appendsearchargs
(
searchargs
,
sub1
,
(
struct
searchargs
*
)
0
);
#endif
}
else
if
(
!
strcmp
(
criteria
.
s
,
"new"
))
{
searchargs
->
peruser_flags_unset
=
1
;
searchargs
->
recent_set
=
1
;
}
else
goto
badcri
;
break
;
case
'o'
:
if
(
!
strcmp
(
criteria
.
s
,
"or"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
sub1
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
sub1
=
zerosearchargs
;
c
=
getsearchcriteria
(
tag
,
sub1
,
charset
,
0
);
if
(
c
==
EOF
)
{
freesearchargs
(
sub1
);
return
EOF
;
}
if
(
c
!=
' '
)
goto
missingarg
;
sub2
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
sub2
=
zerosearchargs
;
c
=
getsearchcriteria
(
tag
,
sub2
,
charset
,
0
);
if
(
c
==
EOF
)
{
freesearchargs
(
sub1
);
freesearchargs
(
sub2
);
return
EOF
;
}
appendsearchargs
(
searchargs
,
sub1
,
sub2
);
}
else
if
(
!
strcmp
(
criteria
.
s
,
"old"
))
{
searchargs
->
recent_unset
=
1
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"on"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
before
||
searchargs
->
before
>
end
)
{
searchargs
->
before
=
end
;
}
if
(
!
searchargs
->
after
||
searchargs
->
after
<
start
)
{
searchargs
->
after
=
start
;
}
}
else
goto
badcri
;
break
;
case
'r'
:
if
(
!
strcmp
(
criteria
.
s
,
"recent"
))
{
searchargs
->
recent_set
=
1
;
}
else
goto
badcri
;
break
;
case
's'
:
if
(
!
strcmp
(
criteria
.
s
,
"seen"
))
{
searchargs
->
peruser_flags_set
=
1
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"sentbefore"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
sentbefore
||
searchargs
->
sentbefore
>
start
)
{
searchargs
->
sentbefore
=
start
;
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"senton"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
sentbefore
||
searchargs
->
sentbefore
>
end
)
{
searchargs
->
sentbefore
=
end
;
}
if
(
!
searchargs
->
sentafter
||
searchargs
->
sentafter
<
start
)
{
searchargs
->
sentafter
=
start
;
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"sentsince"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
sentafter
||
searchargs
->
sentafter
<
start
)
{
searchargs
->
sentafter
=
start
;
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"since"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getsearchdate
(
&
start
,
&
end
);
if
(
c
==
EOF
)
goto
baddate
;
if
(
!
searchargs
->
after
||
searchargs
->
after
<
start
)
{
searchargs
->
after
=
start
;
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"smaller"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
size
=
0
;
for
(
p
=
arg
.
s
;
*
p
&&
isdigit
(
*
p
);
p
++
)
{
size
=
size
*
10
+
*
p
-
'0'
;
}
if
(
!
arg
.
s
||
*
p
)
goto
badnumber
;
if
(
size
==
0
)
size
=
1
;
if
(
!
searchargs
->
smaller
||
size
<
searchargs
->
smaller
)
searchargs
->
smaller
=
size
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"subject"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
subject
,
str
);
}
}
else
goto
badcri
;
break
;
case
't'
:
if
(
!
strcmp
(
criteria
.
s
,
"to"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
to
,
str
);
}
}
else
if
(
!
strcmp
(
criteria
.
s
,
"text"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
);
if
(
strchr
(
str
,
EMPTY
))
{
searchargs
->
recent_set
=
searchargs
->
recent_unset
=
1
;
}
else
{
appendstrlistpat
(
&
searchargs
->
text
,
str
);
}
}
else
goto
badcri
;
break
;
case
'u'
:
if
(
!
strcmp
(
criteria
.
s
,
"uid"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
if
(
!
is_sequence
(
arg
.
s
))
goto
badcri
;
appendstrlist
(
&
searchargs
->
uidsequence
,
arg
.
s
);
}
else
if
(
!
strcmp
(
criteria
.
s
,
"unseen"
))
{
searchargs
->
peruser_flags_unset
=
1
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"unanswered"
))
{
searchargs
->
system_flags_unset
|=
FLAG_ANSWERED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"undeleted"
))
{
searchargs
->
system_flags_unset
|=
FLAG_DELETED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"undraft"
))
{
searchargs
->
system_flags_unset
|=
FLAG_DRAFT
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"unflagged"
))
{
searchargs
->
system_flags_unset
|=
FLAG_FLAGGED
;
}
else
if
(
!
strcmp
(
criteria
.
s
,
"unkeyword"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
if
(
!
is_atom
(
arg
.
s
))
goto
badflag
;
lcase
(
arg
.
s
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
imapd_mailbox
->
flagname
[
flag
]
&&
!
strcmp
(
imapd_mailbox
->
flagname
[
flag
],
arg
.
s
))
break
;
}
if
(
flag
!=
MAX_USER_FLAGS
)
{
searchargs
->
user_flags_unset
[
flag
/
32
]
|=
1
<<
(
flag
&
31
);
}
}
else
goto
badcri
;
break
;
default
:
badcri
:
prot_printf
(
imapd_out
,
"%s BAD Invalid Search criteria
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
return
c
;
missingarg
:
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to Search %s
\r\n
"
,
tag
,
criteria
.
s
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
badflag
:
prot_printf
(
imapd_out
,
"%s BAD Invalid flag name %s in Search command
\r\n
"
,
tag
,
arg
.
s
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
baddate
:
prot_printf
(
imapd_out
,
"%s BAD Invalid date in Search command
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
badnumber
:
prot_printf
(
imapd_out
,
"%s BAD Invalid number in Search command
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
/*
* Parse a "date", for SEARCH criteria
* The time_t's pointed to by 'start' and 'end' are set to the
* times of the start and end of the parsed date.
*/
int
getsearchdate
(
start
,
end
)
time_t
*
start
,
*
end
;
{
int
c
;
struct
tm
tm
;
static
struct
tm
zerotm
;
int
quoted
=
0
;
char
month
[
4
];
tm
=
zerotm
;
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\"'
)
{
quoted
++
;
c
=
prot_getc
(
imapd_in
);
}
/* Day of month */
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_mday
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
isdigit
(
c
))
{
tm
.
tm_mday
=
tm
.
tm_mday
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
}
if
(
c
!=
'-'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
/* Month name */
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
0
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
1
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
2
]
=
c
;
c
=
prot_getc
(
imapd_in
);
month
[
3
]
=
'\0'
;
lcase
(
month
);
for
(
tm
.
tm_mon
=
0
;
tm
.
tm_mon
<
12
;
tm
.
tm_mon
++
)
{
if
(
!
strcmp
(
month
,
monthname
[
tm
.
tm_mon
]))
break
;
}
if
(
tm
.
tm_mon
==
12
)
goto
baddate
;
if
(
c
!=
'-'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
/* Year */
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
isdigit
(
c
))
{
if
(
tm
.
tm_year
<
19
)
goto
baddate
;
tm
.
tm_year
-=
19
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
}
if
(
quoted
)
{
if
(
c
!=
'\"'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
}
tm
.
tm_isdst
=
-1
;
*
start
=
mktime
(
&
tm
);
tm
.
tm_sec
=
tm
.
tm_min
=
59
;
tm
.
tm_hour
=
23
;
*
end
=
mktime
(
&
tm
);
return
c
;
baddate
:
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
/*
* Parse a date_time, for the APPEND command
*/
int
getdatetime
(
date
)
time_t
*
date
;
{
int
c
;
struct
tm
tm
,
*
ltm
;
int
old_format
=
0
;
static
struct
tm
zerotm
;
char
month
[
4
],
zone
[
4
],
*
p
;
int
zone_off
;
tm
=
zerotm
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\"'
)
goto
baddate
;
/* Day of month */
c
=
prot_getc
(
imapd_in
);
if
(
c
==
' '
)
c
=
'0'
;
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_mday
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
isdigit
(
c
))
{
tm
.
tm_mday
=
tm
.
tm_mday
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
}
if
(
c
!=
'-'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
/* Month name */
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
0
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
1
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isalpha
(
c
))
goto
baddate
;
month
[
2
]
=
c
;
c
=
prot_getc
(
imapd_in
);
month
[
3
]
=
'\0'
;
lcase
(
month
);
for
(
tm
.
tm_mon
=
0
;
tm
.
tm_mon
<
12
;
tm
.
tm_mon
++
)
{
if
(
!
strcmp
(
month
,
monthname
[
tm
.
tm_mon
]))
break
;
}
if
(
tm
.
tm_mon
==
12
)
goto
baddate
;
if
(
c
!=
'-'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
/* Year */
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
isdigit
(
c
))
{
if
(
tm
.
tm_year
<
19
)
goto
baddate
;
tm
.
tm_year
-=
19
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_year
=
tm
.
tm_year
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
}
else
old_format
++
;
/* Hour */
if
(
c
!=
' '
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_hour
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_hour
=
tm
.
tm_hour
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
tm
.
tm_hour
>
23
)
goto
baddate
;
/* Minute */
if
(
c
!=
':'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_min
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_min
=
tm
.
tm_min
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
tm
.
tm_min
>
59
)
goto
baddate
;
/* Second */
if
(
c
!=
':'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_sec
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
tm
.
tm_sec
=
tm
.
tm_sec
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
tm
.
tm_min
>
60
)
goto
baddate
;
/* Time zone */
if
(
old_format
)
{
if
(
c
!=
'-'
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isalpha
(
c
))
goto
baddate
;
zone
[
0
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\"'
)
{
/* Military (single-char) zones */
zone
[
1
]
=
'\0'
;
lcase
(
zone
);
if
(
zone
[
0
]
<=
'm'
)
{
zone_off
=
(
zone
[
0
]
-
'a'
+
1
)
*
60
;
}
else
if
(
zone
[
0
]
<
'z'
)
{
zone_off
=
(
'm'
-
zone
[
0
])
*
60
;
}
else
zone_off
=
0
;
}
else
{
/* UT (universal time) */
zone
[
1
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
c
==
'\"'
)
{
zone
[
2
]
=
'\0'
;
lcase
(
zone
);
if
(
!
strcmp
(
zone
,
"ut"
))
goto
baddate
;
zone_off
=
0
;
}
else
{
/* 3-char time zone */
zone
[
2
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\"'
)
goto
baddate
;
zone
[
3
]
=
'\0'
;
lcase
(
zone
);
p
=
strchr
(
"aecmpyhb"
,
zone
[
0
]);
if
(
c
!=
'\"'
||
zone
[
2
]
!=
't'
||
!
p
)
goto
baddate
;
zone_off
=
(
strlen
(
p
)
-
12
)
*
60
;
if
(
zone
[
1
]
==
'd'
)
zone_off
-=
60
;
else
if
(
zone
[
1
]
!=
's'
)
goto
baddate
;
}
}
}
else
{
if
(
c
!=
' '
)
goto
baddate
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'+'
&&
c
!=
'-'
)
goto
baddate
;
zone
[
0
]
=
c
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
zone_off
=
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
zone_off
=
zone_off
*
10
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
zone_off
=
zone_off
*
6
+
c
-
'0'
;
c
=
prot_getc
(
imapd_in
);
if
(
!
isdigit
(
c
))
goto
baddate
;
zone_off
=
zone_off
*
10
+
c
-
'0'
;
if
(
zone
[
0
]
==
'-'
)
zone_off
=
-
zone_off
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\"'
)
goto
baddate
;
}
c
=
prot_getc
(
imapd_in
);
tm
.
tm_isdst
=
-1
;
*
date
=
mktime
(
&
tm
);
ltm
=
localtime
(
date
);
*
date
+=
gmtoff_of
(
ltm
,
*
date
)
-
zone_off
*
60
;
return
c
;
baddate
:
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
/*
* Eat characters up to and including the next newline
*/
eatline
()
{
int
c
;
while
((
c
=
prot_getc
(
imapd_in
))
!=
EOF
&&
c
!=
'\n'
);
}
/*
* Print 's' as an atom, quoted-string, or literal
*/
printastring
(
s
)
char
*
s
;
{
char
*
p
;
if
(
is_atom
(
s
))
{
prot_printf
(
imapd_out
,
"%s"
,
s
);
return
;
}
/* Look for any non-QCHAR characters */
for
(
p
=
s
;
*
p
;
p
++
)
{
if
(
*
p
&
0x80
||
*
p
==
'\r'
||
*
p
==
'\n'
||
*
p
==
'\"'
||
*
p
==
'%'
||
*
p
==
'\\'
)
break
;
}
if
(
*
p
)
{
prot_printf
(
imapd_out
,
"{%u}
\r\n
%s"
,
strlen
(
s
),
s
);
}
else
{
prot_printf
(
imapd_out
,
"
\"
%s
\"
"
,
s
);
}
}
/*
* Append 's' to the strlist 'l'.
*/
appendstrlist
(
l
,
s
)
struct
strlist
**
l
;
char
*
s
;
{
struct
strlist
**
tail
=
l
;
while
(
*
tail
)
tail
=
&
(
*
tail
)
->
next
;
*
tail
=
(
struct
strlist
*
)
xmalloc
(
sizeof
(
struct
strlist
));
(
*
tail
)
->
s
=
strsave
(
s
);
(
*
tail
)
->
p
=
0
;
(
*
tail
)
->
next
=
0
;
}
/*
* Append 's' to the strlist 'l', compiling it as a pattern.
*/
appendstrlistpat
(
l
,
s
)
struct
strlist
**
l
;
char
*
s
;
{
struct
strlist
**
tail
=
l
;
while
(
*
tail
)
tail
=
&
(
*
tail
)
->
next
;
*
tail
=
(
struct
strlist
*
)
xmalloc
(
sizeof
(
struct
strlist
));
(
*
tail
)
->
s
=
strsave
(
s
);
(
*
tail
)
->
p
=
charset_compilepat
(
s
);
(
*
tail
)
->
next
=
0
;
}
/*
* Free the strlist 'l'
*/
freestrlist
(
l
)
struct
strlist
*
l
;
{
struct
strlist
*
n
;
while
(
l
)
{
n
=
l
->
next
;
free
(
l
->
s
);
if
(
l
->
p
)
charset_freepat
(
l
->
p
);
free
((
char
*
)
l
);
l
=
n
;
}
}
/*
* Append the searchargs 's1' and 's2' to the sublist of 's'
*/
appendsearchargs
(
s
,
s1
,
s2
)
struct
searchargs
*
s
,
*
s1
,
*
s2
;
{
struct
searchsub
**
tail
=
&
s
->
sublist
;
while
(
*
tail
)
tail
=
&
(
*
tail
)
->
next
;
*
tail
=
(
struct
searchsub
*
)
xmalloc
(
sizeof
(
struct
searchsub
));
(
*
tail
)
->
sub1
=
s1
;
(
*
tail
)
->
sub2
=
s2
;
(
*
tail
)
->
next
=
0
;
}
/*
* Free the searchargs 's'
*/
freesearchargs
(
s
)
struct
searchargs
*
s
;
{
struct
searchsub
*
sub
,
*
n
;
if
(
!
s
)
return
;
freestrlist
(
s
->
sequence
);
freestrlist
(
s
->
uidsequence
);
freestrlist
(
s
->
from
);
freestrlist
(
s
->
to
);
freestrlist
(
s
->
cc
);
freestrlist
(
s
->
bcc
);
freestrlist
(
s
->
subject
);
freestrlist
(
s
->
body
);
freestrlist
(
s
->
text
);
freestrlist
(
s
->
header_name
);
freestrlist
(
s
->
header
);
for
(
sub
=
s
->
sublist
;
sub
;
sub
=
n
)
{
n
=
sub
->
next
;
freesearchargs
(
sub
->
sub1
);
freesearchargs
(
sub
->
sub2
);
free
(
sub
);
}
free
(
s
);
}
/*
* Print an authentication ready response
*/
static
char
basis_64
[]
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
printauthready
(
len
,
data
)
int
len
;
unsigned
char
*
data
;
{
int
c1
,
c2
,
c3
;
prot_putc
(
'+'
,
imapd_out
);
prot_putc
(
' '
,
imapd_out
);
while
(
len
)
{
c1
=
*
data
++
;
len
--
;
prot_putc
(
basis_64
[
c1
>>
2
],
imapd_out
);
if
(
len
==
0
)
c2
=
0
;
else
c2
=
*
data
++
;
prot_putc
(
basis_64
[((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
)],
imapd_out
);
if
(
len
==
0
)
{
prot_putc
(
'='
,
imapd_out
);
prot_putc
(
'='
,
imapd_out
);
break
;
}
if
(
--
len
==
0
)
c3
=
0
;
else
c3
=
*
data
++
;
prot_putc
(
basis_64
[((
c2
&
0xF
)
<<
2
)
|
((
c3
&
0xC0
)
>>
6
)],
imapd_out
);
if
(
len
==
0
)
{
prot_putc
(
'='
,
imapd_out
);
break
;
}
--
len
;
prot_putc
(
basis_64
[
c3
&
0x3F
],
imapd_out
);
}
prot_putc
(
'\r'
,
imapd_out
);
prot_putc
(
'\n'
,
imapd_out
);
prot_flush
(
imapd_out
);
}
/*
* Issue a MAILBOX untagged response
*/
static
int
mailboxdata
(
name
,
matchlen
,
maycreate
)
char
*
name
;
int
matchlen
;
int
maycreate
;
{
prot_printf
(
imapd_out
,
"* MAILBOX %s
\r\n
"
,
name
);
return
0
;
}
/*
* Issue a LIST or LSUB untagged response
*/
static
int
mstringdata
(
cmd
,
name
,
matchlen
,
maycreate
)
char
*
cmd
;
char
*
name
;
int
matchlen
;
int
maycreate
;
{
static
char
lastname
[
MAX_MAILBOX_PATH
];
static
int
lastnamedelayed
;
static
sawuser
=
0
;
int
lastnamehassub
=
0
;
int
c
;
if
(
lastnamedelayed
)
{
if
(
name
&&
strncasecmp
(
lastname
,
name
,
strlen
(
lastname
))
==
0
&&
name
[
strlen
(
lastname
)]
==
'.'
)
{
lastnamehassub
=
1
;
}
prot_printf
(
imapd_out
,
"* %s (%s)
\"
.
\"
"
,
cmd
,
lastnamehassub
?
""
:
"
\\
Noinferiors"
);
printastring
(
lastname
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
lastnamedelayed
=
0
;
}
/* Special-case to flush any final state */
if
(
!
name
)
{
lastname
[
0
]
=
'\0'
;
return
0
;
}
/* Suppress any output of a partial match */
if
(
name
[
matchlen
]
&&
strncasecmp
(
lastname
,
name
,
matchlen
)
==
0
)
{
return
0
;
}
/*
* We can get a partial match for "user" multiple times with
* other matches inbetween. Handle it as a special case
*/
if
(
matchlen
==
4
&&
strncasecmp
(
name
,
"user"
,
4
)
==
0
)
{
if
(
sawuser
)
return
0
;
sawuser
=
1
;
}
strcpy
(
lastname
,
name
);
lastname
[
matchlen
]
=
'\0'
;
if
(
!
name
[
matchlen
]
&&
!
maycreate
)
{
lastnamedelayed
=
1
;
return
0
;
}
c
=
name
[
matchlen
];
name
[
matchlen
]
=
'\0'
;
prot_printf
(
imapd_out
,
"* %s (%s)
\"
.
\"
"
,
cmd
,
c
?
"
\\
Noselect"
:
""
);
printastring
(
name
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
name
[
matchlen
]
=
c
;
return
0
;
}
/*
* Issue a LIST untagged response
*/
static
int
listdata
(
name
,
matchlen
,
maycreate
)
char
*
name
;
int
matchlen
;
int
maycreate
;
{
return
mstringdata
(
"LIST"
,
name
,
matchlen
,
maycreate
);
}
/*
* Issue a LSUB untagged response
*/
static
int
lsubdata
(
name
,
matchlen
,
maycreate
)
char
*
name
;
int
matchlen
;
int
maycreate
;
{
return
mstringdata
(
"LSUB"
,
name
,
matchlen
,
maycreate
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Fri, Apr 24, 10:49 AM (6 d, 11 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18725664
Default Alt Text
imapd.c (87 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline