diff -Naur cyrus-imapd-2.1.16/imap/cleardel.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/cleardel.c --- cyrus-imapd-2.1.16/imap/cleardel.c 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/cleardel.c 2003-11-21 14:53:44.000000000 +0200 @@ -0,0 +1,880 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imapconf.h" +#include "xmalloc.h" + +#define FINISH_ALL_GFIDS 0 + +typedef struct groupFolderID { + char *id; /* the groupFolderID */ + char *name; /* case sensitive folder name */ + char *perm; /* type of perm */ + char *type_of_perm; /* local or recursive */ + char *dn; /* the DN */ + int can_be_deleted; /* 1 if the gfid can be deleted, 0 otherwise */ + struct groupFolderID *next; +} +GFID; + +char *gfid_attributes[5] = { + "groupFolderID", + "FolderName", + "permFlag", + "TypeOfpermFlag", + NULL +}; + +/* BASE DNs */ +const char *base_people; +const char *base_cyrus; + +char *role_attributes[2] = { "Role", NULL }; + +LDAP *ld; +GFID *fl = 0; + +int no_users = 0; /* Number of affected users */ +int no_gfids = 0; /* Number of deleted gfids */ +int user_size_limit; /* size limit on the users */ +int cont_on_sizelimit_exceeded; /* continue iteration if sizelimit exceeded */ +FILE *logfd; /* File descriptor for the log file */ + + +void fatal(const char *s, int code); +static void gracefully_exit(void); +static void immediately_exit(int); +static int ldap_start(void); +static int ldap_end(void); +static int get_ldap_gfid(void); +static void add_sorted_gfid(GFID * new_gfid); +static int cmpstr(const char *a, const char *b); +static int find_users(void); +static int get_ldap_role(char *filter); +static int check_del(char *val); +static int clear_role(char *dn, char **update_role); +static int myldap_delete_s(LDAP *ld, const char *dn ); +static int deletechildren(LDAP *ld, const char *dn ); +static int clear_gfid(void); +static int my_ldap_search(LDAP *ld, const char *base, int scope, char *filter, char **attrs, + int attrsonly, struct timeval *timeout, int sizelimit, LDAPMessage **res); + +FILE *cdlog_open(char *dirname); +static void cdlog_close(FILE *fd); +static void clear_gfid_list(void); + +/********************************* + * MAIN * + *********************************/ +int +main(int argc, char *argv[]){ + int r, retry=1; + extern char *optarg; + extern int optind; + char *dirname = NULL; + int opt; + + + + if(geteuid() == 0) + fatal("must run as the Cyrus user", 0); + + r = config_init(NULL, "cleardel"); + if(r) + fatal("cannot open imapd.conf", 0); + + + + base_people = config_getstring("ldap_base_people", ""); + base_cyrus = config_getstring("ldap_base_cyrus", ""); + user_size_limit = config_getint("user_size_limit", LDAP_NO_LIMIT); + cont_on_sizelimit_exceeded = config_getint("cont_on_sizelimit_exceeded", 0); + + syslog(LOG_NOTICE, "cleardel is starting"); + + while((opt = getopt(argc, argv, "d:"))!=-1) { + switch(opt) { + case 'd': + dirname = (char *) xmalloc(strlen(optarg)*sizeof(char)); + strncpy(dirname, optarg, strlen(optarg)); + *(dirname + strlen(optarg)) = '\0'; + break; + case '?': + default: + break; + } + } + + if((logfd = cdlog_open(dirname)) == NULL) + fatal("can't open log file", 0); + + signal(SIGINT, immediately_exit); + signal(SIGTERM, immediately_exit); + + r = ldap_start(); + if (r) + fatal("can't contact ldap", 0); + + do { + r = get_ldap_gfid(); + + if (r) + { + if(r==-1) + fatal("can't get groupFolderIDs", 0); + } + + while(retry) { + r = find_users(); + if(r == 0) + retry = 0; + else if(r == 1) + retry = cont_on_sizelimit_exceeded; + else + fatal("can't get users", 0); + } + + r = clear_gfid(); + + if (r) + fatal("can't clear groupFolderIDs", 0); + + clear_gfid_list(); + }while(FINISH_ALL_GFIDS); + + r = ldap_end(); + if(r) + fatal("can't close ldap connection", 0); + + + fprintf(logfd, "\n\ncleardel ended successfully\n"); + fprintf(logfd, "Number of deleted GroupFolderIDs:%d\n", no_gfids); + fprintf(logfd, "Number of affected users:%d\n", no_users); + cdlog_close(logfd); + + syslog(LOG_NOTICE, "cleardel ended successfully"); + syslog(LOG_NOTICE, "Number of deleted GroupFolderIDs:%d", no_gfids); + syslog(LOG_NOTICE, "Number of affected users:%d", no_users); + + return EXIT_SUCCESS; +} + + + +FILE *cdlog_open(char *dirname) +{ +/* + * Open a file for output logging. + * The file is of the format /cleardel-.log + * ie /var/log/cleardel-YYYY-MM-DD-hh-mm-ss.log + * Returns : NULL on error + * A file descriptor on success. + */ + const char *logdir; + char *filename; + char strdate[20]; + int len; + FILE *fd; + struct timeval tp; + struct tm *now; + struct flock fdlock; + + + + /* + * Add current date time as file suffix + * in the format of YYYY-MM-DD-hh-mm-ss + */ + gettimeofday(&tp, NULL); + now = localtime((time_t *) &(tp.tv_sec)); + + sprintf(strdate, "%0.4d-%0.2d-%0.2d-%0.2d-%0.2d-%0.2d", + now->tm_year + 1900, + now->tm_mon+1, + now->tm_mday, + now->tm_hour, + now->tm_min, + now->tm_sec); + strdate[19] = '\0'; + + + /* + * Create log filename. + */ + + logdir = dirname ? dirname : config_getstring("cleardel_logdir", NULL); + if(logdir && strcmp(logdir, "-")) { + + len = strlen(logdir) + strlen("/cleardel-") + strlen(strdate) + + strlen(".log") + 1; + + filename = (char *) xmalloc(len); + strlcpy(filename, logdir, len); + + /* + * If logdir in config file does not end with '/' + * then insert it. + */ + if(logdir[strlen(logdir) - 1] == '/') + strlcat(filename, "cleardel-", len); + else + strlcat(filename, "/cleardel-", len); + + + strlcat(filename, strdate, len); + if(strlcat(filename, ".log", len) > len) { + free(filename); + return NULL; + } + + if((fd = fopen(filename, "w+")) == NULL) { + free(filename); + syslog(LOG_ERR, "cdlog_open::fopen() failed %s", strerror(errno)); + return NULL; + } + + + free(filename); + + fdlock.l_type = F_WRLCK; + fdlock.l_start = 0; + fdlock.l_whence = SEEK_SET; + fdlock.l_len = 0; /* To EOF */ + + /* Lock the file for writting */ + if(fcntl(fileno(fd), F_SETLK, &fdlock) == -1) { + syslog(LOG_ERR, "cdlog_open::fcntl() could not obtain lock - %s", strerror(errno)); + return NULL; + } + return fd; + } + else + return stdout; + +} + + +void cdlog_close(FILE *fd) +{ +/* + * Unlock log file and close it. + */ + struct flock fdlock; + + fdlock.l_type = F_UNLCK; + fdlock.l_start = 0; + fdlock.l_whence = SEEK_SET; + fdlock.l_len = 0; /* To EOF */ + + /* Lock the file for writting */ + if(fcntl(fileno(fd), F_SETLK, &fdlock) == -1) { + syslog(LOG_ERR, "cdlog_open::fcntl() could not release lock - %s", strerror(errno)); + } + + fclose(fd); +} + + + +/********************************* + * Connect to ldap. Initialize ld * + * and bind * + *********************************/ + +int +ldap_start(void) { + const char *ldap_binddn, *ldap_passwd, *ldap_host; + int r = 0; + + ldap_binddn = config_getstring("ldap_write_bind_dn", ""); + ldap_passwd = config_getstring("ldap_write_passwd", ""); + ldap_host = config_getstring("ldap_host", ""); + + + /* get a handle to an LDAP con */ + if ((ld = ldap_init(ldap_host, LDAP_PORT)) == NULL) { + syslog(LOG_ERR, "ldap_start::ldap_init() failed"); + return -1; + } + + /* setup some options. The one that is important right now is LDAP_RESTART */ + if((r = ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "ldap_start::ldap_set_option() failed : %s", ldap_err2string(r)); + return -1; /* we consider this as a fatal option.. perhaps we shouldn't */ + } + + /* auth */ + if ((r = ldap_simple_bind_s(ld, ldap_binddn, ldap_passwd)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "ldap_start::ldap_simple_bind_s() failed : %s", ldap_err2string(r)); + return -1; + } + + return 0; +} + + + +int ldap_end(void) +{ + int r = 0; + + + if((r = ldap_unbind(ld)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "ldap_end::ldap_unbind() failed : %s", ldap_err2string(r)); + return -1; + } + + return 0; +} + + +/********************************** + * Retrieve from LDAP * + * GroupFolderIDs where * + * delFlag=TRUE * + **********************************/ +int +get_ldap_gfid(void) { + + GFID *new_gfid; + LDAPMessage *result, *entry; + BerElement *ber; + char *attribute; + char *dn; + char **value; + char *filter; + int r = 0; + filter = "delFlag=TRUE"; + + r = my_ldap_search(ld, + base_cyrus, + LDAP_SCOPE_SUBTREE, + filter, + gfid_attributes, + 0, + NULL, + LDAP_NO_LIMIT, + &result); + if( r != -1) { + + for (entry = ldap_first_entry(ld, result); entry != NULL; + entry = ldap_next_entry(ld, entry)) { + new_gfid = (GFID *) xmalloc(sizeof (GFID)); + new_gfid->id = NULL; + new_gfid->name = NULL; + new_gfid->perm = NULL; + new_gfid->type_of_perm = NULL; + new_gfid->dn = NULL; + new_gfid->next = NULL; + new_gfid->can_be_deleted = 0; + + if ((dn = ldap_get_dn(ld, entry)) != NULL) { + new_gfid->dn = xstrdup(dn); + } + else { + syslog(LOG_ERR, "get_ldap_gfid::ldap_get_dn() failed"); + return -1; + } + + for (attribute = ldap_first_attribute(ld, entry, &ber); + attribute != NULL; + attribute = ldap_next_attribute(ld, entry, ber)) { + if ((value = ldap_get_values(ld, entry, attribute)) != NULL) { + + while (1) { + if (!strcasecmp(attribute, gfid_attributes[0])) { + new_gfid->id = xstrdup(value[0]); + break; + } + if (!strcasecmp(attribute, gfid_attributes[1])) { + new_gfid->name = xstrdup(value[0]); + break; + } + if (!strcasecmp(attribute, gfid_attributes[2])) { + new_gfid->perm = xstrdup(value[0]); + break; + } + if (!strcasecmp(attribute, gfid_attributes[3])) { + new_gfid->type_of_perm = xstrdup(value[0]); + break; + } + } + ldap_value_free(value); + } + ldap_memfree(attribute); + } + if (ber != NULL) + ber_free(ber, 0); + + /* Do we have the new_gfid->id ? */ + if (new_gfid->id != NULL) { + add_sorted_gfid(new_gfid); + } + ldap_memfree(dn); + } + ldap_msgfree(result); + } + return r; +} + + + + + +/********************************** + * Build a link list * + * Keep an order between * + * the list elements * + **********************************/ + +void +add_sorted_gfid(GFID * new_gfid) { + int cmp; + GFID *new, *prev, *wk; + + new = new_gfid; + + /* Ordering */ + prev = 0; + wk = fl; + + + while (wk != 0 && (cmp = cmpstr(new->id, wk->id)) >= 0) { + /* We keep a gfid only once */ + if (cmp == 0) + return; + + prev = wk; + wk = wk->next; + } + + new->next = wk; + no_gfids++; + if (prev == 0) + fl = new; + else + prev->next = new; +} + + +/********************************** + * Compares two numeric strings * + **********************************/ +int +cmpstr(const char *a, const char *b) { + + int n_a, n_b; + + n_a = atoi(a); + n_b = atoi(b); + + if (n_a == n_b) + return 0; + if (n_a > n_b) { + return 1; + } + else + return -1; +} + + +/********************************** + * Find_users * + **********************************/ +int +find_users(void) { + + int len, r, k=0; + size_t id_len, role_len; + char *filter; + GFID *wk; + + role_len = strlen(role_attributes[0]); + + wk = fl; + while (wk != 0 && wk->id != NULL) { + id_len = strlen(wk->id); + len = id_len + role_len + 2; + filter = (char *) xmalloc(len * sizeof (char)); + strncpy(filter, role_attributes[0], role_len); + filter[role_len] = '='; + strncpy(filter + role_len + 1, wk->id, id_len + 1); + r=get_ldap_role(filter); + k+=r; + if(r == -1) { + free(filter); + return -1; + } else { + if( r == 0) + wk->can_be_deleted=1; + free(filter); + wk = wk->next; + } + } + if(k>0) + return 1; + return 0; +} + +/*********************************** + * + ***********************************/ +int +get_ldap_role(char *filter) { + LDAPMessage *result, *entry; + BerElement *ber; + char *attribute; + char *dn; + char **value; + char **update_role = (char **) 0; + int i, k; + int j = 0; + int r = 0; + int retry = 0; + + + r = my_ldap_search(ld, + base_people, + LDAP_SCOPE_SUBTREE, + filter, + role_attributes, + 0, + NULL, + user_size_limit, + &result); + + if(r != -1) + { + for (entry = ldap_first_entry(ld, result); entry != NULL; + entry = ldap_next_entry(ld, entry)) { + if(ldap_msgtype(entry) == LDAP_RES_SEARCH_ENTRY) { + + if ((dn = ldap_get_dn(ld, entry)) != NULL) { + fprintf(logfd, "User's DN: %s\n", dn); + + attribute = ldap_first_attribute(ld, entry, &ber); + + if ((value = ldap_get_values(ld, entry, attribute)) != NULL) { + for (i = 0; value[i] != NULL; i++) { + /* Check if value[i] exists in the list */ + if (!check_del(value[i])) { + /* Build new array of roles */ + update_role = + (char **) xrealloc((char *) update_role, + (j + 1) * sizeof (char *)); + update_role[j] = xstrdup(value[i]); + j = j + 1; + } + } + + /* new role should be NULL terminated */ + update_role = + (char **) xrealloc((char *) update_role, + (j + 1) * sizeof (char *)); + update_role[j] = NULL; + + /* Modify the role of current dn */ + if (clear_role(dn, update_role)) + return -1; + /* reinitialize update_role */ + j = 0; + for (k = 0; update_role[k] != NULL; k++) { + free(update_role[k]); + } + free(update_role); + update_role = (char **) 0; + ldap_value_free(value); + } + ldap_memfree(attribute); + } + } + ldap_memfree(dn); + + if (ber != NULL) + ber_free(ber, 0); + } + } + ldap_msgfree(result); + return r; +} + + +/********************************** + * check_del + **********************************/ +int +check_del(char *val) { + + GFID *wk; + + wk = fl; + while (wk != 0) { + switch (cmpstr(val, wk->id)) { + /* val = wk->id */ + case 0: + fprintf(logfd, "%s --------> deleted\n", wk->id); + fprintf(logfd, "\tFolderName:\t\t%s\n", wk->name); + fprintf(logfd, "\tpermFlag:\t\t%s\n", wk->perm); + fprintf(logfd, "\tTypeOfpermFlag:\t\t%s\n", wk->type_of_perm); + return 1; + + /* val < wk->strval */ + case -1: + return 0; + /* val > wk->strval */ + case 1: + wk = wk->next; + continue; + } + } + return 0; +} + +/************************************ + * clear_role + ************************************/ +int +clear_role(char *dn, char **new_role) { + int r = 0; + LDAPMod mod; + LDAPMod *mods[2]; + + mod.mod_op = LDAP_MOD_REPLACE; + mod.mod_type = role_attributes[0]; + mod.mod_values = new_role; + mods[0] = &mod; + mods[1] = NULL; + + if ((r = ldap_modify_s(ld, dn, mods)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "clear_role::ldap_modify_s() failed : %s", ldap_err2string(r)); + return -1; + } + + no_users++; + return 0; +} + + +/********************************** + * Remove all groupFolderIDs * + * that have been already removed * + * from users Role attribute * + **********************************/ +int +clear_gfid(void) { + int r = 0; + GFID *wk; + + + wk = fl; + + while (wk != 0) { + if(wk->can_be_deleted == 1) { + if ((r = myldap_delete_s(ld, wk->dn)) != LDAP_SUCCESS) { + syslog(LOG_ERR, "clear_gfid::ldap_delete_s() failed %s for %s", ldap_err2string(r), wk->dn); + return -1; + } + } + wk = wk->next; + } + return 0; +} + +/************************************** + * Remove a whole subtree. * + * Delete the children of the DN * + * recursively, then the DN requested * + **************************************/ +int +myldap_delete_s(LDAP *ld, const char *dn ) +{ + int r = 0; + + r = deletechildren( ld, dn ); + if ( r != LDAP_SUCCESS ) { + return r; + } + + r = ldap_delete_s(ld, dn); + + return r; +} + + +/* ******************************* + * Delete all the children of an * + * entry recursively until leaf * + * nodes are reached. * + *********************************/ +int +deletechildren(LDAP *ld, const char *dn ) +{ + LDAPMessage *res, *e; + int entries; + int r; + static char *attrs[] = { "1.1", NULL }; + + /* + * Do a one level search at dn for children. For each, delete its children. + */ + + r = ldap_search_ext_s( ld, dn, LDAP_SCOPE_ONELEVEL, NULL, attrs, 1, + NULL, NULL, NULL, -1, &res ); + if ( r != LDAP_SUCCESS ) { + syslog(LOG_ERR, "clear_gfid::ldapdeletechlidren() failed %s for %s", ldap_err2string(r), dn); + return(r); + } + + entries = ldap_count_entries( ld, res ); + + if ( entries > 0 ) { + int i; + + for (e = ldap_first_entry( ld, res ), i = 0; e != NULL; + e = ldap_next_entry( ld, e ), i++ ) + { + char *dn = ldap_get_dn( ld, e ); + + if( dn == NULL ) { + ldap_get_option( ld, LDAP_OPT_ERROR_NUMBER, &r ); + ber_memfree( dn ); + return r; + } + + r = deletechildren( ld, dn ); + if ( r == -1 ) { + ber_memfree( dn ); + return r; + } + + r = ldap_delete_s( ld, dn ); + if ( r == -1 ) { + ber_memfree( dn ); + return r; + + } + + ber_memfree( dn ); + } + } + + ldap_msgfree( res ); + return r; +} + +void fatal(const char *s, int code) { + if (ld != NULL ) ldap_unbind(ld); + syslog(LOG_ERR, "Fatal error: %s", s); + if(logfd) { + fprintf(logfd, "Fatal error: %s\n", s); + cdlog_close(logfd); + } + + exit(EXIT_FAILURE); +} + + +void gracefully_exit(void) +{ + syslog(LOG_WARNING, "Process was interrupted by signal. Exiting gracefully"); + fprintf(logfd, "Process was interrupted by signal. Exiting gracefully\n"); + cdlog_close(logfd); + + ldap_end(); + + exit(EXIT_SUCCESS); +} + + +/* + * Signal handler that exits immediately. + */ + +void immediately_exit(int signo) +{ + gracefully_exit(); +} + + +/************************** + * Simply remove the * + * deleted GFID from the * + * linked list and free * + * the memory used. * + **************************/ +void +clear_gfid_list(void) { + GFID *wk, *tofree; + int all_freed = 1; + + + wk = fl; + while(wk!=0) { + tofree=wk; + wk=wk->next; + free(tofree); + } + fl = 0; +} + + + +static int +my_ldap_search(LDAP *ld, + const char *base, + int scope, + char *filter, + char **attrs, + int attrsonly, + struct timeval *timeout, + int sizelimit, + LDAPMessage **res) +{ + int err; + err = ldap_search_ext_s(ld, + base, + LDAP_SCOPE_SUBTREE, + filter, + attrs, + attrsonly, + NULL, + NULL, + timeout, + sizelimit, + res); + + if(err == LDAP_TIMELIMIT_EXCEEDED || err == LDAP_SIZELIMIT_EXCEEDED) { + syslog(LOG_ERR, "my_ldap_search_s(): " + "filter [ %s ]: %s " + "continuing using: %d entries", + filter, + ldap_err2string(err), + ldap_count_entries(ld, *res)); + + fprintf(logfd, "While searching for [ %s ] get: %s\n" + "continuing using: %d entries\n", + filter, + ldap_err2string(err), + ldap_count_entries(ld, *res)); + return 1; + + } + else { + if (err != LDAP_SUCCESS) { + syslog(LOG_ERR, "get_ldap_role::ldap_search_s() failed : %s", + ldap_err2string(err)); + return -1; + } + } + //printf("Number of entries for %s : %d\n", filter, ldap_count_entries(ld, *res)); + return 0; + +} diff -Naur cyrus-imapd-2.1.16/imap/duplicate.h cyrus-imapd-2.1.16_uncompiled-1.1/imap/duplicate.h --- cyrus-imapd-2.1.16/imap/duplicate.h 2003-02-13 22:15:24.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/duplicate.h 2003-11-21 10:55:16.000000000 +0200 @@ -46,7 +46,7 @@ #define DUPLICATE_RECOVER 0x01 /* name of the duplicate delivery database */ -#define FNAME_DELIVERDB "/deliver.db" +#define FNAME_DELIVERDB "/run/deliver.db" int duplicate_init(char*, int); diff -Naur cyrus-imapd-2.1.16/imap/imapconf.h cyrus-imapd-2.1.16_uncompiled-1.1/imap/imapconf.h --- cyrus-imapd-2.1.16/imap/imapconf.h 2003-03-10 21:00:19.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/imapconf.h 2003-11-21 11:01:16.000000000 +0200 @@ -43,6 +43,10 @@ #ifndef INCLUDED_IMAPCONF_H #define INCLUDED_IMAPCONF_H +#ifndef __GNUC__ +#define __attribute__(x) +#endif + #include #include "auth.h" #include "mboxname.h" diff -Naur cyrus-imapd-2.1.16/imap/imapd.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/imapd.c --- cyrus-imapd-2.1.16/imap/imapd.c 2003-09-24 17:16:02.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/imapd.c 2004-02-04 16:54:58.000000000 +0200 @@ -143,6 +143,7 @@ void shut_down(int code); void fatal(const char *s, int code); +void autocreate_inbox(void); void cmdloop(void); void cmd_login(char *tag, char *user); void cmd_authenticate(char *tag, char *authtype); @@ -175,6 +176,7 @@ 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_avelused(char *tag, char *name); void cmd_getquota(char *tag, char *name); void cmd_getquotaroot(char *tag, char *name); void cmd_setquota(char *tag, char *quotaroot); @@ -773,6 +775,7 @@ tls_shutdown_serverengine(); #endif + ldap_cache_done(); exit(code); } @@ -901,6 +904,15 @@ snmp_increment(APPEND_COUNT, 1); } + else if (!strcmp(cmd.s, "Avelused")) { + if (c != ' ') goto missingargs; + c = getastring(imapd_in, imapd_out, &arg1); + if (c == EOF) goto missingargs; + if (c == '\r') c = prot_getc(imapd_in); + if (c != '\n') goto extraargs; + + cmd_avelused(tag.s, arg1.s); + } else goto badcmd; break; @@ -1693,6 +1705,43 @@ } /* + * Autocreate Inbox and subfolders upon login + */ +void autocreate_inbox() +{ + char inboxname[MAX_MAILBOX_NAME+1]; + int autocreatequota; + int r; + + /* + * Exlude admin's accounts + */ + if (imapd_userisadmin || imapd_userisproxyadmin) + return; + + /* + * Exclude anonymous + */ + if (!strcmp(imapd_userid, "anonymous")) + return; + + + if ((autocreatequota = config_getint("autocreatequota", 0))) { + /* This is actyally not required + as long as the lenght of userid is ok */ + r = (*imapd_namespace.mboxname_tointernal) (&imapd_namespace, + "INBOX", imapd_userid, inboxname); + if (!r) + r = mboxlist_lookup(inboxname, NULL, NULL, NULL); + + if (r == IMAP_MAILBOX_NONEXISTENT) + mboxlist_autocreateinbox(&imapd_namespace, imapd_userid, + imapd_authstate, inboxname, autocreatequota); + } +} + + +/* * Perform a LOGIN command */ void cmd_login(char *tag, char *user) @@ -1825,6 +1874,7 @@ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid); freebuf(&passwdbuf); + autocreate_inbox(); return; } @@ -1946,6 +1996,7 @@ /* Translate any separators in userid */ mboxname_hiersep_tointernal(&imapd_namespace, imapd_userid); + autocreate_inbox(); return; } @@ -2265,6 +2316,9 @@ #ifdef ENABLE_X_NETSCAPE_HACK prot_printf(imapd_out, " X-NETSCAPE"); #endif + /* add AVELUSED capability */ + prot_printf(imapd_out, " AVELUSED"); + prot_printf(imapd_out, "\r\n%s OK %s\r\n", tag, error_message(IMAP_OK_COMPLETED)); } @@ -4384,6 +4438,48 @@ } /* + * Perform an AVELUSED command + */ +void +cmd_avelused(tag, name) +char *tag; +char *name; +{ + char mailboxname[MAX_MAILBOX_NAME+1]; + struct appendstate as; + unsigned long used; + int r; + + /* Set up the append */ + r = (*imapd_namespace.mboxname_tointernal)(&imapd_namespace, name, + imapd_userid, mailboxname); + if (!r) { + r = append_setup(&as, mailboxname, MAILBOX_FORMAT_NORMAL, + imapd_userid, imapd_authstate, + (imapd_userisadmin) ? 0x000000 : ACL_DELETE, -1); + } + + if (!r) { + prot_printf(imapd_out, "* AVELUSED "); + printastring(name); + prot_printf(imapd_out, " ("); + used=as.m.quota_mailbox_used; + prot_printf(imapd_out, "%lu", used/QUOTA_UNITS); + prot_printf(imapd_out, ")\r\n"); + append_abort(&as); + } + + 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 @@ -4519,6 +4615,7 @@ char *quotaroot; { int newquota = -1; + int rmquota = 0; int badresource = 0; int c; int force = 0; @@ -4534,7 +4631,10 @@ if (c != ')' || arg.s[0] != '\0') { for (;;) { if (c != ' ') goto badlist; + if (strcasecmp(arg.s, "remove") == 0) rmquota = 1; + else if (strcasecmp(arg.s, "storage") != 0) badresource = 1; + c = getword(imapd_in, &arg); if (c != ' ' && c != ')') goto badlist; if (arg.s[0] == '\0') goto badlist; @@ -4571,7 +4671,10 @@ imapd_userid, mailboxname); if (!r) { - r = mboxlist_setquota(mailboxname, newquota, force); + if (!rmquota) + r = mboxlist_setquota(mailboxname, newquota, force); + else + r = mboxlist_unsetquota(mailboxname); } } @@ -7257,8 +7360,9 @@ } /* Suppress any output of a partial match */ - if (name[matchlen] && strncmp(lastname, name, matchlen) == 0 - && lastname[matchlen] == '\0') { + if ((name[matchlen] + && strncmp(lastname, name, matchlen) == 0 + && (lastname[matchlen] == '\0' || lastname[matchlen] == '.'))) { return; } diff -Naur cyrus-imapd-2.1.16/imap/ipurge.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/ipurge.c --- cyrus-imapd-2.1.16/imap/ipurge.c 2003-04-22 20:39:41.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/ipurge.c 2003-12-01 20:38:42.000000000 +0200 @@ -69,6 +69,7 @@ #include "mailbox.h" #include "xmalloc.h" #include "mboxlist.h" +#include "cyrus_ldap.h" /* globals for getopt routines */ extern char *optarg; @@ -99,6 +100,9 @@ int verbose = 1; int forceall = 0; +int doJunk = 0; + + int purge_me(char *, int, int); int purge_check(struct mailbox *, void *, char *); int usage(char *name); @@ -114,7 +118,7 @@ usage(argv[0]); } - while ((option = getopt(argc, argv, "C:hxd:b:k:m:fs")) != EOF) { + while ((option = getopt(argc, argv, "C:hxd:b:k:m:fsJ")) != EOF) { switch (option) { case 'C': /* alt config file */ alt_config = optarg; @@ -152,15 +156,28 @@ case 's' : { skipflagged = 1; } break; + case 'J' : { + forceall = 1; + doJunk = 1; + } break; case 'h': default: usage(argv[0]); } } - if ((days == -1 ) && (size == -1)) { - printf("One of these must be specified -d, -b -k, -m\n"); + if ((days == -1 ) && (size == -1) && (doJunk == 0)) { + printf("One of these must be specified -d, -b -k, -m -J\n"); usage(argv[0]); } + if((days != -1) && (doJunk == 1)) { + printf("-d is incompatible with -J\n"); + usage(argv[0]); + } + if((exact!= -1) && (doJunk == 1)) { + printf("-x is incompatible with -J\n"); + usage(argv[0]); + } + config_init(alt_config, "ipurge"); if (geteuid() == 0) fatal("must run as the Cyrus user", EX_USAGE); @@ -171,6 +188,11 @@ fatal(error_message(r), EC_CONFIG); } + if ( doJunk && optind == argc ) { + printf("-J requires a mailbox pattern\n"); + usage(argv[0]); + } + mboxlist_init(0); mboxlist_open(NULL); @@ -183,6 +205,10 @@ strncpy(buf, argv[optind], MAX_MAILBOX_NAME); /* Translate any separators in mailboxname */ mboxname_hiersep_tointernal(&purge_namespace, buf); + if ( doJunk && strncasecmp(buf,"user.",5)!=0) { + printf("-J requires a mailbox pattern. Ignoring %s\n",argv[optind]); + continue; + } (*purge_namespace.mboxlist_findall)(&purge_namespace, buf, 1, 0, 0, purge_me, NULL); } @@ -195,12 +221,13 @@ int usage(char *name) { - printf("usage: %s [-f] [-s] [-C ] [-x] {-d days &| -b bytes|-k Kbytes|-m Mbytes}\n\t[mboxpattern1 ... [mboxpatternN]]\n", name); - printf("\tthere are no defaults and at least one of -d, -b, -k, -m\n\tmust be specified\n"); + printf("usage: %s [-f] [-s] [-C ] {[-x] {-d days &| -b bytes|-k Kbytes|-m Mbytes}|-J}\n\t[mboxpattern1 ... [mboxpatternN]]\n", name); + printf("\tthere are no defaults and at least one of -d, -b, -k, -m -J\n\tmust be specified\n"); printf("\tif no mboxpattern is given %s works on all mailboxes\n", name); printf("\t -x specifies an exact match for days or size\n"); printf("\t -f force also to delete mail below user.* and INBOX.*\n"); printf("\t -s skip over messages that are flagged.\n"); + printf("\t -J for each userid, retreive num of days from ldap. Implies -f\n"); exit(0); } @@ -210,6 +237,8 @@ struct mailbox the_box; int error; mbox_stats_t stats; + char *p,*q,*uid; + int len; if( ! forceall ) { /* DON'T purge INBOX* and user.* */ @@ -217,10 +246,41 @@ return 0; } + if ( doJunk ) { + if (strncasecmp(name,"user.",5)!=0) + return 0; + } + memset(&stats, '\0', sizeof(mbox_stats_t)); if (verbose) printf("Working on %s...\n",name); + if (doJunk) { + /* If we have the -J option then the format of the name + * is user.uid.folder and we take the uid part in + * order to get the junkPrune attribute from the + */ + p = name+5; + for(q = p; *q && *q != '.' ; q++); + len = q-p; + if (len <= 1) { + syslog(LOG_ERR, error_message(IMAP_MAILBOX_BADNAME)); + fatal(error_message(IMAP_MAILBOX_BADNAME), EC_SOFTWARE); + } + + uid = xmalloc(len+1); + strncpy(uid,p,len); + uid[len] = '\0'; + + days = get_junkPrune(uid); + if (verbose) + printf("uid: days = %d\n", uid, days); + free(uid); + if (days <= 0) + return 0; + + days *= 86400; /* nominal # of seconds in a 'day' */ + } error = mailbox_open_header(name, 0, &the_box); if (error != 0) { /* did we find it? */ diff -Naur cyrus-imapd-2.1.16/imap/ldapcache_prune.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/ldapcache_prune.c --- cyrus-imapd-2.1.16/imap/ldapcache_prune.c 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/ldapcache_prune.c 2003-11-21 16:06:59.000000000 +0200 @@ -0,0 +1,50 @@ +#include + +#include +#include +#include + +#include "imapconf.h" +#include "exitcodes.h" + +#include "cyrus_ldapcache.h" + +void fatal(const char *message, int code) +{ + fprintf(stderr, "fatal error: %s\n", message); + exit(code); +} + +void usage(void) +{ + fprintf(stderr, "ldapcache_prune [-C ]\n"); + exit(-1); +} + + +int main(int argc, char *argv[]) +{ + extern char *optarg; + int opt; + int cache_timeout; + char *alt_config = NULL; + + if (geteuid() == 0) fatal("must run as the Cyrus user", EC_USAGE); + + while ((opt = getopt(argc, argv, "C:")) != EOF) { + switch (opt) { + case 'C': /* alt config file */ + alt_config = optarg; + break; + + default: + usage(); + break; + } + } + + config_init(alt_config, "ldapcache_prune"); + cache_timeout = config_getint ("roles_cache_timeout", 300); + + return ldap_cache_prune(cache_timeout); +} diff -Naur cyrus-imapd-2.1.16/imap/lmtpd.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/lmtpd.c --- cyrus-imapd-2.1.16/imap/lmtpd.c 2003-10-01 21:20:37.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/lmtpd.c 2003-12-07 17:58:28.000000000 +0200 @@ -148,6 +148,9 @@ struct lmtp_func mylmtp = { &deliver, &verify_user, &shut_down, &spoolfile, &removespool, 0, 1, 0 }; +static int autosieve_subfolder(char *userid, struct auth_state *auth_state, + char *subfolder); + static void logdupelem(); static void usage(); static void setup_sieve(); @@ -692,6 +695,19 @@ sd->username, mdata->notifyheader, fc->mailbox, quotaoverride, 0); + if (ret == IMAP_MAILBOX_NONEXISTENT) { + /* if "plus" folder under INBOX, then try to create it */ + ret = autosieve_subfolder(sd->username, sd->authstate, fc->mailbox); + + if (!ret) + ret = deliver_mailbox(md->data, mdata->stage, md->size, + fc->imapflags->flag, fc->imapflags->nflags, + sd->username, sd->authstate, md->id, + sd->username, mdata->notifyheader, + fc->mailbox, quotaoverride, 0); + + } + if (ret == 0) { snmp_increment(SIEVE_FILEINTO, 1); @@ -1346,9 +1362,52 @@ snmp_increment(ACTIVE_CONNECTIONS, -1); } + ldap_cache_done(); exit(code); } + +/* + * Autocreate Inbox and subfolders upon login + */ +int autocreate_inbox(char *rcpt_userid) +{ + struct auth_state *authstate; + char inboxname[MAX_MAILBOX_NAME+1]; + int rcptisadmin ; + int autocreatequota; + int r; + + /* + * Exclude anonymous + */ + if (!strcmp(rcpt_userid, "anonymous")) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Check for autocreatequota and createonpost + */ + if (!(autocreatequota = config_getint("autocreatequota", 0)) || + !(config_getswitch("createonpost", 0))) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Exclude admin's accounts + */ + authstate = auth_newstate(rcpt_userid, NULL); + rcptisadmin = authisa(authstate, "imap", "admins"); + if (rcptisadmin) + return IMAP_MAILBOX_NONEXISTENT; + + r = (*lmtpd_namespace.mboxname_tointernal) (&lmtpd_namespace, + "INBOX", rcpt_userid, inboxname); + if (!r) + r = mboxlist_autocreateinbox(&lmtpd_namespace, rcpt_userid, + authstate, inboxname, autocreatequota); + return r; +} + + static int verify_user(const char *user, long quotacheck, struct auth_state *authstate) { @@ -1382,6 +1441,8 @@ - don't care about message size (1 msg over quota allowed) */ r = append_check(buf, MAILBOX_FORMAT_NORMAL, authstate, 0, quotacheck > 0 ? 0 : quotacheck); + if (r == IMAP_MAILBOX_NONEXISTENT) + r = autocreate_inbox(buf+5); } } @@ -1498,3 +1559,88 @@ append_removestage(stage); } + + +#define SEP '|' + +static int autosieve_subfolder(char *userid, struct auth_state *auth_state, + char *subfolder) { + char foldername [MAX_MAILBOX_NAME+1]; + char f [MAX_MAILBOX_NAME+1]; + const char *subf ; + char *p, *q, *next_subf, *mbox; + int len, r = 0; + int createsievefolder = 0; + + + /* Check if subfolder is NULL */ + if(subfolder == NULL) + return IMAP_MAILBOX_NONEXISTENT; + + if ((subfolder[0] != 'i' && subfolder[0] != 'I') || + strncasecmp(subfolder, "inbox", 5) || + subfolder[5] != lmtpd_namespace.hier_sep) + return IMAP_MAILBOX_NONEXISTENT; + + + if (config_getswitch("anysievefolder", NULL)) + createsievefolder = 1; + else if ((subf = config_getstring("autosievefolders", NULL)) != NULL) { + /* + * mbox contains only the subfolder to be created name + */ + mbox = strchr(subfolder, (int) lmtpd_namespace.hier_sep); + mbox++; + + /* Roll through subf */ + next_subf = (char *) subf; + while (*next_subf) { + for (p = next_subf ; isspace((int) *p) || *p == SEP ; p++); + for (next_subf = p ; *next_subf && *next_subf != SEP ; next_subf++); + for (q = next_subf ; q > p && (isspace((int) *q) || *q == SEP || !*q); q--); + + if (!*p) continue; + /* + * This is a preliminary length check based on the assumption + * that the *final* internal format will be something + * like user.userid.subfolder(s). + */ + len = q - p + 1; + if (len > sizeof(f) - strlen(userid) - 5) + r = IMAP_MAILBOX_BADNAME; + + if (!r) { + strncpy(f, p, len); + f[len] = '\0'; + + if (!strcmp(f, mbox)) { + createsievefolder = 1; + break; + } + } + } + } + + if (createsievefolder) { + /* Translate any separators in user */ + if (userid) mboxname_hiersep_tointernal(&lmtpd_namespace, userid); + + r = (*lmtpd_namespace.mboxname_tointernal) (&lmtpd_namespace, + subfolder, userid, foldername); + if (!r) + r = mboxlist_createmailbox(foldername, MAILBOX_FORMAT_NORMAL, NULL, + 1, userid, auth_state, 0, 0, 0); + if (!r) { + mboxlist_changesub(foldername, userid, auth_state, 1, 1); + syslog(LOG_DEBUG, "Ondemand subfolder: User %s, subfolder %s creation succeeded.", + userid, foldername); + return 0; + } else { + syslog(LOG_ERR, "Requested subfolder: User %s, subfolder %s creation failed. %s", + userid, foldername,error_message(r)); + return r; + } + } else + return IMAP_MAILBOX_NONEXISTENT; +} + diff -Naur cyrus-imapd-2.1.16/imap/lmtpengine.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/lmtpengine.c --- cyrus-imapd-2.1.16/imap/lmtpengine.c 2003-09-02 22:48:22.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/lmtpengine.c 2003-11-21 12:04:12.000000000 +0200 @@ -192,6 +192,8 @@ "550-Please contact the owner of this mailbox in order to submit\r\n" "550-your message, or %s if you believe you\r\n" "550-received this message in error.\r\n" +"550-For additional information related to this error message - see\r\n" +"550-http://email.uoa.gr/returned-errors/cyrus-imap/POST_PERMISSION.html\r\n" "550 5.7.1 Permission denied\r\n", POSTMASTER); } else { @@ -1605,6 +1607,8 @@ tmp += 5; msg->authuser = parseautheq(&tmp); if (msg->authuser) { + syslog(LOG_WARNING, "auth: %s %s authorized", + cd.clienthost, msg->authuser); msg->authstate = auth_newstate(msg->authuser, NULL); } else { /* do we want to bounce mail because of this? */ diff -Naur cyrus-imapd-2.1.16/imap/mboxlist.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxlist.c --- cyrus-imapd-2.1.16/imap/mboxlist.c 2003-11-04 23:39:18.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxlist.c 2004-02-05 19:30:28.000000000 +0200 @@ -76,6 +76,8 @@ #include "imap_err.h" #include "xmalloc.h" +#include "cyrus_ldap.h" + #include "mboxname.h" #include "mupdate-client.h" @@ -97,6 +99,9 @@ void *rock); static int mboxlist_changequota(const char *name, int matchlen, int maycreate, void *rock); +static int mboxlist_autosubscribe_sharedfolders(struct namespace *namespace, + char *userid, + struct auth_state *auth_state); #define FNAME_SUBSSUFFIX ".sub" @@ -2117,10 +2122,12 @@ */ int mboxlist_unsetquota(const char *root) { + char newquota[MAX_MAILBOX_PATH+1]; char quota_path[MAX_MAILBOX_PATH+1]; char pattern[MAX_MAILBOX_PATH+1]; int fd; int r=0; + struct mailbox mailbox; if (!root[0] || root[0] == '.' || strchr(root, '/') || strchr(root, '*') || strchr(root, '%') || strchr(root, '?')) { @@ -2150,11 +2157,51 @@ if(unlink(quota_path) == -1) { syslog(LOG_ERR, "could not unlink %s (%m)", quota_path); r = IMAP_SYS_ERROR; + goto error_noclose; + } + + if (mailbox_findquota(newquota, sizeof(newquota), root)) { + + r = mailbox_open_header(newquota, 0, &mailbox); + if (r) goto error_noclose; + + r = mailbox_lock_header(&mailbox); + if (r) goto error; + + r = mailbox_open_index(&mailbox); + if (r) goto error; + + r = mailbox_lock_index(&mailbox); + if (r) goto error; + + r = mailbox_lock_quota(&mailbox.quota); + if (r) goto error; + + /* + * Have to update all affected mailboxes + */ + strlcpy(pattern, root, sizeof(pattern)); + strlcat(pattern, ".*", sizeof(pattern)); + + mboxlist_changequota(root, 0, 0, &mailbox.quota); + mboxlist_findall(NULL, pattern, 1, 0, 0, mboxlist_changequota, &mailbox.quota); + + r = mailbox_write_quota(&mailbox.quota); + if (r) goto error; } return r; + +error: + mailbox_unlock_quota(&mailbox.quota); + mailbox_close(&mailbox); +error_noclose: + syslog(LOG_ERR, "LOSTQUOTA: error while unsetting quota root for %s " + " and its children. Try running quota -f", root); + return r; } + /* * Retrieve internal information, for reconstructing mailboxes file */ @@ -2302,6 +2349,7 @@ return 0; } + void mboxlist_init(int myflags) { int r; @@ -2805,3 +2853,259 @@ mboxlist_closesubs(subs); return r; } + + +#define SEP '|' + +/* + * Automatically subscribe user to a shared folder. + * Subscription is done successfully, if the shared + * folder exists and the user has the necessary + * permissions. + */ +static int mboxlist_autosubscribe_sharedfolders(struct namespace *namespace, + char *userid, + struct auth_state *auth_state) { + + const char *sub ; + char *p, *q, *next_sub; + char folder [MAX_MAILBOX_NAME+1]; + char name [MAX_MAILBOX_NAME+1]; + char mailboxname [MAX_MAILBOX_NAME+1]; + int len; + int r = 0; + + sub=config_getstring("autosubscribesharedfolders", ""); + + next_sub = (char *) sub; + while (*next_sub) { + for (p = next_sub ; isspace((int) *p) || *p == SEP ; p++); + for (next_sub = p ; *next_sub && *next_sub != SEP ; next_sub++); + for (q = next_sub ; q > p && (isspace((int) *q) || *q == SEP || !*q) ; q--); + if (!*p ) continue; + + len = q - p + 1; + /* Check for folder length */ + if (len > sizeof(folder)-1) + continue; + + if (!r) { + strncpy(folder, p, len); + folder[len] = NULL; + + strlcpy(name, namespace->prefix[NAMESPACE_SHARED], sizeof(name)); + len = strlcat(name, folder, sizeof(name)); + + r = (namespace->mboxname_tointernal) (namespace, name, userid, + mailboxname); + } + + if (!r) + r = mboxlist_changesub(mailboxname, userid, auth_state, 1, 0); + + if(!r) { + syslog(LOG_DEBUG,"autosubscribe: User %s to %s succeeded", + userid, folder); + } + else { + syslog(LOG_ERR, "autosubscribe: User %s to %s failed: %s", + userid, folder, error_message(r)); + r = 0; + } + } + + return r; +} + +int mboxlist_autocreateinbox(struct namespace *namespace, + char *userid, + struct auth_state *auth_state, + char *mailboxname, int autocreatequota) { + char name [MAX_MAILBOX_NAME+1]; + char folder [MAX_MAILBOX_NAME+1]; + char *partition = NULL; + const char *crt ; + const char *sub ; + char *p, *q, *next_crt, *next_sub; + int len; + int r = 0; + int numcrt = 0; + int numsub = 0; + + + /* + * While this is not needed for admins + * and imap_admins accounts, it would be + * better to separate *all* admins and + * proxyservers from normal accounts + * (accounts that have mailboxes). + * UOA Specific note(1): Even if we do not + * exclude these servers-classes here, + * UOA specific code, will neither return + * role, nor create INBOX, because none of these + * administrative accounts belong to the + * mailRecipient objectclass, or have imapPartition. + * UOA Specific note(2): Another good reason for doing + * this, is to prevent the code, from getting into + * cyrus_ldap.c because of the continues MSA logins to LMTPd. + */ + + /* + * admins and the coresponding imap + * service, had already been excluded. + */ + + /* + * Do we really need group membership + * for admins or service_admins? + */ + if (authisa(auth_state, "lmtp", "admins")) return 0; + if (authisa(auth_state, "sieve", "admins")) return 0; + + /* + * Do we really need group membership + * for proxyservers? + */ + if (authisa(auth_state, "imap", "proxyservers")) return 0; + if (authisa(auth_state, "sieve", "proxyservers")) return 0; + + + /* + * Get Partition info or return. + * (Here you should propably use + * you own "get_partition(char *userid)" + * function. Otherwise all new INBOXes will be + * created into whatever partition has been declared + * as default in your imapd.conf) + */ + partition = get_partition(userid); + + + if (partition == NULL) { + /* + * Couldn't get partition info + */ + syslog(LOG_WARNING, + "Could not get imapPartition info for user %s", userid); + return IMAP_PARTITION_UNKNOWN; + } + r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, partition, + 1, userid, auth_state, 0, 0, 0); + + if (!r && autocreatequota > 0) + r = mboxlist_setquota(mailboxname, autocreatequota, 0); + + if (!r) + r = mboxlist_changesub(mailboxname, userid, + auth_state, 1, 1); + + syslog(r == 0 ? LOG_NOTICE : LOG_ERR, + "User %s, INBOX autocreate in partition-%s: %s", userid, + partition == NULL ? "default" : partition, + r == 0 ? "succeeded" : error_message(r)); + + /* Allocated from get_partition, and not needed any more */ + free_partition(partition); + + + if (r) return r; + + + /* INBOX's subfolders */ + + + if ((crt=config_getstring("autocreateinboxfolders", ""))) + sub=config_getstring("autosubscribeinboxfolders", ""); + + /* Roll through crt */ + next_crt = (char *) crt; + while (next_crt != NULL && *next_crt) { + for (p = next_crt ; isspace((int) *p) || *p == SEP ; p++); + for (next_crt = p ; *next_crt && *next_crt != SEP ; next_crt++); + for (q = next_crt ; q > p && (isspace((int) *q) || *q == SEP || !*q); q--); + + + if (!*p) continue; + + + len = q - p + 1; + + + /* + * This is a preliminary length check based on the assumption + * that the *final* internal format will be something + * like user.userid.subfolder(s). + */ + if (len > sizeof(folder) - strlen(userid) - 5) + r = IMAP_MAILBOX_BADNAME; + + + if (!r) { + strncpy(folder, p, len); + folder[len] = '\0'; + + + strlcpy(name, namespace->prefix[NAMESPACE_INBOX], sizeof(name)); + len = strlcat(name, folder, sizeof(name)); + if (len >= MAX_MAILBOX_NAME) + r = IMAP_MAILBOX_BADNAME; + } + + + if (!r) + r = (namespace->mboxname_tointernal) (namespace, name, userid, + mailboxname); + if (!r) + r = mboxlist_createmailbox(mailboxname, MAILBOX_FORMAT_NORMAL, NULL, + 1, userid, auth_state, 0, 0, 0); + if (!r) { + numcrt++; + syslog(LOG_DEBUG, "autocreateinbox: User %s, subfolder %s creation succeeded.", + userid, name); + } else + syslog(LOG_ERR, "autocreateinbox: User %s, subfolder %s creation failed. %s", + userid, name,error_message(r)); + + if (r) { + r=0; + continue; + } + + /* Roll through sub */ + next_sub = (char *) sub; + while (next_sub != NULL && *next_sub) { + for (p = next_sub ; isspace((int) *p) || *p == SEP ; p++); + for (next_sub = p ; *next_sub && *next_sub != SEP ; next_sub++); + for (q = next_sub ; q > p && (isspace((int) *q) || *q == SEP || !*q) ; q--); + if (!*p ) continue; + + len = q - p + 1; + + if (len != strlen(folder) || strncmp(folder, p, len)) + continue; + + r = mboxlist_changesub(mailboxname, userid, auth_state, 1, 1); + + if (!r) { + numsub++; + syslog(LOG_DEBUG,"autocreateinbox: User %s, subscription to %s succeeded", + userid, name); + } else + syslog(LOG_ERR, "autocreateinbox: User %s, subscription to %s failed. %s", + userid, name, error_message(r)); + break; + } + } + + if (crt != NULL && *crt) + syslog(LOG_INFO, + "User %s, Inbox subfolders, created %d, subscribed %d", userid, + numcrt,numsub); + + /* + * Check if shared folders are available for subscription. + */ + mboxlist_autosubscribe_sharedfolders(namespace, userid, auth_state); + + return r; +} diff -Naur cyrus-imapd-2.1.16/imap/mboxlist.h cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxlist.h --- cyrus-imapd-2.1.16/imap/mboxlist.h 2003-08-09 02:08:52.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxlist.h 2003-12-07 18:00:57.000000000 +0200 @@ -191,4 +191,9 @@ /* done with database stuff */ void mboxlist_done(void); +int mboxlist_autocreateinbox(struct namespace *namespace, + char *userid, + struct auth_state *auth_state, + char *mailboxname, int autocreatequota); + #endif diff -Naur cyrus-imapd-2.1.16/imap/mboxname.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxname.c --- cyrus-imapd-2.1.16/imap/mboxname.c 2003-04-25 20:32:33.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/mboxname.c 2003-12-07 17:59:12.000000000 +0200 @@ -227,9 +227,9 @@ } /* other personal folder */ - if (strlen(result)+6 > MAX_MAILBOX_NAME) { + if ( strlen(result) + 1 + strlen(name) > MAX_MAILBOX_NAME) return IMAP_MAILBOX_BADNAME; - } + strcat(result, "."); strcat(result, name); diff -Naur cyrus-imapd-2.1.16/imap/pop3d.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/pop3d.c --- cyrus-imapd-2.1.16/imap/pop3d.c 2003-09-03 17:38:25.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/pop3d.c 2003-11-21 13:46:28.000000000 +0200 @@ -140,6 +140,8 @@ static struct namespace popd_namespace; static char popd_apop_chal[45 + MAXHOSTNAMELEN + 1]; /* */ +static int autocreate_inbox(char *inboxname); + static void cmd_apop(char *response); static void cmd_auth(); @@ -450,6 +452,7 @@ tls_shutdown_serverengine(); #endif + ldap_cache_done(); exit(code); } @@ -1252,6 +1255,43 @@ } } + +/* + * Autocreate Inbox and subfolders upon login + */ +int autocreate_inbox(char *inboxname) +{ + struct auth_state *authstate; + int userisadmin ; + int autocreatequota; + int r; + + /* + * Exclude anonymous + */ + if (!strcmp(popd_userid, "anonymous")) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Check for autocreatequota + */ + if (!(autocreatequota = config_getint("autocreatequota", 0))) + return IMAP_MAILBOX_NONEXISTENT; + + /* + * Exclude admin's accounts + */ + authstate = auth_newstate(popd_userid, NULL); + userisadmin = authisa(authstate, "imap", "admins"); + if (userisadmin) + return IMAP_MAILBOX_NONEXISTENT; + + r = mboxlist_autocreateinbox(&popd_namespace, popd_userid, + authstate, inboxname, autocreatequota); + return r; +} + + /* * Complete the login process by opening and locking the user's inbox */ @@ -1270,6 +1310,11 @@ strcpy(inboxname, "user."); strcat(inboxname, popd_userid); r = mailbox_open_header(inboxname, 0, &mboxstruct); + + /* Try once again after autocreate_inbox */ + if (r == IMAP_MAILBOX_NONEXISTENT && + !(r = autocreate_inbox(inboxname))) + r = mailbox_open_header(inboxname, 0, &mboxstruct); if (r) { free(popd_userid); popd_userid = 0; diff -Naur cyrus-imapd-2.1.16/imap/pop3proxyd.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/pop3proxyd.c --- cyrus-imapd-2.1.16/imap/pop3proxyd.c 2003-07-22 22:17:17.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/pop3proxyd.c 2003-11-21 13:46:58.000000000 +0200 @@ -366,6 +366,7 @@ tls_shutdown_serverengine(); #endif + ldap_cache_done(); exit(code); } diff -Naur cyrus-imapd-2.1.16/imap/proxyd.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/proxyd.c --- cyrus-imapd-2.1.16/imap/proxyd.c 2003-09-23 21:34:24.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/proxyd.c 2003-12-12 13:38:26.000000000 +0200 @@ -1386,6 +1386,7 @@ tls_shutdown_serverengine(); #endif + ldap_cache_done(); exit(code); } diff -Naur cyrus-imapd-2.1.16/imap/tls.h cyrus-imapd-2.1.16_uncompiled-1.1/imap/tls.h --- cyrus-imapd-2.1.16/imap/tls.h 2003-02-13 22:15:31.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/tls.h 2003-11-21 13:49:51.000000000 +0200 @@ -52,7 +52,7 @@ int tls_enabled(const char *ident); /* name of the SSL/TLS sessions database */ -#define FNAME_TLSSESSIONS "/tls_sessions.db" +#define FNAME_TLSSESSIONS "/run/tls_sessions.db" #ifdef HAVE_SSL diff -Naur cyrus-imapd-2.1.16/imap/user.c cyrus-imapd-2.1.16_uncompiled-1.1/imap/user.c --- cyrus-imapd-2.1.16/imap/user.c 2003-07-28 07:00:32.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/imap/user.c 2003-11-21 13:52:00.000000000 +0200 @@ -81,7 +81,6 @@ #include "seen.h" #include "xmalloc.h" -#if 0 static int user_deleteacl(char *name, int matchlen, int maycreate, void* rock) { /* deleting all references to the user is too slow right now */ @@ -112,7 +111,6 @@ } return 0; } -#endif int user_deletesieve(char *user) { @@ -167,15 +165,12 @@ user_deletequotas(user); /* delete ACLs - we're using the internal names here */ -#if 0 - /* xxx no reason to do this if user_deleteacl is a stub anyway. */ if(wipe_user) { const char pat[] = "*"; mboxlist_findall(NULL, pat, sizeof(pat), userid, authstate, user_deleteacl, user); } -#endif /* delete sieve scripts */ user_deletesieve(user); diff -Naur cyrus-imapd-2.1.16/lib/auth_unix.c cyrus-imapd-2.1.16_uncompiled-1.1/lib/auth_unix.c --- cyrus-imapd-2.1.16/lib/auth_unix.c 2003-05-13 18:33:26.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/auth_unix.c 2003-11-21 14:37:05.000000000 +0200 @@ -46,14 +46,14 @@ #include #include -#include -#include #include #include #include "auth.h" #include "xmalloc.h" +#include "cyrus_ldap.h" + const char *auth_method_desc = "unix"; struct auth_state { @@ -127,8 +127,9 @@ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* 20-2F */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* 30-3F */ - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 40-4F */ - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, /* 50-5F */ + + 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 40-4F */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, /* 50-5F */ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 60-6F */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, /* 70-7F */ @@ -158,7 +159,6 @@ size_t len; { static char retbuf[81]; - struct group *grp; char sawalpha; char *p; @@ -185,9 +185,6 @@ */ if (!strncmp(retbuf, "group:", 6)) { - grp = getgrnam(retbuf+6); - if (!grp) return 0; - strcpy(retbuf+6, grp->gr_name); return retbuf; } @@ -204,6 +201,10 @@ sawalpha = 1; /* FALL THROUGH */ + case 3: + sawalpha = 1; + *p = tolower((unsigned char) *p); + default: ; } @@ -224,16 +225,14 @@ const char *identifier; const char *cacheid; { + int i; + char **result; struct auth_state *newstate; - struct passwd *pwd; - struct group *grp; - char **mem; identifier = auth_canonifyid(identifier, 0); if (!identifier) return 0; if (!strncmp(identifier, "group:", 6)) return 0; - pwd = getpwnam(identifier); newstate = (struct auth_state *)xmalloc(sizeof(struct auth_state)); @@ -241,20 +240,53 @@ newstate->ngroups = 0; newstate->group = (char **) 0; - setgrent(); - while ((grp = getgrent())) { - for (mem = grp->gr_mem; *mem; mem++) { - if (!strcmp(*mem, identifier)) break; - } - - if (*mem || (pwd && pwd->pw_gid == grp->gr_gid)) { - newstate->ngroups++; - newstate->group = (char **)xrealloc((char *)newstate->group, - newstate->ngroups * sizeof(char *)); - newstate->group[newstate->ngroups-1] = xstrdup(grp->gr_name); - } + /* + * While this is not needed for admins + * and imap_admins accounts, it would be + * better to separate *all* admins and + * proxyservers from normal accounts + * (accounts that have mailboxes). + * UOA Specific note(1): Even if we do not + * exclude these servers-classes here, + * UOA specific code, will neither return + * role, nor create INBOX, because none of these + * administrative accounts belong to the + * mailRecipient objectclass, or have imapPartition. + * UOA Specific note(2): Another good reason for doing + * this, is to prevent the code, from getting into + * cyrus_ldap.c because of the continues MSA logins to LMTPd. + */ + + /* + * Do we really need group membership + * for admins or service_admins? + */ + if (authisa(newstate, "lmtp", "admins")) return newstate; + if (authisa(newstate, "imap", "admins")) return newstate; + if (authisa(newstate, "sieve", "admins")) return newstate; + + /* + * Do we really need group membership + * for proxyservers? + */ + if (authisa(newstate, "imap", "proxyservers")) return newstate; + if (authisa(newstate, "sieve", "proxyservers")) return newstate; + + /* + * Do we really need group membership + * for anonymous ? + */ + if (!strcasecmp(identifier, "anonymous")) return newstate; + + if ((result = get_role(newstate->userid))) { + for ( i=0; result[i] != NULL; i++ ) { + newstate->ngroups++; + newstate->group = (char **)xrealloc((char *)newstate->group, + newstate->ngroups * sizeof(char *)); + newstate->group[newstate->ngroups-1] = xstrdup(result[i]); + } + free_role(result); } - endgrent(); return newstate; } diff -Naur cyrus-imapd-2.1.16/lib/cyrusdb_db3.c cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrusdb_db3.c --- cyrus-imapd-2.1.16/lib/cyrusdb_db3.c 2003-08-14 19:20:36.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrusdb_db3.c 2004-01-14 22:08:32.000000000 +0200 @@ -80,6 +80,9 @@ #define txn_checkpoint(xx1,xx2,xx3,xx4) (xx1)->txn_checkpoint(xx1,xx2,xx3,xx4) #define txn_id(xx1) (xx1)->id(xx1) #define log_archive(xx1,xx2,xx3,xx4) (xx1)->log_archive(xx1,xx2,xx3) +#define txn_begin(xx1,xx2,xx3,xx4) (xx1)->txn_begin(xx1,xx2,xx3,xx4) +#define txn_commit(xx1,xx2) (xx1)->commit(xx1,xx2) +#define txn_abort(xx1) (xx1)->abort(xx1) #elif DB_VERSION_MINOR == 3 #define log_archive(xx1,xx2,xx3,xx4) log_archive(xx1,xx2,xx3) #endif diff -Naur cyrus-imapd-2.1.16/lib/cyrus_ldap.c cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldap.c --- cyrus-imapd-2.1.16/lib/cyrus_ldap.c 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldap.c 2003-12-07 17:59:59.000000000 +0200 @@ -0,0 +1,834 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "xmalloc.h" +#include "cyrus_ldap.h" +#include "cyrus_ldapcache.h" + +#include "imap/imapconf.h" + + +struct ldap_context +{ + LDAP *ld; +#define LD ldc.ld + int version; + const char *bind_dn; +#define BIND_DN ldc.bind_dn + const char *base_dn; +#define BASE_DN ldc.base_dn + const char *base_cyrus; +#define BASE_CYRUS ldc.base_cyrus + const char *base_people; +#define BASE_PEOPLE ldc.base_people + const char *passwd; +#define LDAP_PASSWD ldc.passwd + int scope; +#define LDAP_SCOPE ldc.scope + const char *objectclass; + const char *role_attr; + const char *user_attr; + int sizelimit; /* this one currently applies only to the number of dynamic roles that may returned */ + struct timeval search_timeout; + struct timeval dsearch_timeout; + struct timeval net_timeout; + int deref; + int *refer; + int *restart; + const char *host; + int base_depth; +} ldc; + +/* +* The attribute pointing to the DN, +* of the organizational uinit, where each +* uid belongs to. +*/ +const char *orgunitdn = "eduPersonOrgUnitDN"; + + +/* Cyrus specific LDAP attributes */ +const char *partition_attr = "imapPartition"; +const char *applyto_attr = "applyTo"; +const char *gfilter_attr = "groupFilter"; +const char *junk_attr = "junkPrune"; + + +/* LDAP Cache Timeout */ +int roles_cache_timeout_set=0; +int roles_cache_timeout; +int dynamic_roles_cache_timeout_set=0; +int dynamic_roles_cache_timeout; + + + +static int ldap_setup_config(void); +static int ldap_start(void); +static int ldap_connect (void); +static int ldap_reconnect (void); +static char * ldap_userfilter (char *uid, const char *objectclass); +static char * get_applyto_filter(char *uid); +static char ** get_uservalues (const char *attr, char *uid); +static char * get_ldap_error (void); +static char ** get_dynamic_role (char *uid); +static int my_ldap_search_st(LDAP *ld, const char *base, int scope, char *filter, char **attrs, + int attrsonly, struct timeval *timeout, int sizelimit, LDAPMessage **res); + + +/* This function is called to ensure that the + * LDAP subsystem is properly set up. If it is + * not it will try to set it up, returning -1 + * on failure or 0 on success + */ + +int +ldap_start (void) +{ + if (BIND_DN == NULL) /* this should succeed only once actually */ + if (ldap_setup_config()) + return -1; + + + + if (LD == NULL) + if (ldap_connect()) + return -1; + + return 0; +} + + +/* + * Initialize LDAP structure, with values + * provided by configuration parameters + * and clears all other + * Returns : 0 on success + * -1 on failure + * + * Note, this should only be called + * once on a per process basis. + */ + +int +ldap_setup_config (void) +{ + + LDAP_SCOPE = LDAP_SCOPE_SUBTREE; + ldc.deref = LDAP_DEREF_NEVER; + ldc.refer = LDAP_OPT_OFF; + /* TODO: this one should be a user defined option */ + ldc.restart = LDAP_OPT_ON; + ldc.version = LDAP_VERSION3; + + if ((ldc.host = config_getstring ("ldap_host", "NULL")) == NULL) + goto err; + if ((BIND_DN = config_getstring ("ldap_bind_dn", NULL)) == NULL) + goto err; + if ((BASE_DN = config_getstring ("ldap_base_dn", NULL)) == NULL) + goto err; + if ((BASE_CYRUS = config_getstring ("ldap_base_cyrus", NULL)) == NULL) + goto err; + if ((BASE_PEOPLE = config_getstring("ldap_base_people", NULL)) == NULL) + goto err; + if ((LDAP_PASSWD = config_getstring ("ldap_passwd", NULL)) == NULL) + goto err; + if ((ldc.role_attr = config_getstring ("ldap_role_attr", NULL)) == NULL) + goto err; + if ((ldc.user_attr = config_getstring ("ldap_user_attr", NULL)) == NULL) + goto err; + + + /* Dynamic Role */ + if ((BASE_CYRUS = config_getstring ("ldap_base_cyrus", NULL)) == NULL) + goto err; + if ((BASE_PEOPLE = config_getstring("ldap_base_people", NULL)) == NULL) + goto err; + + + + ldc.net_timeout.tv_sec = (time_t) config_getint ("ldap_con_timeout", 5); + ldc.net_timeout.tv_usec = 0; + + ldc.search_timeout.tv_sec = (time_t) config_getint ("ldap_search_timeout", 5); + ldc.search_timeout.tv_usec = 0; + + ldc.dsearch_timeout.tv_sec = (time_t) config_getint("ldap_dynroles_search_timeout",5); + ldc.dsearch_timeout.tv_usec = 0; + + ldc.sizelimit = config_getint("ldap_search_sizelimit", 200); + + ldc.base_depth = config_getint ("ldap_dynamicrole_base_depth", 3); + + /* objectclass should be optional */ + ldc.objectclass = config_getstring ("ldap_objectclass", NULL); + + return 0; + +err: + syslog (LOG_ERR, "cyrus_ldap error: couldn't load configuration"); + return -1; + +} + +/* + * Initialize ldap connection + * by calling ldap_init and ldap_bind + * It also sets ldap_options as defined + * in ldap_context. + * Returns : 0 on success + * -1 on failure + */ + +int +ldap_connect (void) +{ + int r; + char *host; + + LD = NULL; + + LD = ldap_init(ldc.host, LDAP_PORT); + + if (LD == NULL){ + syslog (LOG_ERR, "cyrus_ldap error: ldap_init returned NULL!"); + return -1; + } + + /* init options */ + + r = ldap_set_option(LD, LDAP_OPT_PROTOCOL_VERSION, (void *) &(ldc.version)); + + if (r == LDAP_OPT_SUCCESS) + r = ldap_set_option (LD, LDAP_OPT_RESTART, (void *) &(ldc.restart)); + + if (r == LDAP_OPT_SUCCESS) + r = ldap_set_option (LD, LDAP_OPT_SIZELIMIT, (void *) &(ldc.sizelimit)); + + if (r == LDAP_OPT_SUCCESS) + r = ldap_set_option (LD, LDAP_OPT_NETWORK_TIMEOUT, (void *) &(ldc.net_timeout)); + + if (r == LDAP_OPT_SUCCESS) + r = ldap_set_option (LD, LDAP_OPT_DEREF, (void *) &(ldc.deref)); + + if (r == LDAP_OPT_SUCCESS) + r = ldap_set_option (LD, LDAP_OPT_REFERRALS, (void *) &(ldc.refer)); + + if (r != LDAP_OPT_SUCCESS){ + syslog (LOG_ERR, "cyrus_ldap error: ldap_set_option: %s\n", ldap_err2string (r)); + return -1; + } + + /* bind to server */ + + r = ldap_simple_bind_s (LD, BIND_DN, LDAP_PASSWD); + + if (r != LDAP_SUCCESS){ + syslog (LOG_ERR, "cyrus_ldap error: couldn't bind to ldap server: %s", + ldap_err2string (r)); + return -1; + } + + return 0; +} + +int +ldap_reconnect(void) +{ + syslog( LOG_INFO, "cyrus_ldap : Reconnecting..."); + ldap_unbind(LD); + if (ldap_connect()) + return -1; + return 0; +} + + +/* + * Creates filter to use in ldap_search. + * using the uid and an optional objectclass + * returns: "(&(uid=user)(objectclass=Objectclass))" + * or if objectclass is null : "uid=user" + */ + +char * +ldap_userfilter (char *uid, const char *objectclass) +{ + int attr_len; + int uid_len; + int len; + char *filter = NULL; + + if (uid == NULL) return NULL; + + attr_len = strlen (ldc.user_attr); + uid_len = strlen (uid); + + if (objectclass == NULL) { + len = attr_len + uid_len + 1 + 1; + filter = (char *) xmalloc (len * sizeof (char)); + + sprintf(filter, "%s=%s", ldc.user_attr, uid); + } else { + len = attr_len + uid_len + strlen(objectclass) + 20 + 1; + filter = (char *) xmalloc (len * sizeof(char)); + + sprintf(filter,"(&(%s=%s)(objectclass=%s))",ldc.user_attr, uid, objectclass); + } + return filter; +} + +/* + * query the ldap server with the specified parameters + * and trys to reconnect when ever gets LDAP_SERVER_DOWN + */ + +int +my_ldap_search_st(LDAP *ld, const char *base, int scope, char *filter, char **attrs, + int attrsonly, struct timeval *timeout, int sizelimit, LDAPMessage **res) +{ + int r; + r = ldap_search_ext_s(ld, base, scope, filter, attrs, + attrsonly, NULL, NULL, timeout, sizelimit, res); + + if (r != LDAP_SUCCESS) { + syslog(LOG_ERR, "cyrus_ldap error: ldap error code %d", r); + } + + if (r == LDAP_SERVER_DOWN && !ldap_reconnect()) { + syslog(LOG_INFO, "cyrus_ldap : redoing search after reconnect..."); + r = ldap_search_st(ld, base, scope, filter, attrs, + attrsonly, timeout, res); + } + + if (r != LDAP_SUCCESS) + syslog (LOG_ERR, "my_ldap_search_st:: search failed :" + " filter [ %s ] error [ %s ]", + filter, + get_ldap_error() + ); + return r; +} + +/* + * Query the ldap server for the specified attribute + * of the **specified uid**, with a timeout. + * On error NULL is returned, otherwise a + * null terminated array of values. + * It is the caller's responsibility to + * free the returned memory. + * UID must be unique in LDAP. + */ + +char ** +get_uservalues(const char *attr, char *uid) +{ + char *filter; + char *attribute = NULL; + char *attribs[2] = { NULL, NULL }; + LDAPMessage *entry = NULL; + LDAPMessage *results = NULL; + BerElement *ber; + char **values = NULL; + + attribs[0] = (char *) attr; + + filter = ldap_userfilter(uid, ldc.objectclass); + + if (filter == NULL){ + syslog(LOG_ERR, "cyrus_ldap error: couldn't create ldap filter"); + return NULL; + } + + syslog(LOG_DEBUG, "get_uservalues:: using filter [ %s ]", filter); + + /* perform a search with the specified filter and a timeout */ + if ((my_ldap_search_st(LD, + BASE_DN, + LDAP_SCOPE, + filter, + attribs, + 0, + &ldc.search_timeout, + 1, + &results)) != LDAP_SUCCESS) { + + free(filter); + return NULL; + } + + free(filter); + + /* receive first returned entry */ + entry = ldap_first_entry (LD, results); + + if (entry == NULL) { + syslog (LOG_ERR, "get_uservalues:: couldn't find entry matching the request " + "error [ %s ]", + get_ldap_error() + ); + ldap_msgfree(results); + return NULL; + } + + /* access first attribute in entry */ + + attribute = ldap_first_attribute(LD, entry, &ber); + + if (attribute == NULL ){ + syslog (LOG_NOTICE, "get_uservalues:: entry did not contain attribute [ %s ] " + "error [ %s ]", + attribs[0], + get_ldap_error() + ); + ldap_msgfree(results); + return NULL; + } + + /* get attribute's values */ + + values = ldap_get_values (LD, entry, attribute); + + if (values == NULL){ + syslog (LOG_ERR, "get_uservalues:: couldn't retrieve a value for attribute [ %s ] " + "error [ %s ]", + attribs[0], + get_ldap_error() + ); + } + + ber_free(ber,0); + ldap_memfree(attribute); + ldap_msgfree(results); + + return values; +} + + +char * +get_applyto_filter(char *uid) +{ + + char *uid_filter; + char *attribs[2] = { NULL , NULL }; + LDAPMessage *user_results, *user_entry; + BerElement *ber; + char *attr; + char **orgdn; + char *filter = NULL; + int filter_len = 0; + char *subfilter; + int i, j, depth, len; + char *q, *next_dn, *pre_dn; + int applyto_len = strlen(applyto_attr); + char *applyto_filter = NULL; + int temp1, temp2; + + attribs[0] = (char *)orgunitdn; + + /* + * Retrieve the DN of uid, and get its components + */ + uid_filter = ldap_userfilter(uid, ldc.objectclass); + + if (uid_filter == NULL){ + syslog(LOG_ERR, "cyrus_ldap error: couldn't create ldap filter"); + return NULL; + } + + if ((my_ldap_search_st(LD, + BASE_DN, + LDAP_SCOPE, + uid_filter, + attribs, + 0, + &ldc.search_timeout, + 1, + &user_results)) != LDAP_SUCCESS) { + + free(uid_filter); + return NULL; + } + + user_entry = ldap_first_entry (LD, user_results); + + if (user_entry == NULL) { + syslog (LOG_ERR, "get_applyto_filter:: couldn't find entry matching the request filter [ %s ]" + "error [ %s ]", + uid_filter, + get_ldap_error() + ); + free(uid_filter); + ldap_msgfree(user_results); + return NULL; + } + + /* we only ask for the orgunitdn, thereis no next_attribute */ + if ((attr = ldap_first_attribute(LD, user_entry, &ber)) != NULL) + orgdn = ldap_get_values (LD, user_entry, attr); + + /* + * create a filter containing all DNs + * and subDNs of each orgunitdn + */ + + filter_len = 0; + + for (i=0; orgdn != NULL && orgdn[i] != NULL; i++) { + + for (depth = 1, q = orgdn[i] ; *q != '\0' ; q++ ) { + if (*q == ',') depth++; + } + + if ((ldc.base_depth - depth) > 0) { + syslog (LOG_ERR, "get_applyto_filter:: %s: " + "invalid DN with depth: %d while base_depth is: %d", + orgdn[i], + depth, + ldc.base_depth + ); + continue; + } + + for (j = 0, next_dn = orgdn[i]; + j <= (depth - ldc.base_depth) && next_dn; + j++) { + len = strlen(next_dn) + applyto_len + 3; + + subfilter = (char *)xmalloc((len + 1)*sizeof(char)); + sprintf(subfilter, "(%s=%s)", applyto_attr, next_dn); + + if ((i == 0) || ( i > 0 && strstr(filter, subfilter) == NULL)) { + filter_len += len; + filter = (char *)xrealloc(filter, filter_len + 1); + strcat(filter + filter_len - len, subfilter); + free(subfilter); + } else { + free(subfilter); + break; + } + + pre_dn = next_dn; + next_dn = strchr(pre_dn, ','); + if (next_dn) next_dn++; + } + } + + /* free willy */ + if (attr != NULL) ldap_memfree(attr); + if (orgdn != NULL) ldap_value_free(orgdn); + if (ber != NULL) ber_free(ber,0); + + if (filter) { + applyto_filter = (char *)xmalloc(strlen(filter) + 3 + 1); + sprintf(applyto_filter, "(|%s)", filter); + free(filter); + } + + syslog(LOG_DEBUG, "get_applyto_filter:: uid [ %s ], applyto [ %s ]", + uid, + applyto_filter == NULL ? "NULL" : applyto_filter); + return applyto_filter; +} + + +char ** +get_dynamic_role (char *uid) +{ + + char *afilter; + char *attribs[2] = { NULL, NULL }; + LDAPMessage *group_results, *group_entry; + BerElement *ber; + LDAPMessage *filter_results; + int i, ndgroups; + char *filter_dn, *attr, *gfilter_i; + char *no_attributes[2] = { LDAP_NO_ATTRS, NULL }; + char **gfilter = (char **) 0; + char **dnarray = (char **) 0; + char **result = (char **) 0; + int r = 0; + + + if ((afilter = get_applyto_filter(uid)) == NULL) { + return result; + } + + /* + * Search for filters under ou=Cyrus that matches the specified filter + */ + attribs[0] = (char *)gfilter_attr; + + if ((my_ldap_search_st(LD, + BASE_CYRUS, + LDAP_SCOPE, + afilter, + attribs, + 0, + &ldc.dsearch_timeout, + ldc.sizelimit, + &group_results)) != LDAP_SUCCESS) { + free(afilter); + return NULL; + } + + /* we dont need afilter anymore */ + free(afilter); + +#if 0 + /* maximum results are the number of applyto objects returned in search */ + + if ((max_gfids = ldap_count_entries(LD, group_results)) > 0) + result = (char **)xmalloc(sizeof(char*) * max_gfids); +#endif + + + /* for each matching object get all defined filters */ + ndgroups = 0; + result = (char **) 0; + for (i=0, group_entry = ldap_first_entry (LD, group_results); group_entry != NULL; + group_entry = ldap_next_entry (LD, group_entry),i++) { + + /* Get the DN */ + filter_dn = ldap_get_dn(LD, group_entry); + if (filter_dn == NULL) { + syslog (LOG_ERR, "get_dynamic_role:: " + "couldn't get filter's DN (%s)", + get_ldap_error() + ); + continue; + } + + /* we only ask for groupFilter, thereis no next_attribute */ + if ((attr = ldap_first_attribute(LD, group_entry, &ber)) != NULL) + gfilter = ldap_get_values (LD, group_entry, attr); + + /* TODO check for group NULL */ + + /* for each filter check against the specified uid */ + for (i=0; gfilter != NULL && gfilter[i] != NULL; i++) { + gfilter_i = (char *)xmalloc(strlen(gfilter[i]) + strlen(uid) + 9 + 1); + sprintf(gfilter_i, "(&(uid=%s)%s)", uid, gfilter[i]); + + /* check for the given filter ANDed with the specified uid */ + + if ((r = my_ldap_search_st(LD, + BASE_DN, + LDAP_SCOPE, + gfilter_i, + no_attributes, + 1, + &ldc.dsearch_timeout, + 1, + &filter_results)) != LDAP_SUCCESS) { + + /* Check for bad filter */ + if (r == LDAP_FILTER_ERROR) { + syslog (LOG_ERR, "get_dynamic_role:: " + "invalid filter, while looking into %s", + filter_dn + ); + } + /* in any case, continue with the next defined filter */ + continue; + } + + /* we dont need gfilter_i anymore */ + free(gfilter_i); + + if ((ldap_count_entries(LD, filter_results)) == 1 && + (dnarray = ldap_explode_dn(filter_dn , 1)) != NULL) { + + /* get the groupFolderID from the DN */ + if (dnarray[1]) { + ndgroups++; + result = (char **)xrealloc((char *)result, ndgroups * sizeof(char *)); + result[ndgroups - 1] = xstrdup(dnarray[1]); + } + + ldap_value_free(dnarray); + dnarray = NULL; + + break; + } + } + ldap_memfree(filter_dn); + if (attr != NULL) ldap_memfree(attr); + if (gfilter != NULL) ldap_value_free(gfilter); + if (ber != NULL) ber_free(ber,0); + } + ldap_msgfree(group_results); + + if (result) { + result = (char **)xrealloc(result, (ndgroups +1) * sizeof (char *)); + result[ndgroups] = NULL; + } + + return result; +} + +/* + * query the ldap server for the role attribute of + * the specified uid + */ + +char ** +get_role (char *uid) +{ + char **result = NULL; + char **static_result = NULL; + char **dynamic_result = NULL; + char key[512]; + int staticsz, dynamicsz, i; + + if (!roles_cache_timeout_set){ + roles_cache_timeout = config_getint ("roles_cache_timeout", 180); + roles_cache_timeout_set++; + } + + if (!dynamic_roles_cache_timeout_set){ + dynamic_roles_cache_timeout = config_getint ("dynamic_roles_cache_timeout", 180); + dynamic_roles_cache_timeout_set++; + } + + + /* if we can't find anything in the + * cache and we can start the ldap + * subsystem we query the ldap + * server for the role attribute of the + * specified uid. + */ + /* by setting roles_cache_timeout <= 0 + * we are actually disable ldapcache + */ + + strlcpy(key, uid, 512); + strlcat(key, "-static", 512); + + if (roles_cache_timeout>0 && (static_result=ldap_cache_lookup(key, roles_cache_timeout))){ + staticsz = ldap_count_values(static_result); + }else if (!ldap_start()){ + static_result = get_uservalues (ldc.role_attr, uid); + + if (static_result != NULL) { + staticsz = ldap_count_values(static_result); + if (ldap_cache_put(key,staticsz,static_result)) + syslog (LOG_ERR, "cyrus_ldap warning: couldn't update cache for %s", uid); + }else + staticsz = 0; + }else syslog (LOG_NOTICE, "cyrus_ldap warning: couldn't retrieve roles for %s", uid); + + syslog (LOG_INFO, "cyrus_ldap info: uid %s assigned %d static role(s)", uid, staticsz); + + strlcpy(key, uid, 512); + strlcat(key, "-dynamic", 512); + + if (dynamic_roles_cache_timeout>0 && (dynamic_result = ldap_cache_lookup(key, dynamic_roles_cache_timeout))){ + dynamicsz = ldap_count_values(dynamic_result); + }else if (!ldap_start()){ + dynamic_result=get_dynamic_role(uid); + + if (dynamic_result != NULL) { + dynamicsz = ldap_count_values(dynamic_result); + if (ldap_cache_put(key,dynamicsz,dynamic_result)) + syslog (LOG_ERR, "cyrus_ldap warning: couldn't update cache for %s", uid); + }else + dynamicsz = 0; + } else syslog (LOG_NOTICE, "cyrus_ldap warning: couldn't retrieve dynamic roles for %s", uid); + + syslog (LOG_INFO, "cyrus_ldap info: uid %s assigned %d dynamic role(s)", uid, dynamicsz); + + if (static_result && dynamic_result) { + result = xrealloc(static_result, sizeof(char*)*(staticsz + dynamicsz +1)); + if (result != NULL) { + for (i=0; i< dynamicsz; i++ ) + { + result[i+staticsz]=dynamic_result[i]; + } + result[staticsz+dynamicsz]='\0'; + free(dynamic_result); + }else + syslog(LOG_ERR, "cyrus_ldap warning: couldn't allocate space for dynamic roles of uid %s", uid); + }else if (static_result) + result = static_result; + else result = dynamic_result; + + return result; +} + +/* + * free values returned from 'get_role' + */ + +void +free_role(char **role) +{ +/* XXX this must correspond to the correct freeing of + * the array. If the API changes then it will go KABOOM! */ + + ldap_value_free(role); +} + +/* + * query the ldap server for the partition attribute of + * the specified uid + */ + +char * +get_partition (char *uid) +{ + char *res; + char **ret = get_uservalues(partition_attr, uid); + + if (ret != NULL && *ret != NULL) { + res = (char *) xstrdup(*ret); + ldap_value_free(ret); + return res; + } else return NULL; +} + +int +get_junkPrune (char *uid) +{ + int res; + char **ret; + + ldap_start(); + ret = get_uservalues(junk_attr, uid); + + if (ret != NULL && *ret != NULL) { + res = (int) atoi(*ret); + ldap_value_free(ret); + return res; + } else { + return -1; + } +} + +void +free_partition(char *partition) +{ + free(partition); + +} + + +/* + * return latest ldap error string + */ +char * +get_ldap_error (void) +{ + int err = LDAP_SUCCESS; + + ldap_get_option (LD, LDAP_OPT_ERROR_NUMBER, &err); + + if (err != LDAP_SUCCESS) + return ldap_err2string (err); + else + return "unknown ldap error"; +} diff -Naur cyrus-imapd-2.1.16/lib/cyrus_ldapcache.c cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldapcache.c --- cyrus-imapd-2.1.16/lib/cyrus_ldapcache.c 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldapcache.c 2004-01-14 22:09:21.000000000 +0200 @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include + + +#include "xmalloc.h" +#include "cyrusdb.h" +#include "imap/imapconf.h" +#include "cyrus_ldapcache.h" + +#define DB (&cyrusdb_db3_nosync) + +struct cached_auth_state +{ + time_t mark; + int ngroups; + name groups[1]; +}; + +static struct db *cachedb = NULL; +static int cachedb_open = 0; + +int +ldap_cache_init (void) +{ + char buf[1024]; + int r = 0; + int flags = 0; + + /* create the name of the db file */ + strcpy(buf, config_dir); + strcat(buf, FNAME_DBDIR); + + r = DB->init (buf,flags); + + if (r != 0) + syslog(LOG_ERR, "DBERROR: init %s: %s", buf, + cyrusdb_strerror(r)); + else { + char *fname = NULL; + + fname = xmalloc(strlen(config_dir)+sizeof(FNAME_LDAPCACHEDB)); + strcpy(fname, config_dir); + strcat(fname, FNAME_LDAPCACHEDB); + + r = DB->open(fname, CYRUSDB_CREATE, &cachedb); + + if (r != 0) + syslog(LOG_ERR, "DBERROR: opening %s: %s", fname, + cyrusdb_strerror(r)); + else + cachedb_open = 1; + + free(fname); + + } + return r; +} + + +char ** +ldap_cache_lookup (char *userid, int offset) +{ + struct cached_auth_state *fetched; + char **ret = NULL; + const char *data; + int r, i,len = 0; + + if (!cachedb_open && ldap_cache_init()){ + syslog (LOG_ERR, "cyrus_ldap_cache error: can't initialize cache db"); + return NULL; + } + + do r = DB->fetch(cachedb, userid, strlen(userid), + &data, &len, NULL); + while (r == CYRUSDB_AGAIN); + + if (data) { + fetched = (struct cached_auth_state *) data; + if ((fetched->mark + (time_t)offset) >= time(0)) { + + /* copy groups from 'fetched' to an array of strings */ + ret = (char **) xmalloc ((fetched->ngroups + 1) * (sizeof (char *))); + + for (i = 0; i < fetched->ngroups; i++) + ret[i] = (char *) xstrdup (fetched->groups[i]); + + ret[i] = NULL; + + } + + } else if (r != CYRUSDB_OK) { + syslog(LOG_ERR, "cyrus_ldap_cache error: failed to lookup %s: %s", + userid, cyrusdb_strerror(r)); + } + + if (ret == NULL) + syslog (LOG_INFO, "get_values: cache miss"); + else + syslog (LOG_INFO, "get_values: cache hit"); + + return ret; +} + +int +ldap_cache_put (char *userid, int ngroups, char **groups) +{ + struct cached_auth_state *newstate; + char *data; + int r, i, len; + + if (!cachedb_open && ldap_cache_init()){ + syslog (LOG_ERR, "cyrus_ldap_cache error: can't initialize cache db"); + return -1; + } + + len = sizeof (struct cached_auth_state) + ngroups * sizeof (name); + data = xmalloc (len); + + newstate = (struct cached_auth_state *) data; + + newstate->mark = time (0); + newstate->ngroups = ngroups; + + /* copy the groups to the contiguous area in 'newstate' */ + memset (newstate->groups, 0, ngroups * sizeof (name)); + for (i = 0; i < ngroups; i++) + strcpy (newstate->groups[i], groups[i]); + + do r = DB->store(cachedb, userid, strlen(userid), + data, len, NULL); + while (r == CYRUSDB_AGAIN); + + return !(r == CYRUSDB_OK); +} + +int +ldap_cache_done(void) +{ + int r; + + if (cachedb_open){ + r = DB->close(cachedb); + if (r) { + syslog(LOG_ERR, "DBERROR: error closing cachedb: %s", + cyrusdb_strerror(r)); + } + cachedb_open = 0; + } + r = DB->done(); + return r; +} + + +struct cacherock { + struct db *db; + time_t mark; + int count; + int deletions; +}; + +static int cache_p(void *rock, const char *id, int idlen, + const char *data, int datalen) +{ + struct cacherock *crock = (struct cacherock *) rock; + struct cached_auth_state *state = (struct cached_auth_state *) data; + + crock->count++; + + return (state->mark < crock->mark); +} + +static int cache_cb(void *rock, const char *id, int idlen, + const char *data, int datalen) +{ + struct cacherock *crock = (struct cacherock *) rock; + int r; + + crock->deletions++; + + do r = DB->delete(crock->db, id, idlen, NULL, 0); + while (r == CYRUSDB_AGAIN); + + return 0; +} + +int +ldap_cache_prune(int secs) +{ + struct cacherock crock; + + if (!cachedb_open && ldap_cache_init()){ + syslog (LOG_ERR, "cyrus_ldap_cache error: can't initialize cache db"); + return -1; + } + + crock.count = crock.deletions = 0; + crock.mark = time(NULL) - secs; + syslog(LOG_NOTICE, "ldap_cache_prune: pruning back %d seconds", secs); + + crock.db = cachedb; + DB->foreach(cachedb, "", 0, &cache_p, &cache_cb, &crock, NULL); + + syslog(LOG_NOTICE, "ldap_cache_prune: purged %d out of %d entries", + crock.deletions, crock.count); + + ldap_cache_done(); + + return 0; +} diff -Naur cyrus-imapd-2.1.16/lib/cyrus_ldapcache.h cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldapcache.h --- cyrus-imapd-2.1.16/lib/cyrus_ldapcache.h 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldapcache.h 2003-11-21 14:49:52.000000000 +0200 @@ -0,0 +1,10 @@ +#define LDAP_CACHE_PROGNAME "cyrus_ldapcache" +#define MAX_CACHE_NAMELEN 90 +#define FNAME_LDAPCACHEDB "/run/ldapcache.db" + +char **ldap_cache_lookup(char *, int); +int ldap_cache_put(char *, int, char **); +int ldap_cache_init(void); +int ldap_cache_prune(int secs); + +typedef char name[MAX_CACHE_NAMELEN]; diff -Naur cyrus-imapd-2.1.16/lib/cyrus_ldap.h cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldap.h --- cyrus-imapd-2.1.16/lib/cyrus_ldap.h 1970-01-01 02:00:00.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/cyrus_ldap.h 2003-11-29 22:37:17.000000000 +0200 @@ -0,0 +1,10 @@ +#ifndef CYRUS_LDAP_H +#define CYRUS_LDAP_H + +char **get_role(char *uid); +void free_role(char **role); +char *get_partition(char *uid); +int get_junkPrune (char *uid); +void free_partition(char *partition); + +#endif diff -Naur cyrus-imapd-2.1.16/lib/glob.c cyrus-imapd-2.1.16_uncompiled-1.1/lib/glob.c --- cyrus-imapd-2.1.16/lib/glob.c 2003-02-13 22:15:40.000000000 +0200 +++ cyrus-imapd-2.1.16_uncompiled-1.1/lib/glob.c 2003-12-09 12:22:27.000000000 +0200 @@ -42,7 +42,7 @@ * Start Date: 4/5/93 */ /* - * $Id: glob.c,v 1.25 2003/02/13 20:15:40 rjs3 Exp $ + * $Id: glob.c,v 1.25.2.7 2003/12/08 20:56:50 rjs3 Exp $ */ #include @@ -213,6 +213,9 @@ const char *ghier, *phier; /* pointers for '%' patterns */ const char *start; /* start of input string */ int newglob; + int sepfound; /* Set to 1 when a separator is found + * after a '%'. Otherwise 0. + */ /* check for remaining partial matches */ if (min && *min < 0) return (-1); @@ -255,12 +258,18 @@ /* loop to manage wildcards */ do { + sepfound = 0; /* see if we match to the next '%' or '*' wildcard */ while (*gptr != '*' && *gptr != '%' && ptr != pend && (*gptr == *ptr || (!newglob && *gptr == '?'))) { ++ptr, ++gptr; } - if (*gptr == '\0' && ptr == pend) break; + + if (*gptr == '\0' && ptr == pend) { + /* End of pattern and end of string -- match! */ + break; + } + if (*gptr == '*') { ghier = NULL; gstar = ++gptr; @@ -270,6 +279,7 @@ ghier = ++gptr; phier = ptr; } + if (ghier) { /* look for a match with first char following '%', * stop at a sep_char unless we're doing "*%" @@ -285,7 +295,7 @@ gptr = ghier; break; } - if (*ptr == g->sep_char && *ptr != *ghier) { + if (*ptr == g->sep_char) { if (!*ghier && min && *min < ptr - start && ptr != pend && *ptr == g->sep_char @@ -293,28 +303,39 @@ *min = gstar ? ptr - start + 1 : -1; return (ptr - start); } - gptr = ghier; ghier = NULL; + sepfound = 1; } else { phier = ++ptr; gptr = ghier + 1; } } if (gstar && !ghier) { + /* was the * at the end of the pattern? */ if (!*gstar) { ptr = pend; break; } + /* look for a match with first char following '*' */ while (pstar != pend && *gstar != *pstar) ++pstar; if (pstar == pend) { + if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && + *ptr == g->sep_char) { + /* The pattern ended on a hierarchy separator + * return a partial match */ + *min = ptr - start + 1; + return ptr - start; + } gptr = gstar; break; } + ptr = ++pstar; gptr = gstar + 1; } - if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && *ptr == g->sep_char) { + if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && + *ptr == g->sep_char) { /* The pattern ended on a hierarchy separator * return a partial match */ *min = ptr - start + 1; @@ -323,19 +344,24 @@ /* continue if at wildcard or we passed an asterisk */ } while (*gptr == '*' || *gptr == '%' || - ((gstar || ghier) && (*gptr || ptr != pend))); + ((gstar || ghier || sepfound) && (*gptr || ptr != pend))); } else { /* case insensitive version (same as above, but with TOLOWER()) */ /* loop to manage wildcards */ do { + sepfound = 0; /* see if we match to the next '%' or '*' wildcard */ while (*gptr != '*' && *gptr != '%' && ptr != pend && ((unsigned char) *gptr == TOLOWER(*ptr) || (!newglob && *gptr == '?'))) { ++ptr, ++gptr; } - if (*gptr == '\0' && ptr == pend) break; + if (*gptr == '\0' && ptr == pend) { + /* End of pattern and end of string -- match! */ + break; + } + if (*gptr == '*') { ghier = NULL; gstar = ++gptr; @@ -345,6 +371,7 @@ ghier = ++gptr; phier = ptr; } + if (ghier) { /* look for a match with first char following '%', * stop at a sep_char unless we're doing "*%" @@ -369,6 +396,7 @@ return (ptr - start); } ghier = NULL; + sepfound = 1; } else { phier = ++ptr; gptr = ghier + 1; @@ -383,13 +411,21 @@ while (pstar != pend && (unsigned char) *gstar != TOLOWER(*pstar)) ++pstar; if (pstar == pend) { + if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && + *ptr == g->sep_char) { + /* The pattern ended on a hierarchy separator + * return a partial match */ + *min = ptr - start + 1; + return ptr - start; + } gptr = gstar; break; } ptr = ++pstar; gptr = gstar + 1; } - if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && *ptr == g->sep_char) { + if (*gptr == '\0' && min && *min < ptr - start && ptr != pend && + *ptr == g->sep_char) { /* The pattern ended on a hierarchy separator * return a partial match */ *min = ptr - start + 1; @@ -398,7 +434,7 @@ /* continue if at wildcard or we passed an asterisk */ } while (*gptr == '*' || *gptr == '%' || - ((gstar || ghier) && (*gptr || ptr != pend))); + ((gstar || ghier || sepfound) && (*gptr || ptr != pend))); } if (min) *min = -1; diff -Naur cyrus-imapd-2.1.16/timsieved/timsieved.c cyrus-imapd-2.1.16_uncompiled-1.1/timsieved/timsieved.c --- cyrus-imapd-2.1.16/timsieved/timsieved.c 2003-06-24 18:34:00.000000000 +0300 +++ cyrus-imapd-2.1.16_uncompiled-1.1/timsieved/timsieved.c 2003-12-15 12:45:46.000000000 +0200 @@ -135,6 +135,8 @@ cyrus_close_sock(0); cyrus_close_sock(1); cyrus_close_sock(2); + + ldap_cache_done(); /* done */ exit(code);