#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <pwd.h>
#include <sys/types.h>
#include <sasl/sasl.h>
#include "imclient.h"
#include "imparse.h"
#include "tcl.h"
#include "xmalloc.h"
struct admconn {
struct imclient *imclient;
int cmd_done;
int cmd_result;
Tcl_Interp *interp;
};
int Cyradm_CyradmCmd(), Cyradm_ConnCmd();
static void Cyradm_DeleteConn();
static int cmd_authenticate(), cmd_listmailbox();
static int cmd_deleteaclmailbox(), cmd_listaclmailbox(), cmd_setaclmailbox();
static int cmd_setquota(), cmd_listquota(), cmd_listquotaroot();
int Cyradm_Init(Tcl_Interp *interp)
{
Tcl_CreateCommand(interp, "cyradm", Cyradm_CyradmCmd,
(ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
return TCL_OK;
}
int Cyradm_CyradmCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *connection, *hostname, *port = 0;
struct admconn *newconn;
static struct admconn zeroconn;
int r;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " option [arg...]\"", (char *) NULL);
return TCL_ERROR;
}
if (strcmp(argv[1], "connect")) {
Tcl_AppendResult(interp, "bad option \"",
argv[1], "\", must be connect", (char *) NULL);
return TCL_ERROR;
}
if (argc < 3 || argc > 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " connect connection [hostname port]\"",
(char *) NULL);
return TCL_ERROR;
}
connection = argv[2];
if (argc < 4) {
hostname = connection;
}
else {
hostname = argv[3];
if (argc == 5) {
port = argv[4];
}
}
newconn = (struct admconn *)xmalloc(sizeof(struct admconn));
*newconn = zeroconn;
r = imclient_connect(&newconn->imclient, hostname, port);
if (r) {
if (r == -1) {
interp->result = "unknown host";
return TCL_ERROR;
}
if (r == -2) {
interp->result = "unknown service";
return TCL_ERROR;
}
errno = r;
Tcl_AppendResult(interp, "couldn't connect to ",
hostname, ": ", Tcl_PosixError(interp),
(char *) NULL);
return TCL_ERROR;
}
imclient_addcallback(newconn->imclient,
"OK", CALLBACK_NOLITERAL, (void (*)()) 0, (void *)0,
"NO", CALLBACK_NOLITERAL, (void (*)()) 0, (void *)0,
"BAD", CALLBACK_NOLITERAL, (void (*)()) 0, (void *)0,
"BYE", CALLBACK_NOLITERAL, (void (*)()) 0, (void *)0,
"LIST", 0, (void (*)()) 0, (void *)0,
"LSUB", 0, (void (*)()) 0, (void *)0,
"ACL", 0, (void (*)()) 0, (void *)0,
"QUOTA", 0, (void (*)()) 0, (void *)0,
"QUOTAROOT", 0, (void (*)()) 0, (void *)0,
(char *)0);
Tcl_CreateCommand(interp, connection, Cyradm_ConnCmd,
(ClientData) newconn,
(Tcl_CmdDeleteProc *) Cyradm_DeleteConn);
return TCL_OK;
}
static void
Cyradm_DeleteConn(clientData)
ClientData clientData;
{
struct admconn *conn = (struct admconn *)clientData;
imclient_close(conn->imclient);
free((char *) conn);
}
static void
callback_finish(imclient, rock, reply)
struct imclient *imclient;
void *rock;
struct imclient_reply *reply;
{
struct admconn *conn = (struct admconn *)rock;
conn->cmd_done++;
if (!strcmp(reply->keyword, "OK")) {
return;
}
conn->cmd_result = TCL_ERROR;
if (!strcmp(reply->keyword, "NO")) {
Tcl_ResetResult(conn->interp);
Tcl_AppendResult(conn->interp, "command failed: ",
reply->text, (char *) NULL);
}
else if (!strcmp(reply->keyword, "BAD")) {
Tcl_ResetResult(conn->interp);
Tcl_AppendResult(conn->interp, "server does not support operation: ",
reply->text, (char *) NULL);
}
else if (!strcmp(reply->keyword, "EOF")) {
Tcl_SetResult(conn->interp, "server connection closed",
TCL_STATIC);
}
else {
Tcl_SetResult(conn->interp, "unknown result error type",
TCL_STATIC);
}
}
int Cyradm_ConnCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
struct admconn *conn = (struct admconn *)clientData;
int numcmd = 1;
conn->cmd_done = 0;
conn->cmd_result = TCL_OK;
conn->interp = interp;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " option [arg...]\"", (char *) NULL);
return TCL_ERROR;
}
switch (argv[1][0]) {
case 'a':
if (!strcmp(argv[1], "authenticate")) {
return cmd_authenticate(conn, interp, argc, argv);
}
goto badoption;
case 'c':
if (!strcmp(argv[1], "createmailbox")) {
if (argc < 3 || argc > 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" createmailbox mailbox [partition]\"",
(char *) NULL);
return TCL_ERROR;
}
imclient_send(conn->imclient, callback_finish, (void *)conn,
"CREATE %s%a%a", argv[2], argv[3] ? " " : "",
argv[3] ? argv[3] : "");
break;
}
goto badoption;
case 'd':
if (!strcmp(argv[1], "deletemailbox")) {
if (argc < 3 || argc > 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" deletemailbox mailbox [host]\"",
(char *) NULL);
return TCL_ERROR;
}
if (argc == 4) {
interp->result = "host argument only supported in IMSP";
return TCL_ERROR;
}
imclient_send(conn->imclient, callback_finish, (void *)conn,
"DELETE %s%a%a", argv[2], argv[3] ? " " : "",
argv[3] ? argv[3] : "");
break;
}
if (!strcmp(argv[1], "deleteaclmailbox")) {
numcmd = cmd_deleteaclmailbox(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
goto badoption;
case 'l':
if (!strcmp(argv[1], "listaclmailbox")) {
numcmd = cmd_listaclmailbox(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
if (!strcmp(argv[1], "listmailbox")) {
numcmd = cmd_listmailbox(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
if (!strcmp(argv[1], "listquota")) {
numcmd = cmd_listquota(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
if (!strcmp(argv[1], "listquotaroot")) {
numcmd = cmd_listquotaroot(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
goto badoption;
case 'r':
if (!strcmp(argv[1], "renamemailbox")) {
if (argc < 4 || argc > 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" renamemailbox old new [partition]\"",
(char *) NULL);
return TCL_ERROR;
}
imclient_send(conn->imclient, callback_finish, (void *)conn,
"RENAME %s %s%a%a", argv[2], argv[3],
argv[4] ? " " : "", argv[4] ? argv[4] : "");
break;
}
goto badoption;
case 's':
if (!strcmp(argv[1], "servername")) {
if (argc != 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " servername\"",
(char *) NULL);
return TCL_ERROR;
}
Tcl_SetResult(conn->interp, imclient_servername(conn->imclient),
TCL_STATIC);
return TCL_OK;
}
if (!strcmp(argv[1], "setaclmailbox")) {
numcmd = cmd_setaclmailbox(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
if (!strcmp(argv[1], "setquota")) {
numcmd = cmd_setquota(conn, interp, argc, argv);
if (numcmd < 0) return TCL_ERROR;
break;
}
goto badoption;
badoption:
default:
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\", must be authenticate, createmailbox, ",
"deletemailbox, deleteaclmailbox, ",
"listaclmailbox, listmailbox, listquota, listquotaroot, ",
"renamemailbox, ",
"setaclmailbox, or setquota",
(char *) NULL);
return TCL_ERROR;
}
while (conn->cmd_done < numcmd) {
imclient_processoneevent(conn->imclient);
}
return conn->cmd_result;
}
typedef struct capabilies_s {
char *mechs;
int starttls;
int logindisabled;
} capabilities_t;
static capabilities_t *parsecapabilitylist(char *str)
{
char *tmp;
int num=0;
capabilities_t *ret=(capabilities_t *) xmalloc(sizeof(capabilities_t));
ret->mechs = (char *)xmalloc(strlen(str)+1);
ret->starttls=0;
ret->logindisabled=0;
if (strstr(str,"STARTTLS")!=NULL)
{
ret->starttls=1;
}
if (strstr(str,"LOGINDISABLED")!=NULL)
{
ret->logindisabled=1;
}
strcpy(ret->mechs,"");
while ((tmp=strstr(str,"AUTH="))!=NULL)
{
char *end=tmp+5;
tmp+=5;
while(((*end)!=' ') && ((*end)!='\0'))
end++;
(*end)='\0';
if (num>0)
strcat(ret->mechs," ");
strcat(ret->mechs, tmp);
num++;
str=end+1;
}
return ret;
}
static void callback_capability(struct imclient *imclient,
void *rock,
struct imclient_reply *reply)
{
char *s;
capabilities_t **caps = (capabilities_t **) rock;
s = reply->text;
*caps = parsecapabilitylist(s);
}
static int cmd_login(struct admconn *conn, char *userid, char *pass, int passlen, int tls_layer, int logindisabled)
{
if (logindisabled==1)
{
printf("Login Disabled. Aborting\n");
return -1;
}
if (pass==NULL)
{
printf("Password: ");
pass = getpass("");
passlen = strlen(pass);
}
imclient_send(conn->imclient, callback_finish, (void *)conn,
"LOGIN %s %s",
userid, passlen, pass);
conn->cmd_done = 0;
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
return conn->cmd_result;
}
static int
cmd_authenticate(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *pwcommand = 0;
char *user = 0;
char *p;
int r = 0;
int minssf=0;
int maxssf=10000;
char *mech = NULL;
char *tls_keyfile = "";
capabilities_t *capabilitylist;
int tls_layer = 0;
argv += 2;
while (argv[0]) {
if (!strcmp(argv[0], "-pwcommand")) {
if (!argv[1]) break;
pwcommand = *++argv;
}
else if (!strcmp(argv[0], "-user")) {
if (!argv[1]) break;
user = *++argv;
}
else if (!strcmp(argv[0], "-layers")) {
if (!argv[1]) break;
maxssf = atoi(*++argv);
}
else if (!strcmp(argv[0], "-mech")) {
if (!argv[1]) break;
mech = *++argv;
} else if (!strcmp(argv[0], "-tlskey")) {
if (!argv[1]) break;
tls_keyfile = *++argv;
} else if (!strcmp(argv[0], "-notls")) {
tls_keyfile = NULL;
}
argv++;
}
if (*argv) {
#ifdef HAVE_SSL
Tcl_AppendResult(interp, "incorrect args: should be \"",
argv[0], " authenticate ",
"[-pwcommand string] [-user user] ",
"[-layers #] [-mech mechname]\"",
(char *) NULL);
#else
Tcl_AppendResult(interp, "incorrect args: should be \"",
argv[0], " authenticate ",
"[-pwcommand string] [-user user] ",
"[-layers #] [-mech mechname] [-tlskey keyfile] [-notls]\"",
(char *) NULL);
#endif
return TCL_ERROR;
}
if (!user) {
user = xmalloc(sizeof(char) * 1024);
strcpy(user, getpwuid(getuid())->pw_name);
}
imclient_addcallback(conn->imclient, "CAPABILITY", 0,
callback_capability, (void *) &capabilitylist,
(char *) 0);
imclient_send(conn->imclient, callback_finish, (void *) conn,
"CAPABILITY");
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
#ifdef HAVE_SSL
if (capabilitylist->starttls == 1)
{
if (tls_keyfile!=NULL)
{
imclient_starttls(conn->imclient,
10,
tls_keyfile, tls_keyfile,
&tls_layer);
imclient_addcallback(conn->imclient, "CAPABILITY", 0,
callback_capability, (void *) &capabilitylist,
(char *) 0);
imclient_send(conn->imclient, callback_finish, (void *) conn,
"CAPABILITY");
conn->cmd_done = 0;
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
}
}
#endif
if (!pwcommand) {
r = imclient_authenticate(conn->imclient, mech ? mech : capabilitylist->mechs,
"imap", user, minssf, maxssf);
if (r == SASL_NOMECH) {
r = cmd_login(conn, user, NULL, 0, tls_layer, capabilitylist->logindisabled);
}
}
if (pwcommand) {
Tcl_DString command;
int comc;
char **comv;
Tcl_DStringInit(&command);
while ((p = strchr(pwcommand, '%'))!=NULL) {
Tcl_DStringAppend(&command, pwcommand, p - pwcommand);
switch (*++p) {
case '%':
Tcl_DStringAppend(&command, p, 1);
break;
case 'h':
Tcl_DStringAppendElement(&command,
imclient_servername(conn->imclient));
break;
case 'u':
if (!user) user = "";
Tcl_DStringAppendElement(&command, user);
break;
default:
Tcl_DStringFree(&command);
Tcl_AppendResult(interp, "invalid %-sequence in pwcommand",
(char *) NULL);
return TCL_ERROR;
}
pwcommand = p+1;
}
Tcl_DStringAppend(&command, pwcommand, -1);
r = Tcl_GlobalEval(interp, Tcl_DStringValue(&command));
Tcl_DStringFree(&command);
if (r) return r;
if (Tcl_SplitList(interp, interp->result, &comc, &comv)) {
return TCL_ERROR;
}
Tcl_ResetResult(interp);
if (comc != 2) {
Tcl_SetResult(interp,
"pwcommand script did not return a list with two elements",
TCL_STATIC);
return TCL_ERROR;
}
r = cmd_login(conn, comv[0], comv[1], strlen(comv[1]), tls_layer, capabilitylist->logindisabled);
}
if (r) {
interp->result = "authentication failed";
return TCL_ERROR;
}
return TCL_OK;
}
struct mailboxdata {
Tcl_DString data;
};
static void
callback_list(imclient, rock, reply)
struct imclient *imclient;
void *rock;
struct imclient_reply *reply;
{
struct mailboxdata *mailboxdata = (struct mailboxdata *)rock;
char *s, *end;
char *mailbox, *attributes, *separator = NULL;
int c;
s = reply->text;
if (*s++ != '(') return;
end = strchr(s, ')');
if (!end) return;
attributes = s;
s = end;
*s++ = '\0';
if (*s++ != ' ') return;
if (*s == 'N') {
if (s[1] != 'I' || s[2] != 'L') return;
separator = "";
s += 3;
}
else if (*s == '\"') {
s++;
if (*s == '\\') s++;
separator = s++;
if (*s != '\"') return;
*s++ = '\0';
}
if (*s++ != ' ') return;
c = imparse_astring(&s, &mailbox);
if (c != '\0') return;
Tcl_DStringStartSublist(&mailboxdata->data);
Tcl_DStringAppendElement(&mailboxdata->data, mailbox);
Tcl_DStringStartSublist(&mailboxdata->data);
for (s = attributes; (end = strchr(s, ' '))!=NULL ; s = end+1) {
*s = '\0';
Tcl_DStringAppendElement(&mailboxdata->data, s);
}
Tcl_DStringAppendElement(&mailboxdata->data, s);
Tcl_DStringEndSublist(&mailboxdata->data);
Tcl_DStringAppendElement(&mailboxdata->data, separator);
Tcl_DStringEndSublist(&mailboxdata->data);
}
static int
cmd_listmailbox(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *command = argv[0];
struct mailboxdata mailboxdata;
int subscribed = 0;
char *reference = "";
argc -= 2;
argv += 2;
while (argc > 0 && argv[0][0] == '-') {
argc--;
argv++;
if (!strcmp(argv[-1], "--")) break;
if (!strcmp(argv[-1], "-subscribed")) subscribed = 1;
else {
Tcl_AppendResult(interp, "unrecognized switch: should be \"",
command,
" listmailbox [-subscribed|--] pattern [reference]\"",
(char *) NULL);
return -1;
}
}
if (argc < 1 || argc > 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
command,
" listmailbox [-subscribed|--] pattern [reference]\"",
(char *) NULL);
return -1;
}
if (argc == 2) reference = argv[1];
Tcl_DStringInit(&mailboxdata.data);
imclient_addcallback(conn->imclient,
subscribed ? "LSUB" : "LIST", 0, callback_list,
(void *)&mailboxdata, (char *)0);
imclient_send(conn->imclient, callback_finish, (void *)conn,
"%a %s %s", subscribed ? "LSUB" : "LIST", reference,
argv[0]);
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
if (!conn->cmd_result) {
Tcl_DStringResult(interp, &mailboxdata.data);
}
imclient_addcallback(conn->imclient,
subscribed ? "LSUB" : "LIST", 0, (void (*)()) 0,
(void *)0, (char *)0);
return 0;
}
static int
cmd_deleteaclmailbox(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i, num;
char *mailbox;
if (argc < 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" deleteaclmailbox mailbox id [id]...\"",
(char *) NULL);
return -1;
}
mailbox = argv[2];
argv += 3;
num = argc - 3;
for (i = 0; i < num; i++) {
imclient_send(conn->imclient, callback_finish, (void *)conn,
"DELETEACL MAILBOX %s %s", mailbox, argv[i]);
}
return num;
}
static int
cmd_setaclmailbox(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i, num;
char *mailbox;
char *rights;
if (argc < 5 || !(argc & 1)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" setaclmailbox mailbox id rights [id rights]...\"",
(char *) NULL);
return -1;
}
mailbox = argv[2];
argv += 3;
num = (argc - 3)/2;
for (i = 0; i < num; i++) {
rights = argv[1];
if (!strcasecmp(rights, "none")) rights = "";
else if (!strcasecmp(rights, "read")) rights = "lrs";
else if (!strcasecmp(rights, "post")) rights = "lrsp";
else if (!strcasecmp(rights, "append")) rights = "lrsip";
else if (!strcasecmp(rights, "write")) rights = "lrswipcd";
else if (!strcasecmp(rights, "all")) rights = "lrswipcda";
imclient_send(conn->imclient, callback_finish, (void *)conn,
"SETACL %s %s %s", mailbox, argv[0], rights);
argv += 2;
}
return num;
}
struct acldata {
char *option;
char *object;
Tcl_DString data;
};
static void
callback_acl(imclient, rock, reply)
struct imclient *imclient;
void *rock;
struct imclient_reply *reply;
{
struct acldata *acldata = (struct acldata *)rock;
char *s;
char *val, *identifier, *rights;
int c;
s = reply->text;
c = imparse_astring(&s, &val);
if (strcasecmp(val, acldata->object) != 0) return;
if (c != '\0' && c != ' ') return;
while (c == ' ') {
c = imparse_astring(&s, &identifier);
if (c != ' ') return;
c = imparse_astring(&s, &rights);
if (c != '\0' && c != ' ') return;
Tcl_DStringAppendElement(&acldata->data, identifier);
Tcl_DStringAppendElement(&acldata->data, rights);
}
}
static int
cmd_listaclmailbox(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
struct acldata acldata;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " listaclmailbox mailbox\"", (char *) NULL);
return -1;
}
acldata.option = "MAILBOX";
acldata.object = argv[2];
Tcl_DStringInit(&acldata.data);
imclient_addcallback(conn->imclient,
"ACL", 0, callback_acl, (void *)&acldata,
(char *)0);
imclient_send(conn->imclient, callback_finish, (void *)conn,
"GETACL %s", argv[2]);
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
if (!conn->cmd_result) {
Tcl_DStringResult(interp, &acldata.data);
}
imclient_addcallback(conn->imclient,
"ACL", 0, (void (*)()) 0, (void *)0,
(char *)0);
return 0;
}
static int
cmd_setquota(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i, num;
char *mailbox;
if (argc < 3 || !(argc & 1)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0],
" setquota mailbox [limit num]...\"",
(char *) NULL);
return -1;
}
mailbox = argv[2];
argv += 3;
num = (argc - 3)/2;
for (i = 0; i < num; i++) {
if (!imparse_isatom(argv[2*i])) {
Tcl_AppendResult(interp, "invalid quota resource '",
argv[2*i], "'", (char *) NULL);
return -1;
}
if (!imparse_isnumber(argv[2*i+1])) {
Tcl_AppendResult(interp, "non-numeric quota value '",
argv[2*i+1], "'", (char *) NULL);
return -1;
}
}
imclient_send(conn->imclient, callback_finish, (void *)conn,
"SETQUOTA %s (%v)", mailbox, argv);
return 1;
}
struct quotadata {
char *mailbox;
Tcl_DString quotaroots;
Tcl_DString data;
};
static void
callback_quotaroot(imclient, rock, reply)
struct imclient *imclient;
void *rock;
struct imclient_reply *reply;
{
struct quotadata *quotadata = (struct quotadata *)rock;
char *s;
char *val;
int c;
s = reply->text;
c = imparse_astring(&s, &val);
if (c != ' ' || strcasecmp(val, quotadata->mailbox) != 0) return;
Tcl_DStringFree("adata->quotaroots);
Tcl_DStringAppend("adata->quotaroots, s, -1);
}
static void
callback_quota(imclient, rock, reply)
struct imclient *imclient;
void *rock;
struct imclient_reply *reply;
{
struct quotadata *quotadata = (struct quotadata *)rock;
char *s, *end;
char *root;
int c;
s = reply->text;
c = imparse_astring(&s, &root);
if (c != ' ') return;
if (*s++ != '(') return;
end = strchr(s, ')');
if (!end || end[1]) return;
*end = '\0';
Tcl_DStringAppendElement("adata->data, root);
Tcl_DStringAppendElement("adata->data, s);
}
static int
cmd_listquota(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i;
struct quotadata quotadata;
int quotac;
char **quotav;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " listquota root\"", (char *) NULL);
return -1;
}
Tcl_DStringInit("adata.data);
imclient_addcallback(conn->imclient,
"QUOTA", 0, callback_quota, (void *)"adata,
(char *)0);
imclient_send(conn->imclient, callback_finish, (void *)conn,
"GETQUOTA %s", argv[2]);
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
imclient_addcallback(conn->imclient,
"QUOTA", 0, (void (*)()) 0, (void *)0,
(char *)0);
if (!conn->cmd_result) {
if (Tcl_SplitList(interp, Tcl_DStringValue("adata.data),
"ac, "av)) {
Tcl_DStringFree("adata.data);
return -1;
}
for (i = 0; i < quotac; i += 2) {
if (!strcasecmp(argv[2], quotav[i])) {
Tcl_SetResult(interp, quotav[i+1], TCL_VOLATILE);
}
}
free((char *)quotav);
Tcl_DStringFree("adata.data);
}
return 0;
}
static int
cmd_listquotaroot(conn, interp, argc, argv)
struct admconn *conn;
Tcl_Interp *interp;
int argc;
char **argv;
{
int i, j;
struct quotadata quotadata;
int quotac, rootc;
char **quotav, **rootv;
Tcl_DString result;
char *quotaforroot;
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " listquotaroot mailbox\"", (char *) NULL);
return -1;
}
quotadata.mailbox = argv[2];
Tcl_DStringInit("adata.quotaroots);
Tcl_DStringInit("adata.data);
imclient_addcallback(conn->imclient,
"QUOTA", 0, callback_quota, (void *)"adata,
"QUOTAROOT", 0, callback_quotaroot, (void *)"adata,
(char *)0);
imclient_send(conn->imclient, callback_finish, (void *)conn,
"GETQUOTAROOT %s", argv[2]);
while (!conn->cmd_done) {
imclient_processoneevent(conn->imclient);
}
imclient_addcallback(conn->imclient,
"QUOTA", 0, (void (*)()) 0, (void *)0,
"QUOTAROOT", 0, (void (*)()) 0, (void *)0,
(char *)0);
if (!conn->cmd_result) {
if (Tcl_SplitList(interp, Tcl_DStringValue("adata.data),
"ac, "av) ||
Tcl_SplitList(interp, Tcl_DStringValue("adata.quotaroots),
&rootc, &rootv)) {
Tcl_DStringFree("adata.data);
Tcl_DStringFree("adata.quotaroots);
return -1;
}
Tcl_DStringInit(&result);
for (i = 0; i < rootc; i++) {
Tcl_DStringStartSublist(&result);
Tcl_DStringAppendElement(&result, rootv[i]);
quotaforroot = 0;
for (j = 0; j < quotac; j += 2) {
if (!strcasecmp(rootv[i], quotav[j])) {
quotaforroot = quotav[j+1];
}
}
if (*quotaforroot) {
Tcl_DStringAppend(&result, " ", 1);
Tcl_DStringAppend(&result, quotaforroot, -1);
}
Tcl_DStringEndSublist(&result);
}
Tcl_DStringFree("adata.data);
Tcl_DStringFree("adata.quotaroots);
free((char *)quotav);
free((char *)rootv);
Tcl_DStringResult(interp, &result);
}
return 0;
}