Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117886164
imapd.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
131 KB
Referenced Files
None
Subscribers
None
imapd.c
View Options
/*
* Copyright (c) 1998-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY 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.
*/
/* $Id: imapd.c,v 1.264 2000/07/17 04:32:42 leg Exp $ */
#include
<config.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<stdio.h>
#include
<string.h>
#include
<ctype.h>
#include
<time.h>
#include
<signal.h>
#include
<fcntl.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
<sys/utsname.h>
#include
<sasl.h>
#include
"acl.h"
#include
"util.h"
#include
"auth.h"
#include
"imapconf.h"
#include
"version.h"
#include
"charset.h"
#include
"imparse.h"
#include
"mkgmtime.h"
#include
"exitcodes.h"
#include
"imap_err.h"
#include
"mailbox.h"
#include
"imapd.h"
#include
"xmalloc.h"
#include
"mboxname.h"
#include
"append.h"
#include
"mboxlist.h"
#include
"acapmbox.h"
#include
"pushstats.h"
/* SNMP interface */
#ifdef HAVE_SSL
#include
"tls.h"
extern
SSL
*
tls_conn
;
#endif
/* HAVE_SSL */
extern
int
optind
;
extern
char
*
optarg
;
extern
int
errno
;
sasl_conn_t
*
imapd_saslconn
;
/* the sasl connection context */
int
imapd_starttls_done
=
0
;
/* have we done a successful starttls yet? */
char
*
imapd_userid
;
struct
auth_state
*
imapd_authstate
=
0
;
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
;
time_t
imapd_logtime
;
static
struct
mailbox
mboxstruct
;
static
struct
fetchargs
zerofetchargs
;
static
char
*
monthname
[]
=
{
"jan"
,
"feb"
,
"mar"
,
"apr"
,
"may"
,
"jun"
,
"jul"
,
"aug"
,
"sep"
,
"oct"
,
"nov"
,
"dec"
};
void
shutdown_file
(
int
fd
);
void
motd_file
(
int
fd
);
void
shut_down
(
int
code
);
void
fatal
(
const
char
*
s
,
int
code
);
void
cmdloop
(
void
);
void
cmd_login
(
char
*
tag
,
char
*
user
,
char
*
passwd
);
void
cmd_authenticate
(
char
*
tag
,
char
*
authtype
);
void
cmd_noop
(
char
*
tag
,
char
*
cmd
);
void
cmd_capability
(
char
*
tag
);
void
cmd_append
(
char
*
tag
,
char
*
name
);
void
cmd_select
(
char
*
tag
,
char
*
cmd
,
char
*
name
);
void
cmd_close
(
char
*
tag
);
void
cmd_fetch
(
char
*
tag
,
char
*
sequence
,
int
usinguid
);
void
cmd_partial
(
char
*
tag
,
char
*
msgno
,
char
*
data
,
char
*
start
,
char
*
count
);
void
cmd_store
(
char
*
tag
,
char
*
sequence
,
char
*
operation
,
int
usinguid
);
void
cmd_search
(
char
*
tag
,
int
usinguid
);
void
cmd_sort
(
char
*
tag
,
int
usinguid
);
void
cmd_thread
(
char
*
tag
,
int
usinguid
);
void
cmd_copy
(
char
*
tag
,
char
*
sequence
,
char
*
name
,
int
usinguid
);
void
cmd_expunge
(
char
*
tag
,
char
*
sequence
);
void
cmd_create
(
char
*
tag
,
char
*
name
,
char
*
partition
);
void
cmd_delete
(
char
*
tag
,
char
*
name
);
void
cmd_rename
(
const
char
*
tag
,
char
*
oldname
,
char
*
newname
,
char
*
partition
);
void
cmd_find
(
char
*
tag
,
char
*
namespace
,
char
*
pattern
);
void
cmd_list
(
char
*
tag
,
int
subscribed
,
char
*
reference
,
char
*
pattern
);
void
cmd_changesub
(
char
*
tag
,
char
*
namespace
,
char
*
name
,
int
add
);
void
cmd_getacl
(
char
*
tag
,
char
*
name
,
int
oldform
);
void
cmd_listrights
(
char
*
tag
,
char
*
name
,
char
*
identifier
);
void
cmd_myrights
(
char
*
tag
,
char
*
name
,
int
oldform
);
void
cmd_setacl
(
char
*
tag
,
char
*
name
,
char
*
identifier
,
char
*
rights
);
void
cmd_getquota
(
char
*
tag
,
char
*
name
);
void
cmd_getquotaroot
(
char
*
tag
,
char
*
name
);
void
cmd_setquota
(
char
*
tag
,
char
*
quotaroot
);
void
cmd_status
(
char
*
tag
,
char
*
name
);
void
cmd_getuids
(
char
*
tag
,
char
*
startuid
);
void
cmd_unselect
(
char
*
tag
);
void
cmd_namespace
(
char
*
tag
);
void
cmd_id
(
char
*
tag
);
void
id_getcmdline
(
int
argc
,
char
**
argv
);
void
cmd_starttls
(
char
*
tag
,
int
imaps
);
int
starttls_enabled
(
void
);
#ifdef ENABLE_X_NETSCAPE_HACK
void
cmd_netscrape
(
char
*
tag
);
#endif
enum
string_types
{
IMAP_ASTRING
,
IMAP_NSTRING
,
IMAP_STRING
};
#define getastring(buf) getxstring((buf), IMAP_ASTRING)
#define getnstring(buf) getxstring((buf), IMAP_NSTRING)
#define getstring(buf) getxstring((buf), IMAP_STRING)
int
getword
(
struct
buf
*
buf
);
int
getxstring
(
struct
buf
*
buf
,
int
type
);
int
getsearchprogram
(
char
*
tag
,
struct
searchargs
*
searchargs
,
int
*
charset
,
int
parsecharset
);
int
getsearchcriteria
(
char
*
tag
,
struct
searchargs
*
searchargs
,
int
*
charset
,
int
parsecharset
);
int
getsearchdate
(
time_t
*
start
,
time_t
*
end
);
int
getsortcriteria
(
char
*
tag
,
struct
sortcrit
**
sortcrit
);
int
getdatetime
(
time_t
*
date
);
void
eatline
(
int
c
);
void
printstring
(
const
char
*
s
);
void
printastring
(
const
char
*
s
);
void
appendfieldlist
(
struct
fieldlist
**
l
,
char
*
section
,
struct
strlist
*
fields
,
char
*
trail
);
void
appendstrlist
(
struct
strlist
**
l
,
char
*
s
);
void
appendstrlistpat
(
struct
strlist
**
l
,
char
*
s
);
void
freefieldlist
(
struct
fieldlist
*
l
);
void
freestrlist
(
struct
strlist
*
l
);
void
appendsearchargs
(
struct
searchargs
*
s
,
struct
searchargs
*
s1
,
struct
searchargs
*
s2
);
void
freesearchargs
(
struct
searchargs
*
s
);
static
void
freesortcrit
(
struct
sortcrit
*
s
);
static
int
mailboxdata
(),
listdata
(),
lsubdata
();
static
void
mstringdata
(
char
*
cmd
,
char
*
name
,
int
matchlen
,
int
maycreate
);
extern
void
setproctitle_init
(
int
argc
,
char
**
argv
,
char
**
envp
);
extern
int
proc_register
(
char
*
progname
,
char
*
clienthost
,
char
*
userid
,
char
*
mailbox
);
extern
void
proc_cleanup
(
void
);
static
int
hash_simple
(
const
char
*
str
);
/*
* acl_ok() checks to see if the the inbox for 'user' grants the 'a'
* right to the principal 'auth_identity'. Returns 1 if so, 0 if not.
*/
static
int
acl_ok
(
user
,
auth_identity
)
const
char
*
user
;
const
char
*
auth_identity
;
{
char
*
acl
;
char
inboxname
[
1024
];
int
r
;
struct
auth_state
*
authstate
;
if
(
strchr
(
user
,
'.'
)
||
strlen
(
user
)
+
6
>=
sizeof
(
inboxname
))
return
0
;
strcpy
(
inboxname
,
"user."
);
strcat
(
inboxname
,
user
);
if
(
!
(
authstate
=
auth_newstate
(
auth_identity
,
(
char
*
)
0
))
||
mboxlist_lookup
(
inboxname
,
(
char
**
)
0
,
&
acl
,
NULL
))
{
r
=
0
;
/* Failed so assume no proxy access */
}
else
{
r
=
(
acl_myrights
(
authstate
,
acl
)
&
ACL_ADMIN
)
!=
0
;
}
if
(
authstate
)
auth_freestate
(
authstate
);
return
r
;
}
/* should we allow users to proxy? return SASL_OK if yes,
SASL_BADAUTH otherwise */
static
int
mysasl_authproc
(
void
*
context
__attribute__
((
unused
)),
const
char
*
auth_identity
,
const
char
*
requested_user
,
const
char
**
user
,
const
char
**
errstr
)
{
const
char
*
val
;
char
*
canon_authuser
,
*
canon_requser
;
char
*
realm
;
static
char
replybuf
[
100
];
canon_authuser
=
auth_canonifyid
(
auth_identity
);
if
(
!
canon_authuser
)
{
if
(
errstr
)
*
errstr
=
"bad userid authenticated"
;
return
SASL_BADAUTH
;
}
canon_authuser
=
xstrdup
(
canon_authuser
);
canon_requser
=
auth_canonifyid
(
requested_user
);
if
(
!
canon_requser
)
{
if
(
errstr
)
*
errstr
=
"bad userid requested"
;
return
SASL_BADAUTH
;
}
canon_requser
=
xstrdup
(
canon_requser
);
/* check if remote realm */
if
((
realm
=
strchr
(
canon_authuser
,
'@'
))
!=
NULL
)
{
realm
++
;
val
=
config_getstring
(
"loginrealms"
,
""
);
while
(
*
val
)
{
if
(
!
strncasecmp
(
val
,
realm
,
strlen
(
realm
))
&&
(
!
val
[
strlen
(
realm
)]
||
isspace
((
int
)
val
[
strlen
(
realm
)])))
{
break
;
}
/* not this realm, try next one */
while
(
*
val
&&
!
isspace
((
int
)
*
val
))
val
++
;
while
(
*
val
&&
isspace
((
int
)
*
val
))
val
++
;
}
if
(
!*
val
)
{
snprintf
(
replybuf
,
100
,
"cross-realm login %s denied"
,
canon_authuser
);
if
(
errstr
)
*
errstr
=
replybuf
;
return
SASL_BADAUTH
;
}
}
imapd_authstate
=
auth_newstate
(
canon_authuser
,
NULL
);
/* ok, is auth_identity an admin? */
imapd_userisadmin
=
authisa
(
imapd_authstate
,
"imap"
,
"admins"
);
if
(
strcmp
(
canon_authuser
,
canon_requser
))
{
/* we want to authenticate as a different user; we'll allow this
if we're an admin or if we've allowed ACL proxy logins */
int
use_acl
=
config_getswitch
(
"loginuseacl"
,
0
);
if
(
imapd_userisadmin
||
(
use_acl
&&
acl_ok
(
canon_requser
,
canon_authuser
))
||
authisa
(
imapd_authstate
,
"imap"
,
"proxyservers"
))
{
/* proxy ok! */
imapd_userisadmin
=
0
;
/* no longer admin */
auth_freestate
(
imapd_authstate
);
imapd_authstate
=
auth_newstate
(
canon_requser
,
NULL
);
}
else
{
if
(
errstr
)
*
errstr
=
"user is not allowed to proxy"
;
free
(
canon_authuser
);
free
(
canon_requser
);
auth_freestate
(
imapd_authstate
);
return
SASL_BADAUTH
;
}
}
free
(
canon_authuser
);
*
user
=
canon_requser
;
if
(
errstr
)
*
errstr
=
NULL
;
return
SASL_OK
;
}
static
struct
sasl_callback
mysasl_cb
[]
=
{
{
SASL_CB_GETOPT
,
&
mysasl_config
,
NULL
},
{
SASL_CB_PROXY_POLICY
,
&
mysasl_authproc
,
NULL
},
{
SASL_CB_LIST_END
,
NULL
,
NULL
}
};
/*
* run once when process is forked;
* MUST NOT exit directly; must return with non-zero error code
*/
int
service_init
(
int
argc
,
char
**
argv
,
char
**
envp
)
{
int
r
;
config_changeident
(
"imapd"
);
if
(
geteuid
()
==
0
)
fatal
(
"must run as the Cyrus user"
,
EC_USAGE
);
setproctitle_init
(
argc
,
argv
,
envp
);
/* set signal handlers */
signals_set_shutdown
(
&
shut_down
);
signals_add_handlers
();
signal
(
SIGPIPE
,
SIG_IGN
);
/* set the SASL allocation functions */
sasl_set_alloc
((
sasl_malloc_t
*
)
&
xmalloc
,
(
sasl_calloc_t
*
)
&
calloc
,
(
sasl_realloc_t
*
)
&
xrealloc
,
(
sasl_free_t
*
)
&
free
);
/* load the SASL plugins */
if
((
r
=
sasl_server_init
(
mysasl_cb
,
"Cyrus"
))
!=
SASL_OK
)
{
syslog
(
LOG_ERR
,
"SASL failed initializing: sasl_server_init(): %s"
,
sasl_errstring
(
r
,
NULL
,
NULL
));
return
EC_SOFTWARE
;
}
if
((
r
=
sasl_client_init
(
NULL
))
!=
SASL_OK
)
{
syslog
(
LOG_ERR
,
"SASL failed initializing: sasl_client_init(): %s"
,
sasl_errstring
(
r
,
NULL
,
NULL
));
return
EC_SOFTWARE
;
}
/* open the mboxlist, we'll need it for real work */
mboxlist_init
(
0
);
mboxlist_open
(
NULL
);
mailbox_initialize
();
/* create connection to the SNMP listener, if available. */
snmp_connect
();
/* ignore return code */
snmp_set_str
(
SERVER_NAME_VERSION
,
CYRUS_VERSION
);
return
0
;
}
/*
* run for each accepted connection
*/
int
service_main
(
int
argc
,
char
**
argv
,
char
**
envp
)
{
int
imaps
=
0
;
int
opt
;
int
salen
;
struct
hostent
*
hp
;
int
timeout
;
sasl_security_properties_t
*
secprops
=
NULL
;
sasl_external_properties_t
extprops
;
signals_poll
();
/* get command line args for use in ID before getopt mangles them */
id_getcmdline
(
argc
,
argv
);
memset
(
&
extprops
,
0
,
sizeof
(
sasl_external_properties_t
));
while
((
opt
=
getopt
(
argc
,
argv
,
"sp:"
))
!=
EOF
)
{
switch
(
opt
)
{
case
's'
:
/* imaps (do starttls right away) */
imaps
=
1
;
if
(
!
starttls_enabled
())
{
syslog
(
LOG_ERR
,
"imaps: required OpenSSL options not present"
);
fatal
(
"imaps: required OpenSSL options not present"
,
EC_CONFIG
);
}
break
;
case
'p'
:
/* external protection */
extprops
.
ssf
=
atoi
(
optarg
);
break
;
default
:
break
;
}
}
imapd_in
=
prot_new
(
0
,
0
);
imapd_out
=
prot_new
(
1
,
1
);
/* Find out name of client host */
salen
=
sizeof
(
imapd_remoteaddr
);
if
(
getpeername
(
0
,
(
struct
sockaddr
*
)
&
imapd_remoteaddr
,
&
salen
)
==
0
&&
imapd_remoteaddr
.
sin_family
==
AF_INET
)
{
hp
=
gethostbyaddr
((
char
*
)
&
imapd_remoteaddr
.
sin_addr
,
sizeof
(
imapd_remoteaddr
.
sin_addr
),
AF_INET
);
if
(
hp
!=
NULL
)
{
strncpy
(
imapd_clienthost
,
hp
->
h_name
,
sizeof
(
imapd_clienthost
)
-30
);
imapd_clienthost
[
sizeof
(
imapd_clienthost
)
-30
]
=
'\0'
;
}
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
,
(
struct
sockaddr
*
)
&
imapd_localaddr
,
&
salen
)
==
0
)
{
imapd_haveaddr
=
1
;
}
}
/* create the SASL connection */
if
(
sasl_server_new
(
"imap"
,
config_servername
,
NULL
,
NULL
,
SASL_SECURITY_LAYER
,
&
imapd_saslconn
)
!=
SASL_OK
)
{
fatal
(
"SASL failed initializing: sasl_server_new()"
,
EC_TEMPFAIL
);
}
secprops
=
mysasl_secprops
(
SASL_SEC_NOPLAINTEXT
);
sasl_setprop
(
imapd_saslconn
,
SASL_SEC_PROPS
,
secprops
);
if
(
extprops
.
ssf
)
{
sasl_setprop
(
imapd_saslconn
,
SASL_SSF_EXTERNAL
,
&
extprops
);
}
if
(
imapd_haveaddr
)
{
sasl_setprop
(
imapd_saslconn
,
SASL_IP_REMOTE
,
&
imapd_remoteaddr
);
sasl_setprop
(
imapd_saslconn
,
SASL_IP_LOCAL
,
&
imapd_localaddr
);
}
proc_register
(
"imapd"
,
imapd_clienthost
,
NULL
,
NULL
);
/* Set inactivity timer */
timeout
=
config_getint
(
"timeout"
,
30
);
if
(
timeout
<
30
)
timeout
=
30
;
prot_settimeout
(
imapd_in
,
timeout
*
60
);
prot_setflushonread
(
imapd_in
,
imapd_out
);
/* we were connected on imaps port so we should do
TLS negotiation immediately */
if
(
imaps
==
1
)
cmd_starttls
(
NULL
,
1
);
snmp_increment
(
TOTAL_CONNECTIONS
,
1
);
snmp_increment
(
ACTIVE_CONNECTIONS
,
1
);
cmdloop
();
/* never reaches! */
return
1
;
}
/* called if 'service_init()' was called but not 'service_main()' */
void
service_abort
(
int
error
)
{
mboxlist_close
();
mboxlist_done
();
}
/*
* found a motd file; spit out message and return
*/
void
motd_file
(
fd
)
int
fd
;
{
struct
protstream
*
motd_in
;
char
buf
[
1024
];
char
*
p
;
motd_in
=
prot_new
(
fd
,
0
);
prot_fgets
(
buf
,
sizeof
(
buf
),
motd_in
);
if
((
p
=
strchr
(
buf
,
'\r'
))
!=
NULL
)
*
p
=
0
;
if
((
p
=
strchr
(
buf
,
'\n'
))
!=
NULL
)
*
p
=
0
;
for
(
p
=
buf
;
*
p
==
'['
;
p
++
);
/* can't have [ be first char, sigh */
prot_printf
(
imapd_out
,
"* OK [ALERT] %s
\r\n
"
,
p
);
}
/*
* Found a shutdown file: Spit out an untagged BYE and shut down
*/
void
shutdown_file
(
fd
)
int
fd
;
{
struct
protstream
*
shutdown_in
;
char
buf
[
1024
];
char
*
p
;
shutdown_in
=
prot_new
(
fd
,
0
);
prot_fgets
(
buf
,
sizeof
(
buf
),
shutdown_in
);
if
((
p
=
strchr
(
buf
,
'\r'
))
!=
NULL
)
*
p
=
0
;
if
((
p
=
strchr
(
buf
,
'\n'
))
!=
NULL
)
*
p
=
0
;
for
(
p
=
buf
;
*
p
==
'['
;
p
++
);
/* can't have [ be first char, sigh */
prot_printf
(
imapd_out
,
"* BYE [ALERT] %s
\r\n
"
,
p
);
shut_down
(
0
);
}
/*
* Cleanly shut down and exit
*/
void
shut_down
(
int
code
)
__attribute__
((
noreturn
));
void
shut_down
(
int
code
)
{
extern
void
seen_done
(
void
);
proc_cleanup
();
if
(
imapd_mailbox
)
{
index_closemailbox
(
imapd_mailbox
);
mailbox_close
(
imapd_mailbox
);
}
seen_done
();
mboxlist_close
();
mboxlist_done
();
prot_flush
(
imapd_out
);
/* one less active connection */
snmp_increment
(
ACTIVE_CONNECTIONS
,
-1
);
exit
(
code
);
}
void
fatal
(
const
char
*
s
,
int
code
)
{
static
int
recurse_code
=
0
;
if
(
recurse_code
)
{
/* We were called recursively. Just give up */
proc_cleanup
();
snmp_increment
(
ACTIVE_CONNECTIONS
,
-1
);
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
*/
void
cmdloop
()
{
int
fd
;
char
shutdownfilename
[
1024
];
char
motdfilename
[
1024
];
char
hostname
[
MAXHOSTNAMELEN
+
1
];
int
c
;
int
usinguid
,
havepartition
,
havenamespace
,
oldform
;
static
struct
buf
tag
,
cmd
,
arg1
,
arg2
,
arg3
,
arg4
;
char
*
p
;
const
char
*
err
;
sprintf
(
shutdownfilename
,
"%s/msg/shutdown"
,
config_dir
);
gethostname
(
hostname
,
sizeof
(
hostname
));
prot_printf
(
imapd_out
,
"* OK %s Cyrus IMAP4 %s server ready
\r\n
"
,
hostname
,
CYRUS_VERSION
);
sprintf
(
motdfilename
,
"%s/msg/motd"
,
config_dir
);
if
((
fd
=
open
(
motdfilename
,
O_RDONLY
,
0
))
!=
-1
)
{
motd_file
(
fd
);
close
(
fd
);
}
for
(;;)
{
if
(
!
imapd_userisadmin
&&
(
fd
=
open
(
shutdownfilename
,
O_RDONLY
,
0
))
!=
-1
)
{
shutdown_file
(
fd
);
}
signals_poll
();
/* Parse tag */
c
=
getword
(
&
tag
);
if
(
c
==
EOF
)
{
if
((
err
=
prot_error
(
imapd_in
))
!=
NULL
)
{
syslog
(
LOG_WARNING
,
"%s, closing connection"
,
err
);
prot_printf
(
imapd_out
,
"* BYE %s
\r\n
"
,
err
);
}
shut_down
(
0
);
}
if
(
c
!=
' '
||
!
imparse_isatom
(
tag
.
s
)
||
(
tag
.
s
[
0
]
==
'*'
&&
!
tag
.
s
[
1
]))
{
prot_printf
(
imapd_out
,
"* BAD Invalid tag
\r\n
"
);
eatline
(
c
);
continue
;
}
/* Parse command name */
c
=
getword
(
&
cmd
);
if
(
!
cmd
.
s
[
0
])
{
prot_printf
(
imapd_out
,
"%s BAD Null command
\r\n
"
,
tag
.
s
);
eatline
(
c
);
continue
;
}
if
(
islower
((
int
)
cmd
.
s
[
0
]))
cmd
.
s
[
0
]
=
toupper
((
int
)
cmd
.
s
[
0
]);
for
(
p
=
&
cmd
.
s
[
1
];
*
p
;
p
++
)
{
if
(
isupper
((
int
)
*
p
))
*
p
=
tolower
((
int
)
*
p
);
}
/* Only Authenticate/Login/Logout/Noop/Capability/Id/Starttls
allowed when not logged in */
if
(
!
imapd_userid
&&
!
strchr
(
"ALNCIS"
,
cmd
.
s
[
0
]))
goto
nologin
;
/* note that about half the commands (the common ones that don't
hit the mailboxes file) now close the mailboxes file just in
case it was open. */
switch
(
cmd
.
s
[
0
])
{
case
'A'
:
if
(
!
strcmp
(
cmd
.
s
,
"Authenticate"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getword
(
&
arg1
);
if
(
!
imparse_isatom
(
arg1
.
s
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid authenticate mechanism
\r\n
"
,
tag
.
s
);
eatline
(
c
);
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
);
snmp_increment
(
AUTHENTICATE_COUNT
,
1
);
}
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
);
snmp_increment
(
APPEND_COUNT
,
1
);
}
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
);
snmp_increment
(
BBOARD_COUNT
,
1
);
}
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
);
snmp_increment
(
CAPABILITY_COUNT
,
1
);
}
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
);
snmp_increment
(
CHECK_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Copy"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
copy
:
c
=
getword
(
&
arg1
);
if
(
c
==
'\r'
)
goto
missingargs
;
if
(
c
!=
' '
||
!
imparse_issequence
(
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
);
snmp_increment
(
COPY_COUNT
,
1
);
}
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
(
!
imparse_isatom
(
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
);
snmp_increment
(
CREATE_COUNT
,
1
);
}
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
);
snmp_increment
(
CLOSE_COUNT
,
1
);
}
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
);
snmp_increment
(
DELETE_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Deleteacl"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
!
strcasecmp
(
arg1
.
s
,
"mailbox"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
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_setacl
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
(
char
*
)
0
);
snmp_increment
(
DELETEACL_COUNT
,
1
);
}
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
,
0
);
snmp_increment
(
EXPUNGE_COUNT
,
1
);
}
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
);
snmp_increment
(
EXAMINE_COUNT
,
1
);
}
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
==
'\r'
)
goto
missingargs
;
if
(
c
!=
' '
||
!
imparse_issequence
(
arg1
.
s
))
goto
badsequence
;
cmd_fetch
(
tag
.
s
,
arg1
.
s
,
usinguid
);
snmp_increment
(
FETCH_COUNT
,
1
);
}
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
);
snmp_increment
(
FIND_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
case
'G'
:
if
(
!
strcmp
(
cmd
.
s
,
"Getacl"
))
{
oldform
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
!
strcasecmp
(
arg1
.
s
,
"mailbox"
))
{
oldform
=
1
;
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_getacl
(
tag
.
s
,
arg1
.
s
,
oldform
);
snmp_increment
(
GETACL_COUNT
,
1
);
}
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
);
snmp_increment
(
GETQUOTA_COUNT
,
1
);
}
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
);
snmp_increment
(
GETQUOTAROOT_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
case
'I'
:
if
(
!
strcmp
(
cmd
.
s
,
"Id"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
cmd_id
(
tag
.
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
);
snmp_increment
(
LOGIN_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Logout"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
snmp_increment
(
LOGOUT_COUNT
,
1
);
prot_printf
(
imapd_out
,
"* BYE %s
\r\n
"
,
error_message
(
IMAP_BYE_LOGOUT
));
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
.
s
,
error_message
(
IMAP_OK_COMPLETED
));
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
);
snmp_increment
(
LIST_COUNT
,
1
);
}
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
);
snmp_increment
(
LSUB_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Listrights"
))
{
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg2
);
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_listrights
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
);
snmp_increment
(
LISTRIGHTS_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
case
'M'
:
if
(
!
strcmp
(
cmd
.
s
,
"Myrights"
))
{
oldform
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
!
strcasecmp
(
arg1
.
s
,
"mailbox"
))
{
oldform
=
1
;
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_myrights
(
tag
.
s
,
arg1
.
s
,
oldform
);
snmp_increment
(
MYRIGHTS_COUNT
,
1
);
}
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
);
snmp_increment
(
NOOP_COUNT
,
1
);
}
#ifdef ENABLE_X_NETSCAPE_HACK
else
if
(
!
strcmp
(
cmd
.
s
,
"Netscape"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_netscrape
(
tag
.
s
);
}
#endif
else
if
(
!
imapd_userid
)
goto
nologin
;
else
if
(
!
strcmp
(
cmd
.
s
,
"Namespace"
))
{
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_namespace
(
tag
.
s
);
snmp_increment
(
NAMESPACE_COUNT
,
1
);
}
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
);
snmp_increment
(
PARTIAL_COUNT
,
1
);
}
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
(
!
imparse_isatom
(
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
);
snmp_increment
(
RENAME_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
case
'S'
:
if
(
!
strcmp
(
cmd
.
s
,
"Starttls"
))
{
if
(
!
starttls_enabled
())
{
/* we don't support starttls */
goto
badcmd
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
/* if we've already done SASL fail */
if
(
imapd_userid
!=
NULL
)
{
prot_printf
(
imapd_out
,
"%s BAD Can't Starttls after authentication
\r\n
"
,
tag
.
s
);
continue
;
}
/* check if already did a successful tls */
if
(
imapd_starttls_done
==
1
)
{
prot_printf
(
imapd_out
,
"%s BAD Already did a successful Starttls
\r\n
"
,
tag
.
s
);
continue
;
}
cmd_starttls
(
tag
.
s
,
0
);
snmp_increment
(
STARTTLS_COUNT
,
1
);
continue
;
}
if
(
!
imapd_userid
)
{
goto
nologin
;
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Store"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
store
:
c
=
getword
(
&
arg1
);
if
(
c
!=
' '
||
!
imparse_issequence
(
arg1
.
s
))
goto
badsequence
;
c
=
getword
(
&
arg2
);
if
(
c
!=
' '
)
goto
badsequence
;
cmd_store
(
tag
.
s
,
arg1
.
s
,
arg2
.
s
,
usinguid
);
snmp_increment
(
STORE_COUNT
,
1
);
}
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
);
snmp_increment
(
SELECT_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Search"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
search
:
cmd_search
(
tag
.
s
,
usinguid
);
snmp_increment
(
SEARCH_COUNT
,
1
);
}
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
);
}
snmp_increment
(
SUBSCRIBE_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Setacl"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
!
strcasecmp
(
arg1
.
s
,
"mailbox"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
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
);
snmp_increment
(
SETACL_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Setquota"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
cmd_setquota
(
tag
.
s
,
arg1
.
s
);
snmp_increment
(
SETQUOTA_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Sort"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
sort
:
cmd_sort
(
tag
.
s
,
usinguid
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Status"
))
{
if
(
c
!=
' '
)
goto
missingargs
;
c
=
getastring
(
&
arg1
);
if
(
c
!=
' '
)
goto
missingargs
;
cmd_status
(
tag
.
s
,
arg1
.
s
);
snmp_increment
(
STATUS_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
case
'T'
:
if
(
!
strcmp
(
cmd
.
s
,
"Thread"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
usinguid
=
0
;
if
(
c
!=
' '
)
goto
missingargs
;
thread
:
cmd_thread
(
tag
.
s
,
usinguid
);
}
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
,
"sort"
))
{
goto
sort
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"thread"
))
{
goto
thread
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"copy"
))
{
goto
copy
;
}
else
if
(
!
strcmp
(
arg1
.
s
,
"expunge"
))
{
c
=
getword
(
&
arg1
);
if
(
!
imparse_issequence
(
arg1
.
s
))
goto
badsequence
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_expunge
(
tag
.
s
,
arg1
.
s
);
snmp_increment
(
EXPUNGE_COUNT
,
1
);
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Unrecognized UID subcommand
\r\n
"
,
tag
.
s
);
eatline
(
c
);
}
}
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
);
}
snmp_increment
(
UNSUBSCRIBE_COUNT
,
1
);
}
else
if
(
!
strcmp
(
cmd
.
s
,
"Unselect"
))
{
if
(
!
imapd_mailbox
)
goto
nomailbox
;
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
goto
extraargs
;
cmd_unselect
(
tag
.
s
);
snmp_increment
(
UNSELECT_COUNT
,
1
);
}
else
goto
badcmd
;
break
;
default
:
badcmd
:
prot_printf
(
imapd_out
,
"%s BAD Unrecognized command
\r\n
"
,
tag
.
s
);
eatline
(
c
);
}
continue
;
nologin
:
prot_printf
(
imapd_out
,
"%s BAD Please login first
\r\n
"
,
tag
.
s
);
eatline
(
c
);
continue
;
nomailbox
:
prot_printf
(
imapd_out
,
"%s BAD Please select a mailbox first
\r\n
"
,
tag
.
s
);
eatline
(
c
);
continue
;
missingargs
:
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
eatline
(
c
);
continue
;
extraargs
:
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
eatline
(
c
);
continue
;
badsequence
:
prot_printf
(
imapd_out
,
"%s BAD Invalid sequence in %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
eatline
(
c
);
continue
;
badpartition
:
prot_printf
(
imapd_out
,
"%s BAD Invalid partition name in %s
\r\n
"
,
tag
.
s
,
cmd
.
s
);
eatline
(
c
);
continue
;
}
}
/*
* Perform a LOGIN command
*/
void
cmd_login
(
tag
,
user
,
passwd
)
char
*
tag
;
char
*
user
;
char
*
passwd
;
{
char
*
canon_user
;
const
char
*
reply
=
0
;
const
char
*
val
;
char
buf
[
MAX_MAILBOX_PATH
];
char
*
p
;
FILE
*
logfile
;
int
plaintextloginpause
;
int
result
;
canon_user
=
auth_canonifyid
(
user
);
/* possibly disallow login */
if
((
imapd_starttls_done
==
0
)
&&
(
config_getswitch
(
"allowplaintext"
,
1
)
==
0
)
&&
strcmp
(
canon_user
,
"anonymous"
)
!=
0
)
{
prot_printf
(
imapd_out
,
"%s NO Login only available under a layer
\r\n
"
,
tag
);
return
;
}
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"
;
imapd_userid
=
xstrdup
(
"anonymous"
);
}
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
((
result
=
sasl_checkpass
(
imapd_saslconn
,
canon_user
,
strlen
(
canon_user
),
passwd
,
strlen
(
passwd
),
(
const
char
**
)
&
reply
))
!=
SASL_OK
)
{
if
(
reply
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s plaintext %s %s"
,
imapd_clienthost
,
canon_user
,
reply
);
}
/* Apply penalty only if not under layer */
if
(
imapd_starttls_done
==
0
)
sleep
(
3
);
if
(
reply
)
{
prot_printf
(
imapd_out
,
"%s NO Login failed: %s
\r\n
"
,
tag
,
reply
);
}
else
if
((
reply
=
sasl_errstring
(
result
,
NULL
,
NULL
))
!=
NULL
)
{
prot_printf
(
imapd_out
,
"%s NO Login failed: %s
\r\n
"
,
tag
,
reply
);
}
else
{
prot_printf
(
imapd_out
,
"%s NO Login failed: %d
\r\n
"
,
tag
,
result
);
}
snmp_increment_args
(
AUTHENTICATION_NO
,
1
,
VARIABLE_AUTH
,
hash_simple
(
"LOGIN"
),
VARIABLE_LISTEND
);
return
;
}
else
{
imapd_userid
=
xstrdup
(
canon_user
);
snmp_increment_args
(
AUTHENTICATION_YES
,
1
,
VARIABLE_AUTH
,
hash_simple
(
"LOGIN"
),
VARIABLE_LISTEND
);
syslog
(
LOG_NOTICE
,
"login: %s %s plaintext %s"
,
imapd_clienthost
,
canon_user
,
reply
?
reply
:
""
);
plaintextloginpause
=
config_getint
(
"plaintextloginpause"
,
0
);
if
(
plaintextloginpause
!=
0
&&
!
imapd_starttls_done
)
{
/* Apply penalty only if not under layer */
sleep
(
plaintextloginpause
);
}
}
imapd_authstate
=
auth_newstate
(
canon_user
,
(
char
*
)
0
);
val
=
config_getstring
(
"admins"
,
""
);
while
(
*
val
)
{
for
(
p
=
(
char
*
)
val
;
*
p
&&
!
isspace
((
int
)
*
p
);
p
++
);
strncpy
(
buf
,
val
,
p
-
val
);
buf
[
p
-
val
]
=
0
;
if
(
auth_memberof
(
imapd_authstate
,
buf
))
{
imapd_userisadmin
=
1
;
break
;
}
val
=
p
;
while
(
*
val
&&
isspace
((
int
)
*
val
))
val
++
;
}
if
(
!
reply
)
reply
=
"User logged in"
;
/* Create telemetry log */
sprintf
(
buf
,
"%s%s%s/%lu"
,
config_dir
,
FNAME_LOGDIR
,
imapd_userid
,
(
unsigned
long
)
getpid
());
logfile
=
fopen
(
buf
,
"w"
);
if
(
logfile
)
{
prot_setlog
(
imapd_in
,
fileno
(
logfile
));
prot_setlog
(
imapd_out
,
fileno
(
logfile
));
if
(
config_getswitch
(
"logtimestamps"
,
0
))
{
prot_setlogtime
(
imapd_in
,
&
imapd_logtime
);
prot_setlogtime
(
imapd_out
,
&
imapd_logtime
);
}
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
reply
);
return
;
}
static
int
hash_simple
(
const
char
*
str
)
{
int
value
=
0
;
int
i
;
if
(
!
str
)
return
0
;
for
(
i
=
0
;
*
str
;
i
++
)
{
value
^=
(
*
str
++
<<
((
i
&
3
)
*
8
));
}
return
value
;
}
/*
* Perform an AUTHENTICATE command
*/
void
cmd_authenticate
(
char
*
tag
,
char
*
authtype
)
{
int
sasl_result
;
static
struct
buf
clientin
;
int
clientinlen
=
0
;
char
*
serverout
;
unsigned
int
serveroutlen
;
const
char
*
errstr
;
const
char
*
errorstring
=
NULL
;
char
buf
[
MAX_MAILBOX_PATH
];
FILE
*
logfile
;
int
*
ssfp
;
char
*
ssfmsg
=
NULL
;
sasl_result
=
sasl_server_start
(
imapd_saslconn
,
authtype
,
NULL
,
0
,
&
serverout
,
&
serveroutlen
,
&
errstr
);
/* sasl_server_start will return SASL_OK or SASL_CONTINUE on success */
while
(
sasl_result
==
SASL_CONTINUE
)
{
/* print the message to the user */
printauthready
(
imapd_out
,
serveroutlen
,
(
unsigned
char
*
)
serverout
);
free
(
serverout
);
/* get string from user */
clientinlen
=
getbase64string
(
imapd_in
,
&
clientin
);
if
(
clientinlen
==
-1
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid base64 string
\r\n
"
,
tag
);
return
;
}
sasl_result
=
sasl_server_step
(
imapd_saslconn
,
clientin
.
s
,
clientinlen
,
&
serverout
,
&
serveroutlen
,
&
errstr
);
}
/* failed authentication */
if
(
sasl_result
!=
SASL_OK
)
{
/* convert the sasl error code to a string */
errorstring
=
sasl_errstring
(
sasl_usererr
(
sasl_result
),
NULL
,
NULL
);
if
(
errstr
)
{
syslog
(
LOG_NOTICE
,
"badlogin: %s %s %s [%s]"
,
imapd_clienthost
,
authtype
,
errorstring
,
errstr
);
}
else
{
syslog
(
LOG_NOTICE
,
"badlogin: %s %s %s"
,
imapd_clienthost
,
authtype
,
errorstring
);
}
snmp_increment_args
(
AUTHENTICATION_NO
,
1
,
VARIABLE_AUTH
,
hash_simple
(
authtype
),
VARIABLE_LISTEND
);
sleep
(
3
);
if
(
errorstring
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
errorstring
);
}
else
{
prot_printf
(
imapd_out
,
"%s NO Error authenticating
\r\n
"
,
tag
);
}
return
;
}
/* successful authentication */
/* get the userid from SASL --- already canonicalized from
* mysasl_authproc()
*/
sasl_result
=
sasl_getprop
(
imapd_saslconn
,
SASL_USERNAME
,
(
void
**
)
&
imapd_userid
);
if
(
sasl_result
!=
SASL_OK
)
{
prot_printf
(
imapd_out
,
"%s NO weird SASL error %d SASL_USERNAME
\r\n
"
,
tag
,
sasl_result
);
syslog
(
LOG_ERR
,
"weird SASL error %d getting SASL_USERNAME"
,
sasl_result
);
return
;
}
proc_register
(
"imapd"
,
imapd_clienthost
,
imapd_userid
,
(
char
*
)
0
);
syslog
(
LOG_NOTICE
,
"login: %s %s %s %s"
,
imapd_clienthost
,
imapd_userid
,
authtype
,
"User logged in"
);
sasl_getprop
(
imapd_saslconn
,
SASL_SSF
,
(
void
**
)
&
ssfp
);
switch
(
*
ssfp
)
{
case
0
:
ssfmsg
=
"no protection"
;
break
;
case
1
:
ssfmsg
=
"integrity protection"
;
break
;
default
:
ssfmsg
=
"privacy protection"
;
break
;
}
snmp_increment_args
(
AUTHENTICATION_YES
,
1
,
VARIABLE_AUTH
,
hash_simple
(
authtype
),
VARIABLE_LISTEND
);
prot_printf
(
imapd_out
,
"%s OK Success (%s)
\r\n
"
,
tag
,
ssfmsg
);
prot_setsasl
(
imapd_in
,
imapd_saslconn
);
prot_setsasl
(
imapd_out
,
imapd_saslconn
);
/* Create telemetry log */
sprintf
(
buf
,
"%s%s%s/%lu"
,
config_dir
,
FNAME_LOGDIR
,
imapd_userid
,
(
unsigned
long
)
getpid
());
logfile
=
fopen
(
buf
,
"w"
);
if
(
logfile
)
{
prot_setlog
(
imapd_in
,
fileno
(
logfile
));
prot_setlog
(
imapd_out
,
fileno
(
logfile
));
if
(
config_getswitch
(
"logtimestamps"
,
0
))
{
prot_setlogtime
(
imapd_in
,
&
imapd_logtime
);
prot_setlogtime
(
imapd_out
,
&
imapd_logtime
);
}
}
return
;
}
/*
* Perform a NOOP command
*/
void
cmd_noop
(
tag
,
cmd
)
char
*
tag
;
char
*
cmd
;
{
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
1
);
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Parse and perform an ID command.
*
* the command has been parsed up to the parameter list.
*
* we only allow one ID in non-authenticated state from a given client.
* we only allow MAXIDFAILED consecutive failed IDs from a given client.
* we only record MAXIDLOG ID responses from a given client.
*
* the ID specification (draft-showalter-imap-id-03.txt) that i'm
* working from says one thing and does another. i'll do what i think
* it means, which means that this is subject to change.
*/
#define MAXIDFAILED 3
#define MAXIDLOG 5
#define MAXIDFIELDLEN 30
#define MAXIDVALUELEN 1024
#define MAXIDPAIRS 30
static
char
id_resp_command
[
MAXIDVALUELEN
];
static
char
id_resp_arguments
[
MAXIDVALUELEN
]
=
""
;
void
id_getcmdline
(
int
argc
,
char
**
argv
)
{
snprintf
(
id_resp_command
,
MAXIDVALUELEN
,
*
argv
);
while
(
--
argc
>
0
)
{
snprintf
(
id_resp_arguments
+
strlen
(
id_resp_arguments
),
MAXIDVALUELEN
-
strlen
(
id_resp_arguments
),
"%s%s"
,
*++
argv
,
(
argc
>
1
)
?
" "
:
""
);
}
}
void
cmd_id
(
char
*
tag
)
{
static
int
did_id
=
0
;
static
int
failed_id
=
0
;
static
int
logged_id
=
0
;
int
error
=
0
;
int
c
=
EOF
,
npair
=
0
;
static
struct
buf
arg
,
field
;
struct
strlist
*
fields
=
0
,
*
values
=
0
;
struct
utsname
os
;
/* check if we've already had an ID in non-authenticated state */
if
(
!
imapd_userid
&&
did_id
)
{
prot_printf
(
imapd_out
,
"%s NO Only one Id allowed in non-authenticated state
\r\n
"
,
tag
);
eatline
(
c
);
return
;
}
/* check if we've had too many failed IDs in a row */
if
(
failed_id
>=
MAXIDFAILED
)
{
prot_printf
(
imapd_out
,
"%s NO Too many (%u) invalid Id commands
\r\n
"
,
tag
,
failed_id
);
eatline
(
c
);
return
;
}
/* ok, accept parameter list */
c
=
getword
(
&
arg
);
/* check for "NIL" or start of parameter list */
if
(
strcasecmp
(
arg
.
s
,
"NIL"
)
&&
c
!=
'('
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid parameter list in Id
\r\n
"
,
tag
);
eatline
(
c
);
failed_id
++
;
return
;
}
/* parse parameter list */
if
(
c
==
'('
)
{
for
(;;)
{
if
(
c
==
')'
)
{
/* end of string/value pairs */
break
;
}
/* get field name */
c
=
getstring
(
&
field
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid/missing field name in Id
\r\n
"
,
tag
);
error
=
1
;
break
;
}
/* get field value */
c
=
getnstring
(
&
arg
);
if
(
c
!=
' '
&&
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid/missing value in Id
\r\n
"
,
tag
);
error
=
1
;
break
;
}
/* ok, we're anal, but we'll still process the ID command */
if
(
strlen
(
field
.
s
)
>
MAXIDFIELDLEN
)
{
prot_printf
(
imapd_out
,
"%s BAD field longer than %u octets in Id
\r\n
"
,
tag
,
MAXIDFIELDLEN
);
error
=
1
;
break
;
}
if
(
strlen
(
arg
.
s
)
>
MAXIDVALUELEN
)
{
prot_printf
(
imapd_out
,
"%s BAD value longer than %u octets in Id
\r\n
"
,
tag
,
MAXIDVALUELEN
);
error
=
1
;
break
;
}
if
(
++
npair
>
MAXIDPAIRS
)
{
prot_printf
(
imapd_out
,
"%s BAD too many (%u) field-value pairs in ID
\r\n
"
,
tag
,
MAXIDPAIRS
);
error
=
1
;
break
;
}
/* ok, we're happy enough */
appendstrlist
(
&
fields
,
field
.
s
);
appendstrlist
(
&
values
,
arg
.
s
);
}
if
(
error
||
c
!=
')'
)
{
/* erp! */
eatline
(
c
);
freestrlist
(
fields
);
freestrlist
(
values
);
failed_id
++
;
return
;
}
c
=
prot_getc
(
imapd_in
);
}
/* check for CRLF */
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to Id
\r\n
"
,
tag
);
eatline
(
c
);
freestrlist
(
fields
);
freestrlist
(
values
);
failed_id
++
;
return
;
}
/* log the client's ID string.
eventually this should be a callback or something. */
if
(
npair
&&
logged_id
<
MAXIDLOG
)
{
#define LOGSTR "client id:"
char
logbuf
[
strlen
(
LOGSTR
)
+
MAXIDPAIRS
*
(
MAXIDFIELDLEN
+
MAXIDVALUELEN
+
6
)];
struct
strlist
*
fptr
,
*
vptr
;
strcpy
(
logbuf
,
LOGSTR
);
for
(
fptr
=
fields
,
vptr
=
values
;
fptr
;
fptr
=
fptr
->
next
,
vptr
=
vptr
->
next
)
{
/* should we check for and format literals here ??? */
sprintf
(
logbuf
+
strlen
(
logbuf
),
"
\"
%s
\"
"
,
fptr
->
s
);
if
(
!
strcmp
(
vptr
->
s
,
"NIL"
))
sprintf
(
logbuf
+
strlen
(
logbuf
),
"NIL"
);
else
sprintf
(
logbuf
+
strlen
(
logbuf
),
"
\"
%s
\"
"
,
vptr
->
s
);
}
syslog
(
LOG_INFO
,
"%s"
,
logbuf
);
logged_id
++
;
}
freestrlist
(
fields
);
freestrlist
(
values
);
/* spit out our ID string.
eventually this might be configurable. */
if
(
config_getswitch
(
"imapidresponse"
,
1
))
{
char
env_buf
[
MAXIDVALUELEN
];
prot_printf
(
imapd_out
,
"* ID ("
"
\"
name
\"
\"
Cyrus
\"
"
"
\"
version
\"
\"
%s
\"
"
"
\"
vendor
\"
\"
Project Cyrus
\"
"
"
\"
support-url
\"
\"
http://asg.web.cmu.edu/cyrus
\"
"
,
CYRUS_VERSION
);
/* add the os info */
if
(
uname
(
&
os
)
!=
-1
)
prot_printf
(
imapd_out
,
"
\"
os
\"
\"
%s
\"
"
"
\"
os-version
\"
\"
%s
\"
"
,
os
.
sysname
,
os
.
release
);
/* add the command line info */
prot_printf
(
imapd_out
,
"
\"
command
\"
\"
%s
\"
"
,
id_resp_command
);
if
(
strlen
(
id_resp_arguments
))
prot_printf
(
imapd_out
,
"
\"
arguments
\"
\"
%s
\"
"
,
id_resp_arguments
);
else
prot_printf
(
imapd_out
,
"
\"
arguments
\"
NIL"
);
/* add the environment info */
snprintf
(
env_buf
,
MAXIDVALUELEN
,
"Cyrus SASL %d.%d.%d"
,
SASL_VERSION_MAJOR
,
SASL_VERSION_MINOR
,
SASL_VERSION_STEP
);
#ifdef DB_VERSION_STRING
snprintf
(
env_buf
+
strlen
(
env_buf
),
MAXIDVALUELEN
-
strlen
(
env_buf
),
"; %s"
,
DB_VERSION_STRING
);
#endif
#ifdef HAVE_SSL
snprintf
(
env_buf
+
strlen
(
env_buf
),
MAXIDVALUELEN
-
strlen
(
env_buf
),
"; %s"
,
OPENSSL_VERSION_TEXT
);
#endif
prot_printf
(
imapd_out
,
"
\"
environment
\"
\"
%s
\"
)
\r\n
"
,
env_buf
);
}
else
prot_printf
(
imapd_out
,
"* ID NIL
\r\n
"
);
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
failed_id
=
0
;
did_id
=
1
;
}
/*
* Perform a CAPABILITY command
*/
void
cmd_capability
(
char
*
tag
)
{
char
*
sasllist
;
/* the list of SASL mechanisms */
unsigned
mechcount
;
const
char
*
acapserver
;
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
prot_printf
(
imapd_out
,
"* CAPABILITY "
CAPABILITY_STRING
);
/* add the thread algorithms */
list_thread_algorithms
(
imapd_out
);
if
(
starttls_enabled
())
{
prot_printf
(
imapd_out
,
" STARTTLS"
);
}
if
(
!
imapd_starttls_done
&&
!
config_getswitch
(
"allowplaintext"
,
1
))
{
prot_printf
(
imapd_out
,
" LOGINDISABLED"
);
}
acapserver
=
config_getstring
(
"acap_server"
,
NULL
);
if
(
acapserver
!=
NULL
)
{
prot_printf
(
imapd_out
,
" ACAP=acap://%s%s/"
,
acapserver
,
global_dataset
);
}
/* add the SASL mechs */
if
(
sasl_listmech
(
imapd_saslconn
,
NULL
,
"AUTH="
,
" AUTH="
,
""
,
&
sasllist
,
NULL
,
&
mechcount
)
==
SASL_OK
&&
mechcount
>
0
)
{
prot_printf
(
imapd_out
,
" %s"
,
sasllist
);
free
(
sasllist
);
}
else
{
/* else don't show anything */
}
#ifdef ENABLE_X_NETSCAPE_HACK
prot_printf
(
imapd_out
,
" X-NETSCAPE"
);
#endif
prot_printf
(
imapd_out
,
"
\r\n
%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Parse and perform an APPEND command.
* The command has been parsed up to and including
* the mailbox name.
*/
static
int
isokflag
(
char
*
s
)
{
if
(
s
[
0
]
==
'\\'
)
{
lcase
(
s
);
if
(
!
strcmp
(
s
,
"
\\
seen"
))
return
1
;
if
(
!
strcmp
(
s
,
"
\\
answered"
))
return
1
;
if
(
!
strcmp
(
s
,
"
\\
flagged"
))
return
1
;
if
(
!
strcmp
(
s
,
"
\\
draft"
))
return
1
;
if
(
!
strcmp
(
s
,
"
\\
deleted"
))
return
1
;
/* uh oh, system flag i don't recognize */
return
0
;
}
else
{
/* valid user flag? */
return
imparse_isatom
(
s
);
}
}
#define FLAGGROW 10
void
cmd_append
(
char
*
tag
,
char
*
name
)
{
int
c
;
char
**
flag
=
NULL
;
int
nflags
=
0
,
flagalloc
=
0
;
static
struct
buf
arg
;
char
*
p
;
time_t
internaldate
;
unsigned
size
=
0
;
int
sawdigit
=
0
;
int
isnowait
=
0
;
int
r
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
struct
appendstate
mailbox
;
unsigned
long
uidvalidity
;
unsigned
long
firstuid
,
num
;
const
char
*
parseerr
=
NULL
;
/* Set up the append */
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
append_setup
(
&
mailbox
,
mailboxname
,
MAILBOX_FORMAT_NORMAL
,
imapd_authstate
,
ACL_INSERT
,
size
);
}
if
(
r
)
{
eatline
(
' '
);
prot_printf
(
imapd_out
,
"%s NO %s%s
\r\n
"
,
tag
,
(
r
==
IMAP_MAILBOX_NONEXISTENT
&&
mboxlist_createmailboxcheck
(
mailboxname
,
0
,
0
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
(
char
**
)
0
,
(
char
**
)
0
)
==
0
)
?
"[TRYCREATE] "
:
""
,
error_message
(
r
));
return
;
}
c
=
' '
;
/* just parsed a space */
/* we loop, to support MULTIAPPEND */
while
(
!
r
&&
c
==
' '
)
{
/* Parse flags */
c
=
getword
(
&
arg
);
if
(
c
==
'('
&&
!
arg
.
s
[
0
])
{
do
{
c
=
getword
(
&
arg
);
if
(
!
nflags
&&
!
arg
.
s
[
0
]
&&
c
==
')'
)
break
;
/* empty list */
if
(
!
isokflag
(
arg
.
s
))
{
parseerr
=
"Invalid flag in Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
if
(
nflags
==
flagalloc
)
{
flagalloc
+=
FLAGGROW
;
flag
=
(
char
**
)
xrealloc
((
char
*
)
flag
,
flagalloc
*
sizeof
(
char
*
));
}
flag
[
nflags
++
]
=
xstrdup
(
arg
.
s
);
}
while
(
c
==
' '
);
if
(
c
!=
')'
)
{
parseerr
=
"Missing space or ) after flag name in Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
' '
)
{
parseerr
=
"Missing space after flag list in Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
c
=
getword
(
&
arg
);
}
/* Parse internaldate */
if
(
c
==
'\"'
&&
!
arg
.
s
[
0
])
{
prot_ungetc
(
c
,
imapd_in
);
c
=
getdatetime
(
&
internaldate
);
if
(
c
!=
' '
)
{
parseerr
=
"Invalid date-time in Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
c
=
getword
(
&
arg
);
}
else
{
internaldate
=
time
(
NULL
);
}
if
(
arg
.
s
[
0
]
!=
'{'
)
{
parseerr
=
"Missing required argument to Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
/* Read size from literal */
size
=
0
;
for
(
p
=
arg
.
s
+
1
;
*
p
&&
isdigit
((
int
)
*
p
);
p
++
)
{
sawdigit
++
;
size
=
size
*
10
+
*
p
-
'0'
;
}
if
(
*
p
==
'+'
)
{
isnowait
++
;
p
++
;
}
if
(
c
==
'\r'
)
{
c
=
prot_getc
(
imapd_in
);
}
else
{
prot_ungetc
(
c
,
imapd_in
);
c
=
' '
;
/* Force a syntax error */
}
if
(
*
p
!=
'}'
||
p
[
1
]
||
c
!=
'\n'
||
!
sawdigit
)
{
parseerr
=
"Invalid literal in Append command"
;
r
=
IMAP_PROTOCOL_ERROR
;
goto
done
;
}
if
(
size
<
2
)
{
r
=
IMAP_MESSAGE_NOBLANKLINE
;
goto
done
;
}
if
(
!
isnowait
)
{
/* Tell client to send the message */
prot_printf
(
imapd_out
,
"+ go ahead
\r\n
"
);
prot_flush
(
imapd_out
);
}
/* Perform the rest of the append */
if
(
!
r
)
r
=
append_fromstream
(
&
mailbox
,
imapd_in
,
size
,
internaldate
,
(
const
char
**
)
flag
,
nflags
,
imapd_userid
);
/* if we see a SP, we're trying to append more than one message */
/* Parse newline terminating command */
c
=
prot_getc
(
imapd_in
);
}
done
:
if
(
r
)
{
eatline
(
c
);
}
else
{
/* we should be looking at the end of the line */
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
parseerr
=
"junk after literal"
;
r
=
IMAP_PROTOCOL_ERROR
;
eatline
(
c
);
}
}
if
(
!
r
)
{
r
=
append_commit
(
&
mailbox
,
&
uidvalidity
,
&
firstuid
,
&
num
);
}
else
{
append_abort
(
&
mailbox
);
}
if
(
imapd_mailbox
)
{
index_check
(
imapd_mailbox
,
0
,
0
);
}
if
(
r
==
IMAP_PROTOCOL_ERROR
&&
parseerr
)
{
prot_printf
(
imapd_out
,
"%s BAD %s
\r\n
"
,
tag
,
parseerr
);
}
else
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s%s
\r\n
"
,
tag
,
(
r
==
IMAP_MAILBOX_NONEXISTENT
&&
mboxlist_createmailboxcheck
(
mailboxname
,
0
,
0
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
(
char
**
)
0
,
(
char
**
)
0
)
==
0
)
?
"[TRYCREATE] "
:
""
,
error_message
(
r
));
}
else
{
/* is this a space seperated list or sequence list? */
prot_printf
(
imapd_out
,
"%s OK [APPENDUID %u"
,
tag
,
uidvalidity
);
if
(
num
==
1
)
{
prot_printf
(
imapd_out
,
" %u"
,
firstuid
);
}
else
{
prot_printf
(
imapd_out
,
" %u:%u"
,
firstuid
,
firstuid
+
num
-
1
);
}
prot_printf
(
imapd_out
,
"] %s
\r\n
"
,
error_message
(
IMAP_OK_COMPLETED
));
}
/* free memory */
while
(
nflags
--
)
{
free
(
flag
[
nflags
]);
}
if
(
flag
)
free
((
char
*
)
flag
);
}
/*
* Perform a SELECT/EXAMINE/BBOARD command
*/
void
cmd_select
(
char
*
tag
,
char
*
cmd
,
char
*
name
)
{
struct
mailbox
mailbox
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
int
r
=
0
;
double
usage
;
int
doclose
=
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
{
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
}
if
(
!
r
)
{
r
=
mailbox_open_header
(
mailboxname
,
imapd_authstate
,
&
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
=
((
double
)
imapd_mailbox
->
quota
.
used
*
100.0
)
/
(
double
)
(
imapd_mailbox
->
quota
.
limit
*
QUOTA_UNITS
);
if
(
usage
>=
100.0
)
{
prot_printf
(
imapd_out
,
"* NO [ALERT] %s
\r\n
"
,
error_message
(
IMAP_NO_OVERQUOTA
));
}
else
if
(
usage
>
config_getint
(
"quotawarn"
,
90
))
{
int
usageint
=
(
int
)
usage
;
prot_printf
(
imapd_out
,
"* NO [ALERT] "
);
prot_printf
(
imapd_out
,
error_message
(
IMAP_NO_CLOSEQUOTA
),
usageint
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
}
}
prot_printf
(
imapd_out
,
"%s OK [READ-%s] %s
\r\n
"
,
tag
,
(
imapd_mailbox
->
myrights
&
(
ACL_WRITE
|
ACL_DELETE
))
?
"WRITE"
:
"ONLY"
,
error_message
(
IMAP_OK_COMPLETED
));
proc_register
(
"imapd"
,
imapd_clienthost
,
imapd_userid
,
mailboxname
);
syslog
(
LOG_DEBUG
,
"open: user %s opened %s"
,
imapd_userid
,
name
);
}
/*
* Perform a CLOSE command
*/
void
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
/*
* Perform an UNSELECT command -- for some support of IMAP proxy.
* Just like close except no expunge.
*/
void
cmd_unselect
(
tag
)
char
*
tag
;
{
index_closemailbox
(
imapd_mailbox
);
mailbox_close
(
imapd_mailbox
);
imapd_mailbox
=
0
;
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Parse and perform a FETCH/UID FETCH command
* The command has been parsed up to and including
* the sequence
*/
void
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
;
struct
strlist
*
newfields
=
0
;
char
*
p
,
*
section
;
int
fetchedsomething
;
fetchargs
=
zerofetchargs
;
c
=
getword
(
&
fetchatt
);
if
(
c
==
'('
&&
!
fetchatt
.
s
[
0
])
{
inlist
=
1
;
c
=
getword
(
&
fetchatt
);
}
for
(;;)
{
ucase
(
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
((
int
)
*
p
)
||
*
p
==
'.'
)
{
if
(
*
p
==
'.'
&&
!
isdigit
((
int
)
p
[
-1
]))
break
;
/* Obsolete section 0 can only occur before close brace */
if
(
*
p
==
'0'
&&
!
isdigit
((
int
)
p
[
-1
])
&&
p
[
1
]
!=
']'
)
break
;
p
++
;
}
if
(
*
p
==
'H'
&&
!
strncmp
(
p
,
"HEADER.FIELDS"
,
13
)
&&
(
p
==
section
||
p
[
-1
]
==
'.'
)
&&
(
p
[
13
]
==
'\0'
||
!
strcmp
(
p
+
13
,
".NOT"
)))
{
/*
* If not top-level or a HEADER.FIELDS.NOT, can't pull
* the headers out of the cache.
*/
if
(
p
!=
section
||
p
[
13
]
!=
'\0'
)
{
fetchitems
|=
FETCH_UNCACHEDHEADER
;
}
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required argument to %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
eatline
(
c
);
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
);
eatline
(
c
);
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
);
eatline
(
c
);
goto
freeargs
;
}
appendstrlist
(
&
newfields
,
fieldname
.
s
);
if
(
!
(
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
);
eatline
(
c
);
goto
freeargs
;
}
/* Grab/parse the ]<x.y> part */
c
=
getword
(
&
fieldname
);
p
=
fieldname
.
s
;
if
(
*
p
++
!=
']'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing required close bracket after %s %s
\r\n
"
,
tag
,
cmd
,
fetchatt
.
s
);
eatline
(
c
);
goto
freeargs
;
}
if
(
*
p
==
'<'
&&
isdigit
((
int
)
p
[
1
]))
{
p
+=
2
;
while
(
isdigit
((
int
)
*
p
))
p
++
;
if
(
*
p
==
'.'
&&
p
[
1
]
>=
'1'
&&
p
[
1
]
<=
'9'
)
{
p
+=
2
;
while
(
isdigit
((
int
)
*
p
))
p
++
;
}
else
p
--
;
if
(
*
p
!=
'>'
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid body partial
\r\n
"
,
tag
);
eatline
(
c
);
goto
freeargs
;
}
p
++
;
}
if
(
*
p
)
{
prot_printf
(
imapd_out
,
"%s BAD Junk after body section
\r\n
"
,
tag
);
eatline
(
c
);
goto
freeargs
;
}
appendfieldlist
(
&
fetchargs
.
fsections
,
section
,
newfields
,
fieldname
.
s
);
newfields
=
0
;
break
;
}
switch
(
*
p
)
{
case
'H'
:
if
(
p
!=
section
&&
p
[
-1
]
!=
'.'
)
break
;
if
(
!
strncmp
(
p
,
"HEADER]"
,
7
))
p
+=
6
;
break
;
case
'M'
:
if
(
!
strncmp
(
p
-1
,
".MIME]"
,
6
))
p
+=
4
;
break
;
case
'T'
:
if
(
p
!=
section
&&
p
[
-1
]
!=
'.'
)
break
;
if
(
!
strncmp
(
p
,
"TEXT]"
,
5
))
p
+=
4
;
break
;
}
if
(
*
p
!=
']'
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid body section
\r\n
"
,
tag
);
eatline
(
c
);
goto
freeargs
;
}
p
++
;
if
(
*
p
==
'<'
&&
isdigit
((
int
)
p
[
1
]))
{
p
+=
2
;
while
(
isdigit
((
int
)
*
p
))
p
++
;
if
(
*
p
==
'.'
&&
p
[
1
]
>=
'1'
&&
p
[
1
]
<=
'9'
)
{
p
+=
2
;
while
(
isdigit
((
int
)
*
p
))
p
++
;
}
else
p
--
;
if
(
*
p
!=
'>'
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid body partial
\r\n
"
,
tag
);
eatline
(
c
);
goto
freeargs
;
}
p
++
;
}
if
(
*
p
)
{
prot_printf
(
imapd_out
,
"%s BAD Junk after body section
\r\n
"
,
tag
);
eatline
(
c
);
goto
freeargs
;
}
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
);
eatline
(
c
);
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
);
eatline
(
c
);
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
);
eatline
(
c
);
goto
freeargs
;
}
lcase
(
fieldname
.
s
);;
appendstrlist
(
strlen
(
fetchatt
.
s
)
==
19
?
&
fetchargs
.
headers
:
&
fetchargs
.
headers_not
,
fieldname
.
s
);
if
(
strlen
(
fetchatt
.
s
)
!=
19
)
{
fetchitems
|=
FETCH_UNCACHEDHEADER
;
}
if
(
!
(
fetchitems
&
FETCH_UNCACHEDHEADER
))
{
for
(
i
=
0
;
i
<
mailbox_num_cache_header
;
i
++
)
{
if
(
!
strcmp
(
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
);
eatline
(
c
);
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
);
eatline
(
c
);
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
);
eatline
(
c
);
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
(
c
);
goto
freeargs
;
}
if
(
!
fetchitems
&&
!
fetchargs
.
bodysections
&&
!
fetchargs
.
fsections
&&
!
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
,
&
fetchedsomething
);
if
(
fetchedsomething
||
usinguid
)
{
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
else
{
/* normal FETCH, nothing came back */
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_NO_NOSUCHMSG
));
}
freeargs
:
freestrlist
(
newfields
);
freestrlist
(
fetchargs
.
bodysections
);
freefieldlist
(
fetchargs
.
fsections
);
freestrlist
(
fetchargs
.
headers
);
freestrlist
(
fetchargs
.
headers_not
);
}
/*
* Perform a PARTIAL command
*/
void
cmd_partial
(
tag
,
msgno
,
data
,
start
,
count
)
char
*
tag
;
char
*
msgno
;
char
*
data
;
char
*
start
;
char
*
count
;
{
char
*
p
;
struct
fetchargs
fetchargs
;
char
*
section
;
int
fetchedsomething
;
fetchargs
=
zerofetchargs
;
for
(
p
=
msgno
;
*
p
;
p
++
)
{
if
(
!
isdigit
((
int
)
*
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
((
int
)
*
p
)
||
*
p
==
'.'
)
{
if
(
*
p
==
'.'
&&
(
p
==
section
||
!
isdigit
((
int
)
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
((
int
)
*
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
((
int
)
*
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
,
&
fetchedsomething
);
index_check
(
imapd_mailbox
,
0
,
0
);
if
(
fetchedsomething
)
{
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid sequence in PARTIAL command
\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
*/
void
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
);
eatline
(
c
);
goto
freeflags
;
}
}
else
if
(
!
imparse_isatom
(
flagname
.
s
))
{
prot_printf
(
imapd_out
,
"%s BAD Invalid flag name %s in %s command
\r\n
"
,
tag
,
flagname
.
s
,
cmd
);
eatline
(
c
);
goto
freeflags
;
}
else
{
if
(
nflags
==
flagalloc
)
{
flagalloc
+=
FLAGGROW
;
flag
=
(
char
**
)
xrealloc
((
char
*
)
flag
,
flagalloc
*
sizeof
(
char
*
));
}
flag
[
nflags
++
]
=
xstrdup
(
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
);
eatline
(
c
);
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
(
c
);
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
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
freeflags
:
while
(
nflags
--
)
{
free
(
flag
[
nflags
]);
}
if
(
flag
)
free
((
char
*
)
flag
);
}
void
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
(
c
);
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
freesearchargs
(
searchargs
);
}
/*
* Perform a SORT/UID SORT command
*/
void
cmd_sort
(
tag
,
usinguid
)
char
*
tag
;
int
usinguid
;
{
int
c
;
struct
sortcrit
*
sortcrit
;
static
struct
buf
arg
;
int
charset
=
0
;
struct
searchargs
*
searchargs
;
static
struct
searchargs
zerosearchargs
;
c
=
getsortcriteria
(
tag
,
&
sortcrit
);
if
(
c
==
EOF
)
{
eatline
(
' '
);
freesortcrit
(
sortcrit
);
return
;
}
/* get charset */
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing charset in Sort
\r\n
"
,
tag
);
eatline
(
c
);
freesortcrit
(
sortcrit
);
return
;
}
c
=
getword
(
&
arg
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing search criteria in Sort
\r\n
"
,
tag
);
eatline
(
c
);
freesortcrit
(
sortcrit
);
return
;
}
lcase
(
arg
.
s
);
charset
=
charset_lookupname
(
arg
.
s
);
if
(
charset
==
-1
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_UNRECOGNIZED_CHARSET
));
eatline
(
c
);
freesortcrit
(
sortcrit
);
return
;
}
searchargs
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
searchargs
=
zerosearchargs
;
c
=
getsearchprogram
(
tag
,
searchargs
,
&
charset
,
0
);
if
(
c
==
EOF
)
{
eatline
(
' '
);
freesearchargs
(
searchargs
);
freesortcrit
(
sortcrit
);
return
;
}
if
(
c
==
'\r'
)
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\n'
)
{
prot_printf
(
imapd_out
,
"%s BAD Unexpected extra arguments to Sort
\r\n
"
,
tag
);
eatline
(
c
);
freesearchargs
(
searchargs
);
freesortcrit
(
sortcrit
);
return
;
}
index_sort
(
imapd_mailbox
,
sortcrit
,
searchargs
,
usinguid
);
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
freesortcrit
(
sortcrit
);
freesearchargs
(
searchargs
);
return
;
}
/*
* Perform a THREAD/UID THREAD command
*/
void
cmd_thread
(
tag
,
usinguid
)
char
*
tag
;
int
usinguid
;
{
static
struct
buf
arg
;
int
c
;
int
charset
=
0
;
int
alg
;
struct
searchargs
*
searchargs
;
static
struct
searchargs
zerosearchargs
;
/* get algorithm */
c
=
getword
(
&
arg
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing algorithm in Thread
\r\n
"
,
tag
);
eatline
(
c
);
return
;
}
if
((
alg
=
find_thread_algorithm
(
arg
.
s
))
==
-1
)
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Thread algorithm %s
\r\n
"
,
tag
,
arg
.
s
);
eatline
(
c
);
return
;
}
/* get charset */
c
=
getword
(
&
arg
);
if
(
c
!=
' '
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing charset in Thread
\r\n
"
,
tag
);
eatline
(
c
);
return
;
}
lcase
(
arg
.
s
);
charset
=
charset_lookupname
(
arg
.
s
);
if
(
charset
==
-1
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_UNRECOGNIZED_CHARSET
));
eatline
(
c
);
return
;
}
searchargs
=
(
struct
searchargs
*
)
xmalloc
(
sizeof
(
struct
searchargs
));
*
searchargs
=
zerosearchargs
;
c
=
getsearchprogram
(
tag
,
searchargs
,
&
charset
,
0
);
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 Thread
\r\n
"
,
tag
);
eatline
(
c
);
freesearchargs
(
searchargs
);
return
;
}
index_thread
(
imapd_mailbox
,
alg
,
searchargs
,
usinguid
);
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
freesearchargs
(
searchargs
);
return
;
}
/*
* Perform a COPY/UID COPY command
*/
void
cmd_copy
(
tag
,
sequence
,
name
,
usinguid
)
char
*
tag
;
char
*
sequence
;
char
*
name
;
int
usinguid
;
{
int
r
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
char
*
copyuid
;
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
index_copy
(
imapd_mailbox
,
sequence
,
usinguid
,
mailboxname
,
&
copyuid
);
}
index_check
(
imapd_mailbox
,
usinguid
,
0
);
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s%s
\r\n
"
,
tag
,
(
r
==
IMAP_MAILBOX_NONEXISTENT
&&
mboxlist_createmailboxcheck
(
mailboxname
,
0
,
0
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
(
char
**
)
0
,
(
char
**
)
0
)
==
0
)
?
"[TRYCREATE] "
:
""
,
error_message
(
r
));
}
else
{
if
(
copyuid
)
{
prot_printf
(
imapd_out
,
"%s OK [COPYUID %s] %s
\r\n
"
,
tag
,
copyuid
,
error_message
(
IMAP_OK_COMPLETED
));
free
(
copyuid
);
}
else
if
(
usinguid
)
{
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
else
{
/* normal COPY, message doesn't exist */
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
IMAP_NO_NOSUCHMSG
));
}
}
}
/*
* Perform an EXPUNGE command
*/
void
cmd_expunge
(
tag
,
sequence
)
char
*
tag
;
char
*
sequence
;
{
int
r
;
if
(
!
(
imapd_mailbox
->
myrights
&
ACL_DELETE
))
r
=
IMAP_PERMISSION_DENIED
;
else
if
(
sequence
)
{
r
=
mailbox_expunge
(
imapd_mailbox
,
1
,
index_expungeuidlist
,
sequence
);
}
else
{
r
=
mailbox_expunge
(
imapd_mailbox
,
1
,
(
mailbox_decideproc_t
*
)
0
,
(
void
*
)
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
/*
* Perform a CREATE command
*/
void
cmd_create
(
tag
,
name
,
partition
)
char
*
tag
;
char
*
name
;
char
*
partition
;
{
int
r
=
0
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
int
autocreatequota
;
if
(
partition
&&
!
imapd_userisadmin
)
{
r
=
IMAP_PERMISSION_DENIED
;
}
if
(
name
[
0
]
&&
name
[
strlen
(
name
)
-1
]
==
'.'
)
{
/* We don't care about trailing hierarchy delimiters. */
name
[
strlen
(
name
)
-1
]
=
'\0'
;
}
if
(
!
r
)
{
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
}
if
(
!
r
)
{
r
=
mboxlist_createmailbox
(
mailboxname
,
0
,
partition
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
);
if
(
r
==
IMAP_PERMISSION_DENIED
&&
!
strcasecmp
(
name
,
"INBOX"
)
&&
(
autocreatequota
=
config_getint
(
"autocreatequota"
,
0
)))
{
/* Auto create */
r
=
mboxlist_createmailbox
(
mailboxname
,
0
,
partition
,
1
,
imapd_userid
,
imapd_authstate
);
if
(
!
r
&&
autocreatequota
>
0
)
{
(
void
)
mboxlist_setquota
(
mailboxname
,
autocreatequota
);
}
}
}
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
/*
* Perform a DELETE command
*/
void
cmd_delete
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
r
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_deletemailbox
(
mailboxname
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
struct
tmplist
{
int
alloc
;
int
num
;
char
mb
[
1
][
MAX_MAILBOX_NAME
];
};
#define TMPLIST_INC 50
static
int
addmbox
(
char
*
name
,
int
matchlen
,
int
maycreate
,
void
*
rock
)
{
struct
tmplist
**
lptr
=
(
struct
tmplist
**
)
rock
;
struct
tmplist
*
l
=
*
lptr
;
if
(
l
->
alloc
==
l
->
num
)
{
l
->
alloc
+=
TMPLIST_INC
;
l
=
xrealloc
(
l
,
sizeof
(
struct
tmplist
)
+
l
->
alloc
*
MAX_MAILBOX_NAME
*
(
sizeof
(
char
)));
*
lptr
=
l
;
}
strcpy
(
l
->
mb
[
l
->
num
++
],
name
);
return
0
;
}
/*
* Perform a RENAME command
*/
void
cmd_rename
(
const
char
*
tag
,
char
*
oldname
,
char
*
newname
,
char
*
partition
)
{
int
r
=
0
;
char
oldmailboxname
[
MAX_MAILBOX_NAME
+
3
];
char
newmailboxname
[
MAX_MAILBOX_NAME
+
2
];
char
*
p
;
int
isinbox
;
/* canonicalize names */
if
(
partition
&&
!
imapd_userisadmin
)
{
r
=
IMAP_PERMISSION_DENIED
;
}
isinbox
=
!
strcasecmp
(
oldname
,
"inbox"
);
if
(
!
r
)
r
=
mboxname_tointernal
(
oldname
,
imapd_userid
,
oldmailboxname
);
if
(
!
r
)
r
=
mboxname_tointernal
(
newname
,
imapd_userid
,
newmailboxname
);
/* verify that the mailbox doesn't have a wildcard in it */
for
(
p
=
oldmailboxname
;
!
r
&&
*
p
;
p
++
)
{
if
(
*
p
==
'*'
||
*
p
==
'%'
)
r
=
IMAP_MAILBOX_BADNAME
;
}
/* attempt to rename the base mailbox */
if
(
!
r
)
{
r
=
mboxlist_renamemailbox
(
oldmailboxname
,
newmailboxname
,
partition
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
);
}
/* rename all mailboxes matching this */
if
(
!
r
&&
!
isinbox
)
{
struct
tmplist
*
l
=
xmalloc
(
sizeof
(
struct
tmplist
));
int
ol
=
strlen
(
oldmailboxname
)
+
1
;
int
nl
=
strlen
(
newmailboxname
)
+
1
;
int
i
;
l
->
alloc
=
0
;
l
->
num
=
0
;
strcat
(
oldmailboxname
,
".*"
);
strcat
(
newmailboxname
,
"."
);
/* add submailboxes; we pretend we're an admin since we successfully
renamed the parent */
mboxlist_findall
(
oldmailboxname
,
1
,
imapd_userid
,
imapd_authstate
,
addmbox
,
&
l
);
/* foreach mailbox in list, rename it, pretending we're admin */
for
(
i
=
0
;
i
<
l
->
num
;
i
++
)
{
int
r2
=
0
;
if
(
nl
+
strlen
(
l
->
mb
[
i
]
+
ol
)
>
MAX_MAILBOX_NAME
)
{
/* this mailbox name is too long */
continue
;
}
strcpy
(
newmailboxname
+
nl
,
l
->
mb
[
i
]
+
ol
);
r2
=
mboxlist_renamemailbox
(
l
->
mb
[
i
],
newmailboxname
,
partition
,
1
,
imapd_userid
,
imapd_authstate
);
if
(
r2
)
break
;
}
free
(
l
);
}
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
/*
* Perform a FIND command
*/
void
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
,
imapd_authstate
,
mailboxdata
,
NULL
,
0
);
}
else
if
(
!
strcmp
(
namespace
,
"all.mailboxes"
))
{
mboxlist_findall
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
mailboxdata
,
NULL
);
}
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a LIST or LSUB command
*/
void
cmd_list
(
tag
,
subscribed
,
reference
,
pattern
)
char
*
tag
;
int
subscribed
;
char
*
reference
;
char
*
pattern
;
{
char
*
buf
=
NULL
;
int
patlen
=
0
;
int
reflen
=
0
;
static
int
ignorereference
=
0
;
/* Ignore the reference argument?
(the behavior in 1.5.10 & older) */
if
(
ignorereference
==
0
)
{
ignorereference
=
config_getswitch
(
"ignorereference"
,
0
);
}
/* Reset state in mstringdata */
mstringdata
(
NULL
,
NULL
,
0
,
0
);
if
(
!
pattern
[
0
]
&&
!
subscribed
)
{
/* Special case: query top-level hierarchy separator */
prot_printf
(
imapd_out
,
"* LIST (
\\
Noselect)
\"
.
\"
\"\"\r\n
"
);
}
else
{
/* Do we need to concatenate fields? */
if
(
!
ignorereference
||
pattern
[
0
]
==
'.'
)
{
/* Either
* - name begins with dot
* - we're configured to honor the reference argument */
/* Allocate a buffer, figure out how to stick the arguments
together, do it, then do that instead of using pattern. */
patlen
=
strlen
(
pattern
);
reflen
=
strlen
(
reference
);
buf
=
xmalloc
(
patlen
+
reflen
+
1
);
buf
[
0
]
=
'\0'
;
if
(
*
reference
)
{
/* check for LIST A. .B, change to LIST "" A.B */
if
(
reference
[
reflen
-1
]
==
'.'
&&
pattern
[
0
]
==
'.'
)
{
reference
[
--
reflen
]
=
'\0'
;
}
strcpy
(
buf
,
reference
);
}
strcat
(
buf
,
pattern
);
pattern
=
buf
;
}
if
(
subscribed
)
{
mboxlist_findsub
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
lsubdata
,
NULL
,
0
);
lsubdata
((
char
*
)
0
,
0
,
0
,
0
);
}
else
{
mboxlist_findall
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
listdata
,
NULL
);
listdata
((
char
*
)
0
,
0
,
0
,
0
);
}
if
(
buf
)
free
(
buf
);
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a SUBSCRIBE (add is nonzero) or
* UNSUBSCRIBE (add is zero) command
*/
void
cmd_changesub
(
tag
,
namespace
,
name
,
add
)
char
*
tag
;
char
*
namespace
;
char
*
name
;
int
add
;
{
int
r
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
if
(
namespace
)
lcase
(
namespace
);
if
(
!
namespace
||
!
strcmp
(
namespace
,
"mailbox"
))
{
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_changesub
(
mailboxname
,
imapd_userid
,
imapd_authstate
,
add
,
0
);
}
}
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: %s
\r\n
"
,
tag
,
add
?
"Subscribe"
:
"Unsubscribe"
,
error_message
(
r
));
}
else
{
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
}
/*
* Perform a GETACL command
*/
void
cmd_getacl
(
tag
,
name
,
oldform
)
char
*
tag
;
char
*
name
;
int
oldform
;
{
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
int
r
,
access
;
char
*
acl
;
char
*
rights
,
*
nextid
;
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_lookup
(
mailboxname
,
(
char
**
)
0
,
&
acl
,
NULL
);
}
if
(
!
r
)
{
access
=
acl_myrights
(
imapd_authstate
,
acl
);
if
(
!
(
access
&
(
ACL_READ
|
ACL_ADMIN
))
&&
!
imapd_userisadmin
&&
!
mboxname_userownsmailbox
(
imapd_userid
,
mailboxname
))
{
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
;
}
if
(
oldform
)
{
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
;
}
}
else
{
prot_printf
(
imapd_out
,
"* ACL "
);
printastring
(
name
);
while
(
acl
)
{
rights
=
strchr
(
acl
,
'\t'
);
if
(
!
rights
)
break
;
*
rights
++
=
'\0'
;
nextid
=
strchr
(
rights
,
'\t'
);
if
(
!
nextid
)
break
;
*
nextid
++
=
'\0'
;
prot_printf
(
imapd_out
,
" "
);
printastring
(
acl
);
prot_printf
(
imapd_out
,
" "
);
printastring
(
rights
);
acl
=
nextid
;
}
prot_printf
(
imapd_out
,
"
\r\n
"
);
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a LISTRIGHTS command
*/
void
cmd_listrights
(
tag
,
name
,
identifier
)
char
*
tag
;
char
*
name
;
char
*
identifier
;
{
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
int
r
,
rights
;
char
*
canon_identifier
;
int
canonidlen
=
0
;
char
*
acl
;
char
*
rightsdesc
;
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_lookup
(
mailboxname
,
(
char
**
)
0
,
&
acl
,
NULL
);
}
if
(
!
r
)
{
rights
=
acl_myrights
(
imapd_authstate
,
acl
);
if
(
!
rights
&&
!
imapd_userisadmin
&&
!
mboxname_userownsmailbox
(
imapd_userid
,
mailboxname
))
{
r
=
IMAP_MAILBOX_NONEXISTENT
;
}
}
if
(
!
r
)
{
canon_identifier
=
auth_canonifyid
(
identifier
);
if
(
canon_identifier
)
canonidlen
=
strlen
(
canon_identifier
);
if
(
!
canon_identifier
)
{
rightsdesc
=
"
\"\"
"
;
}
else
if
(
!
strncmp
(
mailboxname
,
"user."
,
5
)
&&
!
strchr
(
canon_identifier
,
'.'
)
&&
!
strncmp
(
mailboxname
+
5
,
canon_identifier
,
canonidlen
)
&&
(
mailboxname
[
5
+
canonidlen
]
==
'\0'
||
mailboxname
[
5
+
canonidlen
]
==
'.'
))
{
rightsdesc
=
"lca r s w i p d 0 1 2 3 4 5 6 7 8 9"
;
}
else
{
rightsdesc
=
"
\"\"
l r s w i p c d a 0 1 2 3 4 5 6 7 8 9"
;
}
prot_printf
(
imapd_out
,
"* LISTRIGHTS "
);
printastring
(
name
);
prot_putc
(
' '
,
imapd_out
);
printastring
(
identifier
);
prot_printf
(
imapd_out
,
" %s"
,
rightsdesc
);
prot_printf
(
imapd_out
,
"
\r\n
%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
return
;
}
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
}
/*
* Perform a MYRIGHTS command
*/
void
cmd_myrights
(
tag
,
name
,
oldform
)
char
*
tag
;
char
*
name
;
int
oldform
;
{
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
int
r
,
rights
=
0
;
char
*
acl
;
char
str
[
ACL_MAXSTR
];
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_lookup
(
mailboxname
,
(
char
**
)
0
,
&
acl
,
NULL
);
}
if
(
!
r
)
{
rights
=
acl_myrights
(
imapd_authstate
,
acl
);
/* Add in implicit rights */
if
(
imapd_userisadmin
||
mboxname_userownsmailbox
(
imapd_userid
,
mailboxname
))
{
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 "
);
if
(
oldform
)
prot_printf
(
imapd_out
,
"MAILBOX "
);
printastring
(
name
);
prot_printf
(
imapd_out
,
" "
);
printastring
(
acl_masktostr
(
rights
,
str
));
prot_printf
(
imapd_out
,
"
\r\n
%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a SETACL command
*/
void
cmd_setacl
(
tag
,
name
,
identifier
,
rights
)
char
*
tag
;
char
*
name
;
char
*
identifier
;
char
*
rights
;
{
int
r
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mboxlist_setacl
(
mailboxname
,
identifier
,
rights
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a GETQUOTA command
*/
void
cmd_getquota
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
r
;
struct
quota
quota
;
char
buf
[
MAX_MAILBOX_PATH
];
quota
.
root
=
name
;
quota
.
fd
=
-1
;
if
(
!
imapd_userisadmin
)
r
=
IMAP_PERMISSION_DENIED
;
else
{
mailbox_hash_quota
(
buf
,
quota
.
root
);
quota
.
fd
=
open
(
buf
,
O_RDWR
,
0
);
if
(
quota
.
fd
==
-1
)
{
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
.
fd
!=
-1
)
{
close
(
quota
.
fd
);
}
if
(
r
)
{
prot_printf
(
imapd_out
,
"%s NO %s
\r\n
"
,
tag
,
error_message
(
r
));
return
;
}
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Perform a GETQUOTAROOT command
*/
void
cmd_getquotaroot
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
struct
mailbox
mailbox
;
int
r
;
int
doclose
=
0
;
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mailbox_open_header
(
mailboxname
,
imapd_authstate
,
&
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
/*
* Parse and perform a SETQUOTA command
* The command has been parsed up to the resource list
*/
void
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
((
int
)
*
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
(
c
);
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
return
;
badlist
:
prot_printf
(
imapd_out
,
"%s BAD Invalid quota list in Setquota
\r\n
"
,
tag
);
eatline
(
c
);
}
#ifdef HAVE_SSL
/*
* this implements the STARTTLS command, as described in RFC 2595.
* one caveat: it assumes that no external layer is currently present.
* if a client executes this command, information about the external
* layer that was passed on the command line is disgarded. this should
* be fixed.
*/
int
starttls_enabled
(
void
)
{
if
(
config_getstring
(
"tls_cert_file"
,
NULL
)
==
NULL
)
return
0
;
if
(
config_getstring
(
"tls_key_file"
,
NULL
)
==
NULL
)
return
0
;
return
1
;
}
/* imaps - weather this is an imaps transaction or not */
void
cmd_starttls
(
char
*
tag
,
int
imaps
)
{
int
result
;
int
*
layerp
;
sasl_external_properties_t
external
;
/* SASL and openssl have different ideas about whether ssf is signed */
layerp
=
(
int
*
)
&
(
external
.
ssf
);
if
(
imapd_starttls_done
==
1
)
{
prot_printf
(
imapd_out
,
"%s NO TLS already active
\r\n
"
,
tag
);
return
;
}
result
=
tls_init_serverengine
(
5
,
/* depth to verify */
!
imaps
,
/* can client auth? */
0
,
/* require client to auth? */
(
char
*
)
config_getstring
(
"tls_ca_file"
,
""
),
(
char
*
)
config_getstring
(
"tls_ca_path"
,
""
),
(
char
*
)
config_getstring
(
"tls_cert_file"
,
""
),
(
char
*
)
config_getstring
(
"tls_key_file"
,
""
));
if
(
result
==
-1
)
{
syslog
(
LOG_ERR
,
"error initializing TLS: "
"[CA_file: %s] [CA_path: %s] [cert_file: %s] [key_file: %s]"
,
(
char
*
)
config_getstring
(
"tls_ca_file"
,
""
),
(
char
*
)
config_getstring
(
"tls_ca_path"
,
""
),
(
char
*
)
config_getstring
(
"tls_cert_file"
,
""
),
(
char
*
)
config_getstring
(
"tls_key_file"
,
""
));
if
(
imaps
==
0
)
{
prot_printf
(
imapd_out
,
"%s NO Error initializing TLS
\r\n
"
,
tag
);
}
else
{
fatal
(
"tls_init() failed"
,
EC_CONFIG
);
}
return
;
}
if
(
imaps
==
0
)
{
prot_printf
(
imapd_out
,
"%s OK Begin TLS negotiation now
\r\n
"
,
tag
);
/* must flush our buffers before starting tls */
prot_flush
(
imapd_out
);
}
result
=
tls_start_servertls
(
0
,
/* read */
1
,
/* write */
layerp
,
&
(
external
.
auth_id
));
/* if error */
if
(
result
==
-1
)
{
if
(
imaps
==
0
)
{
prot_printf
(
imapd_out
,
"%s NO Starttls failed
\r\n
"
,
tag
);
syslog
(
LOG_NOTICE
,
"STARTTLS failed: %s"
,
imapd_clienthost
);
return
;
}
else
{
syslog
(
LOG_NOTICE
,
"imaps failed: %s"
,
imapd_clienthost
);
fatal
(
"tls_start_servertls() failed"
,
EC_TEMPFAIL
);
return
;
}
}
/* tell SASL about the negotiated layer */
result
=
sasl_setprop
(
imapd_saslconn
,
SASL_SSF_EXTERNAL
,
&
external
);
if
(
result
!=
SASL_OK
)
{
fatal
(
"sasl_setprop() failed: cmd_starttls()"
,
EC_TEMPFAIL
);
}
/* if authenticated set that */
if
(
external
.
auth_id
!=
NULL
)
{
imapd_userid
=
external
.
auth_id
;
}
/* tell the prot layer about our new layers */
prot_settls
(
imapd_in
,
tls_conn
);
prot_settls
(
imapd_out
,
tls_conn
);
imapd_starttls_done
=
1
;
}
#else
int
starttls_enabled
(
void
)
{
return
0
;
}
void
cmd_starttls
(
char
*
tag
,
int
imaps
)
{
fatal
(
"cmd_starttls() executed, but starttls isn't implemented!"
,
EC_SOFTWARE
);
}
#endif
/* HAVE_SSL */
/*
* Parse and perform a STATUS command
* The command has been parsed up to the attribute list
*/
void
cmd_status
(
tag
,
name
)
char
*
tag
;
char
*
name
;
{
int
c
;
int
statusitems
=
0
;
static
struct
buf
arg
;
struct
mailbox
mailbox
;
char
mailboxname
[
MAX_MAILBOX_NAME
+
1
];
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
,
"uidnext"
))
{
statusitems
|=
STATUS_UIDNEXT
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"uidvalidity"
))
{
statusitems
|=
STATUS_UIDVALIDITY
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"unseen"
))
{
statusitems
|=
STATUS_UNSEEN
;
}
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Status attribute %s
\r\n
"
,
tag
,
arg
.
s
);
eatline
(
c
);
return
;
}
if
(
c
==
' '
)
c
=
getword
(
&
arg
);
else
break
;
}
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing close parenthesis in Status
\r\n
"
,
tag
);
eatline
(
c
);
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
(
c
);
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
);
}
r
=
mboxname_tointernal
(
name
,
imapd_userid
,
mailboxname
);
if
(
!
r
)
{
r
=
mailbox_open_header
(
mailboxname
,
imapd_authstate
,
&
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 %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
return
;
badlist
:
prot_printf
(
imapd_out
,
"%s BAD Invalid status list in Status
\r\n
"
,
tag
);
eatline
(
c
);
}
#ifdef ENABLE_X_NETSCAPE_HACK
/*
* Reply to Netscape's crock with a crock of my own
*/
void
cmd_netscrape
(
tag
)
char
*
tag
;
{
const
char
*
url
;
/* so tempting, and yet ... */
/* url = "http://random.yahoo.com/ryl/"; */
url
=
config_getstring
(
"netscapeurl"
,
"http://andrew2.andrew.cmu.edu/cyrus/imapd/netscape-admin.html"
);
/* I only know of three things to reply with: */
prot_printf
(
imapd_out
,
"* OK [NETSCAPE] Carnegie Mellon Cyrus IMAP
\r\n
* VERSION %s
\r\n
"
,
CYRUS_VERSION
);
prot_printf
(
imapd_out
,
"* ACCOUNT-URL %s
\r\n
%s OK %s
\r\n
"
,
url
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
}
#endif
/* ENABLE_X_NETSCAPE_HACK */
/* Callback for cmd_namespace to be passed to mboxlist_findall.
* For each top-level mailbox found, print a bit of the response
* if it is a shared namespace. The rock is used as an integer in
* order to ensure the namespace response is correct on a server with
* no shared namespace.
*/
/* locations to set if the user can see a given namespace */
#define NAMESPACE_INBOX 0
#define NAMESPACE_USER 1
#define NAMESPACE_SHARED 2
static
int
namespacedata
(
name
,
matchlen
,
maycreate
,
rock
)
char
*
name
;
int
matchlen
;
int
maycreate
;
void
*
rock
;
{
int
*
sawone
=
(
int
*
)
rock
;
if
(
!
name
)
{
return
0
;
}
if
(
!
(
strncmp
(
name
,
"INBOX."
,
6
)))
{
/* The user has a "personal" namespace. */
sawone
[
NAMESPACE_INBOX
]
=
1
;
}
else
if
(
!
(
strncmp
(
name
,
"user."
,
5
)))
{
/* The user can see the "other users" namespace. */
sawone
[
NAMESPACE_USER
]
=
1
;
}
else
{
/* The user can see the "shared" namespace. */
sawone
[
NAMESPACE_SHARED
]
=
1
;
}
return
0
;
}
/*
* Print out a response to the NAMESPACE command defined by
* RFC 2342.
*/
void
cmd_namespace
(
tag
)
char
*
tag
;
{
int
sawone
[
3
]
=
{
0
,
0
,
0
};
char
*
pattern
=
xstrdup
(
"%"
);
/* now find all the exciting toplevel namespaces */
mboxlist_findall
(
pattern
,
imapd_userisadmin
,
imapd_userid
,
imapd_authstate
,
namespacedata
,
(
void
*
)
sawone
);
prot_printf
(
imapd_out
,
"* NAMESPACE %s %s %s
\r\n
"
,
(
sawone
[
NAMESPACE_INBOX
])
?
"((
\"
INBOX.
\"
\"
.
\"
))"
:
"NIL"
,
(
sawone
[
NAMESPACE_USER
])
?
"((
\"
user.
\"
\"
.
\"
))"
:
"NIL"
,
(
sawone
[
NAMESPACE_SHARED
])
?
"((
\"\"
\"
.
\"
))"
:
"NIL"
);
prot_printf
(
imapd_out
,
"%s OK %s
\r\n
"
,
tag
,
error_message
(
IMAP_OK_COMPLETED
));
free
(
pattern
);
}
/*
* Parse a word
* (token not containing whitespace, parens, or double quotes)
*/
#define BUFGROWSIZE 100
int
getword
(
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 xstring
* (astring, nstring or string based on type)
*/
int
getxstring
(
struct
buf
*
buf
,
int
type
)
{
int
c
;
int
i
,
len
=
0
;
int
sawdigit
=
0
;
int
isnowait
;
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
:
switch
(
type
)
{
case
IMAP_ASTRING
:
/* atom, quoted-string or literal */
/*
* 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
);
}
break
;
case
IMAP_NSTRING
:
/* "NIL", quoted-string or literal */
/*
* Look for "NIL"
*/
if
(
c
==
'N'
)
{
prot_ungetc
(
c
,
imapd_in
);
c
=
getword
(
buf
);
if
(
!
strcmp
(
buf
->
s
,
"NIL"
))
return
c
;
}
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
break
;
case
IMAP_STRING
:
/* quoted-string or literal */
/*
* Nothing to do here - fall through.
*/
break
;
}
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 */
isnowait
=
0
;
buf
->
s
[
0
]
=
'\0'
;
while
((
c
=
prot_getc
(
imapd_in
))
!=
EOF
&&
isdigit
(
c
))
{
sawdigit
=
1
;
len
=
len
*
10
+
c
-
'0'
;
}
if
(
c
==
'+'
)
{
isnowait
++
;
c
=
prot_getc
(
imapd_in
);
}
if
(
!
sawdigit
||
c
!=
'}'
)
{
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'\r'
)
{
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
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
);
}
if
(
!
isnowait
)
{
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
);
}
}
/*
* 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
i
,
c
,
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
(
imparse_issequence
(
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
;
lcase
(
arg
.
s
);
if
(
!
(
searchargs
->
flags
&
SEARCH_UNCACHEDHEADER
))
{
for
(
i
=
0
;
i
<
mailbox_num_cache_header
;
i
++
)
{
if
(
!
strcmp
(
mailbox_cache_header_name
[
i
],
arg
.
s
))
break
;
}
if
(
i
==
mailbox_num_cache_header
)
{
searchargs
->
flags
|=
SEARCH_UNCACHEDHEADER
;
}
}
appendstrlist
(
&
searchargs
->
header_name
,
arg
.
s
);
c
=
getastring
(
&
arg
);
if
(
c
==
EOF
)
goto
missingarg
;
str
=
charset_convert
(
arg
.
s
,
*
charset
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
else
{
appendstrlistpat
(
&
searchargs
->
header
,
str
);
}
}
else
goto
badcri
;
break
;
case
'k'
:
if
(
!
strcmp
(
criteria
.
s
,
"keyword"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
if
(
!
imparse_isatom
(
arg
.
s
))
goto
badflag
;
lcase
(
arg
.
s
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
imapd_mailbox
->
flagname
[
flag
]
&&
!
strcasecmp
(
imapd_mailbox
->
flagname
[
flag
],
arg
.
s
))
break
;
}
if
(
flag
==
MAX_USER_FLAGS
)
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
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
((
int
)
*
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
;
}
appendsearchargs
(
searchargs
,
sub1
,
(
struct
searchargs
*
)
0
);
}
else
if
(
!
strcmp
(
criteria
.
s
,
"new"
))
{
searchargs
->
flags
|=
(
SEARCH_SEEN_UNSET
|
SEARCH_RECENT_SET
);
}
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
->
flags
|=
SEARCH_RECENT_UNSET
;
}
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
->
flags
|=
SEARCH_RECENT_SET
;
}
else
goto
badcri
;
break
;
case
's'
:
if
(
!
strcmp
(
criteria
.
s
,
"seen"
))
{
searchargs
->
flags
|=
SEARCH_SEEN_SET
;
}
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
((
int
)
*
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
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
,
NULL
,
0
);
if
(
strchr
(
str
,
EMPTY
))
{
/* Force failure */
searchargs
->
flags
=
(
SEARCH_RECENT_SET
|
SEARCH_RECENT_UNSET
);
}
else
{
appendstrlistpat
(
&
searchargs
->
text
,
str
);
}
}
else
goto
badcri
;
break
;
case
'u'
:
if
(
!
strcmp
(
criteria
.
s
,
"uid"
))
{
if
(
c
!=
' '
)
goto
missingarg
;
c
=
getword
(
&
arg
);
if
(
!
imparse_issequence
(
arg
.
s
))
goto
badcri
;
appendstrlist
(
&
searchargs
->
uidsequence
,
arg
.
s
);
}
else
if
(
!
strcmp
(
criteria
.
s
,
"unseen"
))
{
searchargs
->
flags
|=
SEARCH_SEEN_UNSET
;
}
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
(
!
imparse_isatom
(
arg
.
s
))
goto
badflag
;
lcase
(
arg
.
s
);
for
(
flag
=
0
;
flag
<
MAX_USER_FLAGS
;
flag
++
)
{
if
(
imapd_mailbox
->
flagname
[
flag
]
&&
!
strcasecmp
(
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
;
tm
.
tm_isdst
=
-1
;
*
end
=
mktime
(
&
tm
);
return
c
;
baddate
:
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
#define SORTGROWSIZE 10
/*
* Parse a sort criteria
*/
int
getsortcriteria
(
char
*
tag
,
struct
sortcrit
**
sortcrit
)
{
int
c
;
static
struct
buf
arg
;
int
nsort
,
n
;
*
sortcrit
=
NULL
;
c
=
prot_getc
(
imapd_in
);
if
(
c
!=
'('
)
goto
missingcrit
;
c
=
getword
(
&
arg
);
if
(
arg
.
s
[
0
]
==
'\0'
)
goto
missingcrit
;
*
sortcrit
=
NULL
;
nsort
=
0
;
n
=
0
;
for
(;;)
{
if
(
n
==
nsort
)
{
/* (Re)allocate an array for sort criteria */
nsort
+=
SORTGROWSIZE
;
*
sortcrit
=
(
struct
sortcrit
*
)
xrealloc
(
*
sortcrit
,
nsort
*
sizeof
(
struct
sortcrit
));
/* Zero out the newly added sortcrit */
memset
((
*
sortcrit
)
+
n
,
0
,
SORTGROWSIZE
*
sizeof
(
struct
sortcrit
));
}
lcase
(
arg
.
s
);
if
(
!
strcmp
(
arg
.
s
,
"reverse"
))
{
(
*
sortcrit
)[
n
].
key
=
SORT_REVERSE
;
goto
nextcrit
;
}
else
if
(
!
strcmp
(
arg
.
s
,
"arrival"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_ARRIVAL
;
else
if
(
!
strcmp
(
arg
.
s
,
"cc"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_CC
;
else
if
(
!
strcmp
(
arg
.
s
,
"date"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_DATE
;
else
if
(
!
strcmp
(
arg
.
s
,
"from"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_FROM
;
else
if
(
!
strcmp
(
arg
.
s
,
"size"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_SIZE
;
else
if
(
!
strcmp
(
arg
.
s
,
"subject"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_SUBJECT
;
else
if
(
!
strcmp
(
arg
.
s
,
"to"
))
(
*
sortcrit
)[
n
].
key
|=
SORT_TO
;
else
{
prot_printf
(
imapd_out
,
"%s BAD Invalid Sort criterion %s
\r\n
"
,
tag
,
arg
.
s
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
n
++
;
nextcrit
:
if
(
c
==
' '
)
c
=
getword
(
&
arg
);
else
break
;
}
if
((
*
sortcrit
)[
n
].
key
==
SORT_REVERSE
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing Sort criterion to reverse
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
if
(
c
!=
')'
)
{
prot_printf
(
imapd_out
,
"%s BAD Missing close parenthesis in Sort
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
/* Terminate the list with the implicit sort criterion */
(
*
sortcrit
)[
n
++
].
key
=
SORT_SEQUENCE
;
c
=
prot_getc
(
imapd_in
);
return
c
;
missingcrit
:
prot_printf
(
imapd_out
,
"%s BAD Missing Sort criteria
\r\n
"
,
tag
);
if
(
c
!=
EOF
)
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
;
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
=
mkgmtime
(
&
tm
)
-
zone_off
*
60
;
return
c
;
baddate
:
prot_ungetc
(
c
,
imapd_in
);
return
EOF
;
}
/*
* Eat characters up to and including the next newline
* Also look for and eat non-synchronizing literals.
*/
void
eatline
(
int
c
)
{
int
state
=
0
;
char
*
statediagram
=
" {+}
\r
"
;
int
size
=
-1
;
for
(;;)
{
if
(
c
==
'\n'
)
return
;
if
(
c
==
statediagram
[
state
+
1
])
{
state
++
;
if
(
state
==
1
)
size
=
0
;
else
if
(
c
==
'\r'
)
{
/* Got a non-synchronizing literal */
c
=
prot_getc
(
imapd_in
);
/* Eat newline */
while
(
size
--
)
{
c
=
prot_getc
(
imapd_in
);
/* Eat contents */
}
state
=
0
;
/* Go back to scanning for eol */
}
}
else
if
(
state
==
1
&&
isdigit
(
c
))
{
size
=
size
*
10
+
c
-
'0'
;
}
else
state
=
0
;
c
=
prot_getc
(
imapd_in
);
if
(
c
==
EOF
)
return
;
}
}
/*
* Print 's' as a quoted-string or literal (but not an atom)
*/
void
printstring
(
s
)
const
char
*
s
;
{
const
char
*
p
;
int
len
=
0
;
/* Look for any non-QCHAR characters */
for
(
p
=
s
;
*
p
&&
len
<
1024
;
p
++
)
{
len
++
;
if
(
*
p
&
0x80
||
*
p
==
'\r'
||
*
p
==
'\n'
||
*
p
==
'\"'
||
*
p
==
'%'
||
*
p
==
'\\'
)
break
;
}
/* if it's too long, literal it */
if
(
*
p
||
len
>=
1024
)
{
prot_printf
(
imapd_out
,
"{%u}
\r\n
%s"
,
strlen
(
s
),
s
);
}
else
{
prot_printf
(
imapd_out
,
"
\"
%s
\"
"
,
s
);
}
}
/*
* Print 's' as an atom, quoted-string, or literal
*/
void
printastring
(
s
)
const
char
*
s
;
{
const
char
*
p
;
int
len
=
0
;
if
(
imparse_isatom
(
s
))
{
prot_printf
(
imapd_out
,
"%s"
,
s
);
return
;
}
/* Look for any non-QCHAR characters */
for
(
p
=
s
;
*
p
&&
len
<
1024
;
p
++
)
{
len
++
;
if
(
*
p
&
0x80
||
*
p
==
'\r'
||
*
p
==
'\n'
||
*
p
==
'\"'
||
*
p
==
'%'
||
*
p
==
'\\'
)
break
;
}
/* if it's too long, literal it */
if
(
*
p
||
len
>=
1024
)
{
prot_printf
(
imapd_out
,
"{%u}
\r\n
%s"
,
strlen
(
s
),
s
);
}
else
{
prot_printf
(
imapd_out
,
"
\"
%s
\"
"
,
s
);
}
}
/*
* Append 'section', 'fields', 'trail' to the fieldlist 'l'.
*/
void
appendfieldlist
(
l
,
section
,
fields
,
trail
)
struct
fieldlist
**
l
;
char
*
section
;
struct
strlist
*
fields
;
char
*
trail
;
{
struct
fieldlist
**
tail
=
l
;
while
(
*
tail
)
tail
=
&
(
*
tail
)
->
next
;
*
tail
=
(
struct
fieldlist
*
)
xmalloc
(
sizeof
(
struct
fieldlist
));
(
*
tail
)
->
section
=
xstrdup
(
section
);
(
*
tail
)
->
fields
=
fields
;
(
*
tail
)
->
trail
=
xstrdup
(
trail
);
(
*
tail
)
->
next
=
0
;
}
/*
* Append 's' to the strlist 'l'.
*/
void
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
=
xstrdup
(
s
);
(
*
tail
)
->
p
=
0
;
(
*
tail
)
->
next
=
0
;
}
/*
* Append 's' to the strlist 'l', compiling it as a pattern.
* Caller must pass in memory that is freed when the strlist is freed.
*/
void
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
=
s
;
(
*
tail
)
->
p
=
charset_compilepat
(
s
);
(
*
tail
)
->
next
=
0
;
}
/*
* Free the fieldlist 'l'
*/
void
freefieldlist
(
l
)
struct
fieldlist
*
l
;
{
struct
fieldlist
*
n
;
while
(
l
)
{
n
=
l
->
next
;
free
(
l
->
section
);
freestrlist
(
l
->
fields
);
free
(
l
->
trail
);
free
((
char
*
)
l
);
l
=
n
;
}
}
/*
* Free the strlist 'l'
*/
void
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'
*/
void
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'
*/
void
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
);
}
/*
* Free an array of sortcrit
*/
static
void
freesortcrit
(
struct
sortcrit
*
s
)
{
int
i
=
0
;
if
(
!
s
)
return
;
do
{
if
(
s
[
i
].
args
[
0
])
free
(
s
[
i
].
args
[
0
]);
if
(
s
[
i
].
args
[
1
])
free
(
s
[
i
].
args
[
1
]);
i
++
;
}
while
(
s
[
i
].
key
!=
SORT_SEQUENCE
);
free
(
s
);
}
/*
* Issue a MAILBOX untagged response
*/
static
int
mailboxdata
(
name
,
matchlen
,
maycreate
,
rock
)
char
*
name
;
int
matchlen
;
int
maycreate
;
void
*
rock
;
{
prot_printf
(
imapd_out
,
"* MAILBOX %s
\r\n
"
,
name
);
return
0
;
}
/*
* Issue a LIST or LSUB untagged response
*/
static
void
mstringdata
(
char
*
cmd
,
char
*
name
,
int
matchlen
,
int
maycreate
)
{
static
char
lastname
[
MAX_MAILBOX_PATH
];
static
int
lastnamedelayed
;
static
int
sawuser
=
0
;
int
lastnamehassub
=
0
;
int
c
;
/* We have to reset the sawuser flag before each list command.
* Handle it as a dirty hack.
*/
if
(
cmd
==
NULL
)
{
sawuser
=
0
;
return
;
}
if
(
lastnamedelayed
)
{
if
(
name
&&
strncmp
(
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
;
}
/* Suppress any output of a partial match */
if
(
name
[
matchlen
]
&&
strncmp
(
lastname
,
name
,
matchlen
)
==
0
)
{
return
;
}
/*
* 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
;
sawuser
=
1
;
}
strcpy
(
lastname
,
name
);
lastname
[
matchlen
]
=
'\0'
;
if
(
!
name
[
matchlen
]
&&
!
maycreate
)
{
lastnamedelayed
=
1
;
return
;
}
c
=
name
[
matchlen
];
if
(
c
)
name
[
matchlen
]
=
'\0'
;
prot_printf
(
imapd_out
,
"* %s (%s)
\"
.
\"
"
,
cmd
,
c
?
"
\\
Noselect"
:
""
);
printstring
(
name
);
prot_printf
(
imapd_out
,
"
\r\n
"
);
if
(
c
)
name
[
matchlen
]
=
c
;
return
;
}
/*
* Issue a LIST untagged response
*/
static
int
listdata
(
name
,
matchlen
,
maycreate
,
rock
)
char
*
name
;
int
matchlen
;
int
maycreate
;
void
*
rock
;
{
mstringdata
(
"LIST"
,
name
,
matchlen
,
maycreate
);
return
0
;
}
/*
* Issue a LSUB untagged response
*/
static
int
lsubdata
(
name
,
matchlen
,
maycreate
,
rock
)
char
*
name
;
int
matchlen
;
int
maycreate
;
void
*
rock
;
{
mstringdata
(
"LSUB"
,
name
,
matchlen
,
maycreate
);
return
0
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Apr 6, 2:24 AM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832036
Default Alt Text
imapd.c (131 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline