/* user.c -- User manipulation routines
 * 
 * Copyright (c) 1998-2003 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: user.c,v 1.14 2003/04/23 18:07:48 rjs3 Exp $
 */

#include <config.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>

#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#include "imapconf.h"
#include "user.h"
#include "mboxlist.h"
#include "mailbox.h"
#include "util.h"
#include "seen.h"
#include "xmalloc.h"

static int user_deleteacl(char *name, int matchlen, int maycreate, void* rock)
{
#if 0
    /* deleting all references to the user is too slow right now */

    char *ident = (char *) rock;
    int r;
    char *acl;
    char *rights, *nextid;

    r = mboxlist_lookup(name, (char **)0, &acl, NULL);

    while (!r && acl) {
	rights = strchr(acl, '\t');
	if (!rights) break;
	*rights++ = '\0';

	nextid = strchr(rights, '\t');
	if (!nextid) break;
	*nextid++ = '\0';

	if (!strcmp(acl, ident)) {
	    /* delete ACL for ident */
	    if (!r) mboxlist_setacl(name, ident, (char *)0,
				    1, ident, NULL);
	}

	acl = nextid;
    }
#endif
    return 0;
}

int user_deletesieve(char *user) 
{
    char sieve_path[2048];
    char filename[2048];
    DIR *mbdir;
    struct dirent *next = NULL;
    
    /* oh well */
    if(config_getswitch("sieveusehomedir", 0)) return 0;
    
    snprintf(sieve_path, sizeof(sieve_path), "%s/%c/%s",
	     config_getstring("sievedir", "/usr/sieve"),
	     dir_hash_c(user), user);
    mbdir=opendir(sieve_path);

    if(mbdir) {
	while((next = readdir(mbdir)) != NULL) {
	    if(!strcmp(next->d_name, ".")
	       || !strcmp(next->d_name, "..")) continue;

	    snprintf(filename, sizeof(filename), "%s/%s",
		     sieve_path, next->d_name);

	    unlink(filename);
	}
	
	closedir(mbdir);

	/* remove mbdir */
	rmdir(sieve_path);
    }

    return 0;
}

int user_delete(char *user, char *userid, struct auth_state *authstate,
		int wipe_user)
{
    char *fname;
    char pat[] = "*";

    /* delete seen state */
    if(wipe_user)
	seen_delete_user(user);

    /* delete subscriptions */
    fname = mboxlist_hash_usersubs(user);
    (void) unlink(fname);
    free(fname);

    /* delete quotas */
    user_deletequotas(user);

    /* delete ACLs - we're using the internal names here */
    if(wipe_user)
	mboxlist_findall(NULL, pat, 1, userid, authstate, user_deleteacl,
			 user);

    /* delete sieve scripts */
    user_deletesieve(user);
    
    return 0;
}
#if 0
static int user_renameacl(char *name, int matchlen, int maycreate, void* rock)
{
    char **ident = (char **) rock;
    int r;
    char *acl;
    char *rights, *nextid;

    r = mboxlist_lookup(name, (char **)0, &acl, NULL);

    while (!r && acl) {
	rights = strchr(acl, '\t');
	if (!rights) break;
	*rights++ = '\0';

	nextid = strchr(rights, '\t');
	if (!nextid) break;
	*nextid++ = '\0';

	if (!strcmp(acl, ident[0])) {
	    /* copy ACL for old ident to new ident */
	    r = mboxlist_setacl(name, ident[1], rights, 1, ident[1], NULL);
	    /* delete ACL for old ident */
	    if (!r) mboxlist_setacl(name, ident[0], (char *)0,
				    1, ident[1], NULL);
	}

	acl = nextid;
    }
    return 0;
}

static int user_renamesub(char *name, int matchlen, int maycreate, void* rock)
{
    char **ident = (char **) rock;
    char newname[MAX_MAILBOX_NAME+1];

    /* unsubscribe from old folder */
    mboxlist_changesub(name, ident[1], NULL, 0, 1);

    /* subscribe to new folder */
    snprintf(newname, sizeof(newname),
	     "user.%s.%s", ident[1], name+6+strlen(ident[0]));
    mboxlist_changesub(newname, ident[1], NULL, 1, 1);

    return 0;
}

int user_rename(char *oldmailboxname, char *newmailboxname,
		char *userid, struct auth_state *authstate)
{
    char *ident[] = { oldmailboxname+5, newmailboxname+5 };
    char pat[MAX_MAILBOX_NAME+1];
    char *oldfname, *newfname;
    int r = 0;

    /* change ACLs - we're using the internal names here */
    strcpy(pat, "*");
    mboxlist_findall(NULL, pat, 1, userid, authstate, user_renameacl, ident);

    /* rename/change subscriptions */
    oldfname = mboxlist_hash_usersubs(ident[0]);
    newfname = mboxlist_hash_usersubs(ident[1]);
    unlink(newfname);
    r = mailbox_copyfile(oldfname, newfname);
    if (!r) {
	unlink(oldfname);
	snprintf(pat, sizeof(pat), "%s.*", oldmailboxname);
	/* we're using internal names here */
	mboxlist_findsub(NULL, pat, 1, ident[1], authstate, user_renamesub,
			 ident, 1);
    }
    free(oldfname);
    free(newfname);

    /* rename seendb */
    seen_rename_user(ident[0], ident[1]);

    /* set quota on INBOX */
    user_copyquota(oldmailboxname, newmailboxname);
}

int user_copyquota(char *oldname, char *newname)
{
    int r;
    struct quota quota;
    char buf[MAX_MAILBOX_PATH];

    quota.root = oldname;
    quota.fd = -1;

    mailbox_hash_quota(buf, quota.root);
    quota.fd = open(buf, O_RDWR, 0);
    if (quota.fd > 0) {
	r = mailbox_read_quota(&quota);
	if (!r) mboxlist_setquota(newname, quota.limit, 0);
    }
}
#endif
int user_deletequotas(const char *user)
{
    char c, qpath[MAX_MAILBOX_NAME], *tail;
    size_t qp_len;
    DIR *dirp;
    struct dirent *f;

    /* this violates the quota abstraction layer; oh well */
    c = dir_hash_c(user);
    snprintf(qpath, sizeof(qpath), "%s%s%c/", config_dir, FNAME_QUOTADIR, c);
    qp_len = strlen(qpath);
    tail = qpath + qp_len;

    dirp = opendir(qpath);
    if (dirp) {
	while ((f = readdir(dirp)) != NULL) {
	    if (!strncmp(f->d_name, "user.", 5) &&
		!strncmp(f->d_name+5, user, strlen(user)) &&
		(f->d_name[5+strlen(user)] == '\0'||
		 f->d_name[5+strlen(user)] == '.')) {

		strlcpy(tail, f->d_name, sizeof(qpath) - qp_len);
		unlink(qpath);
	    }
	}
	closedir(dirp);
    }

    return 0;
}
